3 #define Copyright "Copyright 1999 Ed Casas"
5 #define Version "efax v 0.9"
8 Copyright (C) 1999 Ed Casas
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 Please contact the author if you wish to use efax or efix in
25 ways not covered by the GNU GPL.
27 You may contact the author by e-mail at: edc@cce.com, by mail
28 at: 2629 West 3rd Ave, Vancouver, BC, Canada, V6K 1M4, or by
29 fax at: +1 604 734 5291.
33 static const char *Usage =
35 " %s [ option ]... [ -t num [ file... ] ]\n"
37 " -a str use command ATstr to answer\n"
38 " -c cap set modem and receive capabilites to cap\n"
39 " -d dev use modem on device dev\n"
40 " -e cmd exec \"/bin/sh -c cmd\" for voice calls\n"
42 " -f fnt use (PBM) font file fnt for headers\n"
44 " -g cmd exec \"/bin/sh -c cmd\" for data calls\n"
45 " -h hdr use page header hdr (use %%d's for current page/total pages)\n"
46 " -i str send modem command ATstr at start\n"
47 " -j str send modem command ATstr after set fax mode\n"
48 " -k str send modem command ATstr when done\n"
49 " -l id set local identification to id\n"
50 " -o opt use protocol option opt:\n"
51 " 0 use class 2.0 instead of class 2 modem commands\n"
53 " 1 use class 1 modem commands\n"
55 " 2 use class 2 modem commands\n"
56 " a if first [data mode] answer attempt fails retry as fax\n"
57 " e ignore errors in modem initialization commands\n"
58 " f use virtual flow control\n"
59 " h use hardware flow control\n"
60 " l halve lock file polling interval\n"
61 " n ignore page retransmission requests\n"
62 " r do not reverse received bit order for Class 2 modems\n"
63 " x use XON instead of DC2 to trigger reception\n"
64 " z add 100 ms to pause before each modem comand (cumulative)\n"
65 " -q ne ask for retransmission if more than ne errors per page\n"
67 " -r pat save received pages into files pat.001, pat.002, ... \n"
69 " -s share (unlock) modem device while waiting for call\n"
70 " -v lvl print messages of type in string lvl (ewinchamr)\n"
71 " -w don't answer phone, wait for OK or CONNECT instead\n"
72 " -x fil use uucp-style lock file fil\n"
74 " -t dial num and send fax image files file... \n"
77 #include <ctype.h> /* ANSI C */
83 #include "efaxio.h" /* EFAX */
90 /* delays and timeouts (t/o), in deciseconds */
91 #define T1 350 /* T.30 T1 - waiting for DIS/DCS before Phase B */
92 #define T2 60 /* T.30 T2 - waiting for frame in Phase B */
93 #define T3S 30 /* T.30 response timeout (not T3) */
94 #define T4 30 /* T.30 T4 - between [re]transmissions of DIS */
96 #define TMOD 55 /* T.30 pause between v.21&v.29, 75-20 ms */
98 #define TO_A 1200 /* dial/answer (Phase A) - modem may t/o first */
99 #define TO_ABRT 20 /* max delay after sending abort sequence */
100 #define TO_CHAR 102 /* per data character (2x max FILL length) */
101 #define TO_DATAF 80 /* software adaptive answer data connect t/o */
102 #define TO_DRAIN_H 136 /* minimum HDLC buffer drain time (4k/300cps) */
103 #define TO_DRAIN_D 300 /* minimum data buffer drain time */
104 #define TO_FT 31 /* max delay after +F[TR][MH] command */
105 #define TO_RTCMD 20 /* return to command mode after DLE-ETX (rx) */
107 #define TO_C2B 450 /* Class 2 DIS to CONNECT:(DCS+TCF+CFR)xretries */
108 #define TO_C2X 20 /* Class 2 wait for XON: 2/5 of 5s timeout */
109 #define TO_C2PP 200 /* Class 2 wait for ppr: (ppm+ppr)x3retries + 2 */
110 #define TO_C2R 600 /* Class 2 receive: (TCF+FTT)x11 retrains + 5 */
111 #define TO_C2EOR 120 /* Class 2 end of data rx (4 retrans x 3 s) */
113 #define ANSCMD "A" /* default modem command to answer calls */
114 #define DCSLEN 3 /* length of FIF for DCS commands sent */
115 #define DEFDISLEN 3 /* length of DIS initially transmitted */
116 #define DEFCAP 1,3,0,2,0,0,0,0 /* default local capabilities */
117 #define DEFID " " /* default local ID */
118 #define DEFPAT "%m%d%H%M%S" /* default received file name pattern */
119 #define HDRSHFT 54 /* shift header right 6.7mm into image area */
120 #define HDRSPCE 20 /* number of scan lines inserted before image */
121 #define HDRSTRT 4 /* scan line where header is placed on image */
123 #define HDRCHRH 24 /* header character height (pels, at 196lpi) */
125 #define HDRCHRH 0 /* header character height (pels, at 196lpi) */
127 #define HDRCHRW 12 /* header character width (pels) */
128 #define IDLEN 20 /* length of T.30 ID strings, must be 20 */
129 #define MAXDIS 8 /* maximum DIS frames sent without response (T1) */
130 #define MAXERRPRT 32 /* maximum number of reception errors to report */
131 #define MAXFIFLEN 125 /* max FIF len = MAXFRLEN - (adx+ctl+FCF) - FCS */
132 #define MAXFRLEN 130 /* max frame length = 3.45s x 300 bps / 8 */
133 #define MAXGETTY 512 /* maximum length of exec'ed (-g, -e) commands */
134 #define MAXICMD 100 /* maximum # of modem setup/reset commands */
135 #define MAXLKFILE 16 /* maximum number of lock files */
136 #define MAXNULLS 2 /* maximum consecutive received nulls saved */
137 #define MAXPGERR 10 /* maximum received errors allowed per page */
138 #define MAXTRAIN 2 /* maximum training retries at lowest speed */
139 #define MAXRETRY 3 /* maximum retries of unacknowledged commands */
140 #define NCAP 8 /* number of fields in a capability string */
141 #define NTXRETRY 3 /* maximum re-sends per page */
143 typedef int cap [ NCAP ] ; /* remote/local capabilities */
145 /* capability fields... */
146 enum captype { VR, BR, WD, LN, DF, EC, BF, ST } ;
147 static int capmax [ NCAP ] = { 1, 7, 2, 2, 3, 2, 1, 7 } ;
148 /* & maximum values */
151 /* vertical resolution, dpi */
152 int vresolution [ 2 ] = { 98, 196 } ;
155 /* characters per second for br */
156 static int cps [ 8 ] = { 300, 600, 900, 1200, 1500, 1800, 900, 1200 } ;
159 /* next br = fallback [ br ] */
160 /* 0, 1, 2, 3, 4, 5, 6, 7 */
161 int fallback [ 8 ] = {-1, 0, 1, 2, 7, 4, 3, 6 } ;
163 /* negotiation speed index */
164 /* 0, 1, 2, 3, 4, 5, 6, 7 */
165 int brindex [ 8 ] = { 0, 1, 2, 3, 4, 7, 5, 6 } ;
168 /* minimum scan time in ms */
169 static int mst [ 8 ] = { 0 , 5, 10, 10, 20, 20, 40, 40 } ;
171 /* page width in pixels */
172 static int pagewidth [ 5 ] = { 1728, 2048, 2432, 1216, 864 } ;
174 /* Table to convert between T.30 DIS/DCS/DTC FIF and Class 2-like
175 capability codes. Uses br=6, 7 for V.17 at 7200, 9600. */
177 typedef struct t30tabstruct
180 uchar byte, shift, mask ;
182 uchar captodis[8], distocap[16], captodcs[8], dcstocap[16] ;
185 #define X 0xff /* invalid values */
187 static t30tabst t30tab [ NCAP ] = {
188 { "vr", 1, 1, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
189 { "br", 1, 2, 0x0f, 0,
190 { 0, 4, 12, 12, 13, 13 } ,
191 { 0, X, X, X, 1, X, X, X, 3, X, X, X, 3, 5, 3, X } ,
192 { 0, 4, 12, 8, 5, 1 } ,
193 { 0, 5, 5, X, 1, 4, 4, X, 3, 7, X, X, 2, 6, X, X } } ,
194 { "wd", 2, 6, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, 2 } ,
195 { 0, 2, 1 } , { 0, 2, 1, 2 } },
196 { "ln", 2, 4, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, X } ,
197 { 0, 2, 1 } , { 0, 2, 1, X } },
198 { "df", 1, 0, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
199 { "ec", 3, 4, 0x03, 0, { 0, 2, 2 } , { 0, X, 2, X } ,
200 { 0, 3, 2 } , { 0, 0, 2, 1 } },
201 { "bf", 5, 5, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
202 { "st", 2, 1, 0x07, 7,
203 { 7, 4, 3, 2, 6, 0, 5, 1 } , { 5, 7, 3, 2, 1, 6, 4, 0 } ,
204 { 7, 4, X, 2, X, 0, X, 1 } , { 5, 7, 3, 1, X, X, X, 0 } }
207 /* values of capability fields */
208 static char *capvaluestr [ NCAP ] [8] = {
209 { " 98lpi", "196lpi" } ,
210 { " 2400bps", " 4800bps", " 7200bps", " 9600bps", " 12kbps", "14.4kbps",
211 "7200V.17", "9600V.17" } ,
212 { "8.5\"/215mm", " 10\"/255mm", " 12\"/303mm",
213 " 6\"/151mm", "4.2\"/107mm" } ,
214 { "11\"/A4", "14\"/B4", " any " } ,
215 { "1D" , "2D" }, { " - ", "ECM-256", "ECM-64 " }, { " - ", "BFT" },
216 { "0ms", "5ms", "10/5ms", "10ms", "20/10ms", "20ms", "40/20ms", "40ms" }
219 /* T.30 control frames */
222 DIS=0x01, CSI, NSF=0x04,
224 MCF=0x31, RTN, RTP, PIN, PIP,
225 DCS=0x41, TSI, NSS=0x44,
227 EOM=0x71, MPS, EOP=0x74, PRI_EOM=0x79, PRI_MPS, PRI_EOP=0x7c,
228 DTC=0x81, CIG, NSC=0x84
231 enum commanddtype { RCV=0, SND=1, DTA=0, TRN=1 } ;
233 /* Class 1 commands to [receive=0/transmit=1] [data=0/training=1] for
237 char *c1cmd [ 2 ] [ 2 ] [ 8 ] = {
238 { { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=122", "+FRM=146" ,
239 "+FRM=74", "+FRM=98" } ,
240 { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=121", "+FRM=145" ,
241 "+FRM=73", "+FRM=97" } } ,
242 { { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=122", "+FTM=146" ,
243 "+FTM=74", "+FTM=98", } ,
244 { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=121", "+FTM=145" ,
245 "+FTM=73", "+FTM=97" } }
249 static struct c2msgstruct
254 { 0, 9, "Call Placement and Termination:" },
255 { 0, 0, " Normal and proper end of connection" },
256 { 1, 1, " Ring Detect without successful handshake" },
257 { 2, 2, " Call aborted, from +FK[S] or CAN" },
258 { 3, 3, " No Loop Current" },
259 { 4, 4, " Ringback Detected, no answer" },
260 { 5, 5, " Ringback Detected, answer without CED" },
262 { 10, 19, "Transmit Phase A & Miscellaneous Errors:" },
263 { 10, 10, " Unspecified Phase A error" },
264 { 11, 11, " No Answer (T.30 T1 timeout)" },
265 { 20, 39, "Transmit Phase B Hangup Codes:" },
266 { 20, 20, " Unspecified Transmit Phase B error" },
267 { 21, 21, " Remote cannot receive or send" },
268 { 22, 22, " COMREC error in transmit Phase B" },
269 { 23, 23, " COMREC invalid command received" },
270 { 24, 24, " RSPREC error" },
271 { 25, 25, " DCS sent three times without response" },
272 { 26, 26, " DIS/DTC received 3 times; DCS not recognized" },
273 { 27, 27, " Failure to train at 2400 bps or +FMINSP value" },
274 { 28, 28, " RSPREC invalid response received" },
275 { 40, 49, "Transmit Phase C Hangup Codes:" },
276 { 40, 40, " Unspecified Transmit Phase C error" },
277 { 41, 41, " Unspecified image format error" },
278 { 42, 42, " Image conversion error" },
279 { 43, 43, " DTE to DCE data underflow" },
280 { 44, 44, " Unrecognized transparent data command" },
281 { 45, 45, " Image error, line length wrong" },
282 { 46, 46, " Image error, page length wrong" },
283 { 47, 47, " Image error, wrong compression code" },
284 { 50, 69, "Transmit Phase D Hangup Codes:" },
285 { 50, 50, " Unspecified Transmit Phase D error" },
286 { 51, 51, " RSPREC error" },
287 { 52, 52, " No response to MPS repeated 3 times" },
288 { 53, 53, " Invalid response to MPS" },
289 { 54, 54, " No response to EOP repeated 3 times" },
290 { 55, 55, " Invalid response to EOP" },
291 { 56, 56, " No response to EOM repeated 3 times" },
292 { 57, 57, " Invalid response to EOM" },
293 { 58, 58, " Unable to continue after PIN or PIP" },
295 { 70, 89, "Receive Phase B Hangup Codes:" },
296 { 70, 70, " Unspecified Receive Phase B error" },
297 { 71, 71, " RSPREC error" },
298 { 72, 72, " COMREC error" },
299 { 73, 73, " T.30 T2 timeout, expected page not received" },
300 { 74, 74, " T.30 T1 timeout, after EOM received" },
301 { 90, 99, "Receive Phase C Hangup Codes:" },
302 { 90, 90, " Unspecified Receive Phase C error" },
303 { 91, 91, " Missing EOL after 5 seconds" },
304 { 92, 92, " Unused code" },
305 { 93, 93, " DCE to DTE buffer overflow" },
306 { 94, 94, " Bad CRC or frame (ECM or BFT modes)" },
307 { 100, 119, "Receive Phase D Hangup Codes:" },
308 { 100, 100, " Unspecified Receive Phase D errors" },
309 { 101, 101, " RSPREC invalid response received" },
310 { 102, 102, " COMREC invalid response received" },
311 { 103, 103, " Unable to continue after PIN or PIP" },
312 { 120, 255, "Reserved Codes" },
316 /* meaning of efax return codes */
318 static char *errormsg [] = {
320 "number busy or modem in use",
321 "unrecoverable error",
322 "invalid modem response",
323 "no response from modem",
324 "terminated by signal",
329 /* Return name of frame of type 'fr'. */
332 char *frname ( int fr )
334 static struct framenamestruct { int code ; char *name ; }
337 {NSC,"NSC - poller features"}, /* these 3 frames must be first */
338 {CIG,"CIG - poller ID"},
339 {DTC,"DTC - poller capabilities"},
340 {NSF,"NSF - answering features"},
341 {CSI,"CSI - answering ID"},
342 {DIS,"DIS - answering capabilities"},
343 {NSS,"NSS - caller features"},
344 {TSI,"TSI - caller ID"},
345 {DCS,"DCS - session format"},
347 {CFR,"CFR - channel OK"},
348 {FTT,"FTT - channel not OK"},
350 {MPS,"MPS - not done"},
351 {EOM,"EOM - not done, new format"},
354 {PRI_MPS,"PRI-MPS - not done, call operator"},
355 {PRI_EOM,"PRI-EOM - not done, new format, call operator"},
356 {PRI_EOP,"PRI-EOP - done, call operator"},
358 {MCF,"MCF - page OK"},
359 {RTP,"RTP - page OK, check channel"},
360 {PIP,"PIP - page OK, call operator"},
361 {RTN,"RTN - page not OK, check channel"},
362 {PIN,"PIN - page not OK, call operator"},
364 {CRP,"CRP - repeat command"},
365 {DCN,"DCN - disconnect"},
369 for ( p=framenames ; p->code ; p++ )
370 if ( fr == p->code || ( fr & 0x7f ) == p->code) break ;
371 return p->code ? p->name : "UNKNOWN" ;
375 /* Range-check capability. */
377 static int checkcap ( cap c )
381 for ( i=0 ; i<NCAP ; i++ )
382 if ( c[i] > capmax[i] || c[i] < 0 ) {
383 err = msg ( "E3%s = %d out of range, set to 0", t30tab[i].name, c[i] ) ;
390 /* Print cap[ability] c using text values and prefix s. */
392 static void printcap ( char *s , cap c )
395 msg ( "N-+ %s" , s ) ;
397 for ( i=0 ; i<NCAP ; i++ )
398 msg ( "N-+ %s" , capvaluestr [ i ] [ c[i] ] ) ;
403 /* Convert capability string to cap struct. Returns 0 or 2 on errors. */
405 static int str2cap ( char *s, cap c )
410 for ( n=0 ; n<NCAP ; n++ ) {
413 c [ n ] = strtol ( s, &end, 10 );
414 if (end && *end && *end!=',')
419 if ( n < NCAP ) msg ( "Wmissing value(s) in \"%s\"", s ) ;
427 /* Convert a cap[ability] 'c' to a DIS/DCS/DTC FIF 'fif' of 'len'
428 bytes. Converts into DIS format if 'isdis' is true, else into
432 void mkdis ( cap c, uchar *fif, int len, int isdis, int t4tx )
437 len = len > DCSLEN ? DCSLEN : len ;
440 fif[1] = ( isdis && t4tx ? 0x80 : 0 ) | 0x40 ;
441 for ( i=2 ; i<len-1 ; i++ ) fif[i] = 0x01 ; /* add extension bits */
446 for ( i=0 ; i<NCAP ; i++ ) {
448 if ( ( k = ( isdis ? p->captodis : p->captodcs ) [ c [ i ] ] ) == X )
449 msg ( "E3mkdis: can't happen (invalid %s)", p->name ), k=0 ;
450 if ( p->byte < len ) fif [ p->byte ] |= k << p->shift ;
456 /* Return length of DIS/DTC FIF (counts extension bits). */
459 int dislen ( uchar *fif )
462 for ( n=3 ; fif [ n-1 ] & 0x01 && n < MAXFIFLEN ; n++ ) ;
468 /* Convert received DIS/DCS/DTC FIF to cap. Returns 0 or 3 if bad DIS/DCS
472 int mkcap ( uchar *fif, cap c, int dis )
474 int err=0, i, j, k, len ;
477 len = dislen ( fif ) ;
479 for ( i=0 ; i<NCAP ; i++ ) {
481 if ( p->byte >= len ) {
484 j = ( fif [ p->byte ] >> p->shift ) & p->mask ;
485 k = ( dis ? p->distocap : p->dcstocap ) [ j ] ;
487 c [ i ] = p->safeval ;
488 err = msg("E3mkcap: bad %s field (%d) set to %d",
489 p->name, j, c [ i ] ) ;
500 /* Compute compatible local/remote capabilities. Used by the
501 sending station only and only for Class 1. Returns 0 if OK or
502 3 if no compatible settings possible. */
505 int mincap ( cap local, cap remote, cap session )
508 int msttab[2][8] = { { 0,1,3,3,5,5,7,7 } , { 0,1,1,3,3,5,5,7 } } ;
510 printcap ( "local ", local ) ;
511 printcap ( "remote ", remote ) ;
513 for ( i=0 ; i<NCAP && i!=ST && i !=BR ; i++ )
514 session[i] = remote[i] < local[i] ? remote[i] : local[i] ;
516 session[BR] = brindex[ remote[BR] ] < brindex[ local[BR] ] ?
517 remote[BR] : local[BR] ;
519 session[ST] = msttab [ session[VR] ] [ remote[ST] ] ;
521 printcap ( "session", session ) ;
523 if ( local[WD] != session[WD] || local[LN] > session[LN] ||
524 local[DF] != session[DF] )
525 err = msg ("W3incompatible local and remote capabilities" ) ;
532 /* Skip to start of first/next page (or to start of previous page
533 if dp is 0). If ppm in not null, it is then set to EOP if
534 there are no pages following this one, MPS if the next page
535 has the same format as `local' (assumed to be the format of
536 the previous page), EOM if the page has a different format.
537 If local is non-NULL its format fields are set according to
538 the format of the new page. Currently only considers the
541 This function is called before send_data() and obtains the ppm
542 for that page. It can be called again with dp=0 if a PIN or
543 RTN is received to restart the page. Returns 0 or 2 on
546 static int rdpage ( IFILE *f, int dp, int *ppm, cap local, int *changed )
548 int err=0, m=EOP, yres, fVR, nVR ;
550 if ( nextipage ( f, dp ) )
551 err = msg ( "E2 can't happen (rdpage: can't go to %s page)",
552 dp ? "next" : "same" ) ;
556 yres = f->page->yres ;
557 fVR = ( yres > (196+98)/2 ) ? 1 : 0 ;
559 if ( local && yres ) {
560 if ( local [ VR ] != fVR ) {
562 if ( changed ) *changed = 1 ;
564 if ( changed ) *changed = 0 ;
568 if ( lastpage ( f ) ) {
571 PAGE *p = f->page + 1 ;
572 nVR = ( p->yres > (196+98)/2 ) ? 1 : 0 ;
573 m = ( nVR != fVR ) ? EOM : MPS ;
579 *ppm = err ? EOP : m ;
586 /* Terminate previous page if page number is non-zero and start
587 next output page if page number is non-negative. If page is -1
588 removes the most recently opened file. Returns 0 if OK, 2 on
592 int wrpage ( OFILE *f, int page )
596 err = nextopage ( f, page ) ;
598 if ( ! err && page == -1 ) {
599 if ( remove ( f->cfname ) ) {
600 err = msg ( "ES2can't delete file %s:", f->cfname ) ;
602 msg ( "Fremoved %s", f->cfname ) ;
611 /* Send data for one page. Figures out required padding and 196->98 lpi
612 decimation based on local and session capabilitites, substitutes page
613 numbers in header string and enables serial port flow control. Inserts
614 the page header before the input file data. Converts each scan line to
615 T.4 codes and adds padding (FILL) and EOL codes before writing out.
616 Sends RTC when done. Sends DLE-ETX and returns serial port to command
617 mode when done. Returns 0 if OK, non-0 on errors. */
619 static int send_data ( TFILE *mf, IFILE *f, int page, int pages,
620 cap local, cap session
622 , char *header, faxfont *font
626 int done=0, err=0, noise=0, nr=0, lastnr=0, line, pixels ;
627 int i, decimate, pwidth, minlen, dcecps, inheader, skip=0 ;
628 uchar buf [ MAXCODES + 2*EOLBITS/8 + 1 ], *p ;
629 short runs [ MAXRUNS ], lastruns [ MAXRUNS ] ;
631 char headerbuf [ MAXLINELEN ] ;
637 dcecps = cps[session[BR]] ;
638 minlen = ( (long)dcecps * mst[session[ST]] - 1500 + 500 ) / 1000 ;
639 pwidth = pagewidth [ session [ WD ] ] ;
640 decimate = local[VR] > session[VR] ;
642 msg ( "T padding to %d bytes/scan line.%s", minlen+1,
643 decimate ? " reducing 196->98 lpi." : "" ) ;
646 msg ( "T limiting output to %d bps for %d byte modem buffer",
647 dcecps*8, MAXDCEBUF + MINWRITE ) ;
650 if ( ckfmt ( header, 6 ) )
651 msg ( "W too many %%d escapes in header format string \"%s\"", header ) ;
653 sprintf ( headerbuf, header, page, pages, page, pages, page, pages ) ;
654 msg ("I header:[%s]", headerbuf ) ;
657 done = err = ttymode ( mf, SEND ) ;
659 mf->start = time(0) ;
660 mf->mstart = proc_ms() ;
661 mf->bytes = mf->pad = mf->lines = 0 ;
663 /* start T.4 data with some FILL and an EOL */
665 for ( i=0 ; i<32 ; i++ )
666 p = putcode ( &e, 0, 8, buf ) ;
667 p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
670 err = msg ( "E2can't happen(send_data)" ) ;
673 for ( line=0 ; ! done && ! err ; line++ ) {
675 if ( line < HDRSPCE ) { /* insert blank lines at the top */
680 if ( ( nr = readline ( f, runs, &pixels ) ) < 0 ) {
686 /* generate and OR in header pixels */
687 if ( line >= HDRSTRT && line < HDRSTRT + HDRCHRH ) {
689 short hruns [ MAXRUNS ] ;
690 hnr = texttorun ( (uchar*) headerbuf, font, line-HDRSTRT,
691 HDRCHRW, HDRCHRH, HDRSHFT,
693 nr = runor ( runs, nr, hruns, hnr, 0, &pixels ) ;
697 inheader = line < HDRSTRT + HDRCHRH ;
699 if ( decimate || ( inheader && local[VR] == 0 ) ) {
700 if ( ++skip & 1 ) { /* save the first of every 2 lines */
701 memcpy ( lastruns, runs, nr * sizeof(short) ) ;
703 continue ; /* get next line */
704 } else { /* OR previous line into current line */
705 nr = runor ( runs, nr, lastruns, lastnr, 0, &pixels ) ;
711 /* make line the right width */
712 if ( pixels != pwidth ) nr = xpad ( runs, nr, pwidth - pixels ) ;
713 /* convert to MH coding */
714 p = runtocode ( &e, runs, nr, p ) ;
715 /* zero pad to minimum scan time */
716 while ( p - buf < minlen ) {
717 p = putcode ( &e, 0, 8, p ) ;
721 p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
722 sendbuf ( mf, buf, p - buf, dcecps ) ;
723 mf->bytes += p - buf ;
726 /* probably read an EOL as part of RTC */
728 if ( tdata ( mf, 0 ) ) noise = 1 ;
733 for ( i=0 ; i < RTCEOL ; i++ )
734 p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
735 p = putcode ( &e, 0, 0, p ) ;
736 sendbuf ( mf, buf, p - buf, dcecps ) ;
737 mf->bytes += p - buf ;
739 if ( noise ) msg ("W- characters received while sending" ) ;
745 static int end_data ( TFILE *mf, cap session, int ppm, int *good )
751 if ( ! ppm ) p = DLE_ETX ;
752 else if ( ppm == MPS ) p = "\020," ;
753 else if ( ppm == EOM ) p = "\020;" ;
754 else if ( ppm == EOP ) p = "\020." ;
757 err = msg ( "E2 can't happen (end_data)" ) ;
762 dt = time(0) - mf->start ;
763 /* time to drain buffers + 100% + 4s */
764 draintime = ( 2 * ( mf->bytes / cps[ session[BR] ] + 1 - dt ) + 4 ) * 10 ;
765 draintime = draintime < TO_DRAIN_D ? TO_DRAIN_D : draintime ;
767 c = ckcmd ( mf, 0, 0, (int) draintime, OK ) ;
769 if ( good ) *good = ( c == OK ) ? 1 : 0 ;
771 dt = time(0) - mf->start ;
773 msg ( "Isent %d+%d lines, %d+%d bytes, %d s %d bps" ,
774 HDRSPCE, mf->lines-HDRSPCE,
775 mf->bytes-mf->pad, mf->pad, (int) dt, (mf->bytes*8)/dt ) ;
777 if ( mf->bytes / (dt+1) > cps[session[BR]] )
778 msg ( "E flow control did not work" ) ;
780 if ( ! err ) err = ttymode ( mf, COMMAND ) ;
786 /* Read one scan line from fax device. If pointer pels is not
787 null it is used to save pixel count. Returns number of runs
788 stored, EOF on RTC, or -2 on EOF, DLE-ETX or other error. */
791 int readfaxruns ( TFILE *f, DECODER *d, short *runs, int *pels )
793 int err=0, c=EOF, x, n ;
796 short *p, *maxp, *q, len=0 ;
799 maxp = ( p = runs ) + MAXRUNS ;
801 x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
802 rd_state = f->rd_state ;
806 while ( shift < 0 ) {
807 c = tgetd ( f, TO_CHAR ) ;
809 rd_state = ( rd_state & rd_allowed[c] ) ?
810 ( ( rd_state & rd_nexts[c] ) ? rd_state <<= 1 : rd_state ) :
813 if ( rd_state == RD_END )
814 msg ( "W+ modem response in data" ) ;
817 x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
819 x = ( x << 8 ) | c ; shift += 8 ;
822 t = tab + ( ( x >> shift ) & 0x1ff ) ;
825 } while ( ! t->code ) ;
826 if ( p < maxp ) *p++ = t->code ;
827 } while ( t->code != -1 ) ;
829 d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
830 f->rd_state = rd_state ;
832 if ( p >= maxp ) msg ( "Wrun length buffer overflow" ) ;
834 /* combine make-up and terminating codes and remove +1 offset
838 for ( p = q = runs ; n-- > 0 ; )
839 if ( *p > 64 && n-- > 0 ) {
840 len += *q++ = p[0] + p[1] - 2 ;
843 len += *q++ = *p++ - 1 ;
847 /* check for RTC and errors */
852 if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
854 if ( c < 0 ) err = - 2 ;
856 if ( pels ) *pels = len ;
858 return err ? err : n ;
862 /* Receive data. Reads scan lines from modem and writes to output
863 file. Checks for errors by comparing received line width and
864 session line width. Check that the output file is still OK
865 and if not, send one CANcel character and wait for protocol to
866 complete. Returns 0 if OK or 2 if there was a file write error. */
868 int receive_data ( TFILE *mf, OFILE *f, cap session, int *nerr )
870 int err=0, line, lines, nr, len ;
871 int pwidth = pagewidth [ session [ WD ] ] ;
872 short runs [ MAXRUNS ] ;
875 if ( ! f || ! f->f ) {
876 msg ( "E2 can't happen (writeline)" ) ;
883 for ( line=0 ; ( nr = readfaxruns ( mf, &d, runs, &len ) ) >= 0 ; line++ ) {
884 if ( nr > 0 && len > 0 && line ) { /* skip first line+EOL and RTC */
885 if ( len != pwidth ) {
887 if ( *nerr <= MAXERRPRT ) msg ("R-+ (%d:%d)", line, len ) ;
888 nr = xpad ( runs, nr, pwidth - len ) ;
890 writeline ( f, runs, nr, 1 ) ;
893 if ( ferror ( f->f ) ) {
894 err = msg ("ES2file write:") ;
895 tput ( mf, CAN_STR, 1 ) ;
896 msg ("Wdata reception CANcelled") ;
901 if ( *nerr > MAXERRPRT ) msg ("R-+ ....." ) ;
902 msg ("R- : reception errors" ) ;
903 msg ("W- %d reception errors", *nerr ) ;
907 while ( tgetd ( mf, TO_CHAR ) >= 0 ) ; /* got RTC, wait for DLE-ETX */
909 msg ( "I- received %d lines, %d errors", lines, *nerr ) ;
915 /* Send training check sequence of n zeroes. Returns 0 or 2 on error. */
917 int puttrain ( TFILE *f, char *s, int n )
920 uchar buf [ MINWRITE ] = { 0 } ;
922 ckcmd ( f, &err, s, TO_FT, CONNECT ) ;
925 ttymode ( f, SEND ) ;
927 for ( i=0 ; i < n ; i += m ) {
928 m = n-i < MINWRITE ? n-i : MINWRITE ;
929 sendbuf ( f, buf, m, 0 ) ;
932 buf[0] = 1 ; /* make sure last byte is non-zero */
935 sendbuf ( f, buf, 3, 0 ) ;
937 ttymode ( f, COMMAND ) ;
939 ckcmd ( f, &err, 0, TO_DRAIN_D, OK ) ;
940 msg ( "I- sent TCF - channel check of %d bytes", n ) ;
947 /* Checks for an error-free run of at least n bytes in the
948 received training check sequence. Sets good if it's not null,
949 the run was long enough and there were no errors. Returns 0 or
950 3 on other errors. */
952 int gettrain ( TFILE *f, char *s, int n, int *good )
954 int err=0, c, i=0, maxrunl=0, runl=0 ;
956 ckcmd ( f, &err, s, T2, CONNECT ) ;
960 for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
962 if ( runl > maxrunl ) maxrunl = runl ;
969 err = msg ( "E3timed out during training check data" ) ;
971 ckcmd ( f, &err, 0, TO_RTCMD, NO ) ;
975 if ( runl > maxrunl ) maxrunl = runl ;
977 if ( good ) *good = !err && maxrunl > n ;
980 msg ( "I- received TCF - channel check (%sOK: run of %d in %d)",
981 maxrunl > n ? "" : "not ", maxrunl, i ) ;
988 /* Log a sent/received HDLC frame. Display of these messages is delayed to
989 avoid possible timing problems. */
991 void logfr ( char *s , char *nm , uchar *p , int n )
994 msg ( n > 10 ? "H- %s %d bytes:" : "H-+ %s %d bytes:" , s, n ) ;
995 for ( i=0 ; i<n ; i++ ) {
996 msg ( "H-+ %02x" , p[i] & 0xff ) ;
997 if ( ( i&0xf ) == 0xf && i != n-1 ) msg ( "H-" ) ;
1000 msg ( "I- %s %s", s, nm ) ;
1002 #endif /* UCLINUX */
1005 /* Send HDLC control frame of type type. Extra bits can be OR'ed
1006 into the frame type (FCF) to indicate that this frame follows
1007 a previous one (no +FTH required) and/or that more frames will
1008 follow. Sets up flag, address, and fax control field bytes in
1009 `buf'. Sends these plus `len` additional bytes. Terminates
1010 with DLE-ETX and checks response. Returns 0 if OK, 2 or 3 on
1013 #define MORE_FR 0x100
1014 #define SUB_FR 0x200
1016 static int nframes = 0 ; /* counts frames sent/received */
1019 int putframe ( int type, uchar *buf, int len, TFILE *f, int t )
1024 buf [ 1 ] = type & MORE_FR ? 0xc0 : 0xc8 ;
1025 buf [ 2 ] = type & 0xff ;
1027 if ( nframes++ && ! ( type & SUB_FR ) )
1028 ckcmd ( f, &err, "+FTH=3" , TO_FT, CONNECT ) ;
1032 if ( ! buf[len+2] ) {
1033 msg ( "Wlast byte of frame is NULL" ) ;
1036 /* ttymode ( f, SEND ) ; */
1037 sendbuf ( f, buf, len+3, 0 ) ;
1038 tput ( f, DLE_ETX, 2 ) ;
1039 /* ttymode ( f, COMMAND ) ; */
1041 logfr ( "sent", frname ( buf [ 2 ] ), buf, len+3 ) ;
1043 ckcmd ( f, &err, 0, TO_DRAIN_H, ( type & MORE_FR ) ? CONNECT : OK ) ;
1050 /* Reverse bit and byte order of ID strings as per T.30 5.3.6.2.4-6 */
1052 void revcpy ( uchar *from , uchar *to )
1055 for ( i=0, j=IDLEN-1 ; i<IDLEN ; i++, j-- )
1056 to [ i ] = normalbits [ from [ j ] & 0xff ] ;
1060 /* Check for missing initial 0xFF in HDLC frame and insert it if
1061 missing. Ugly fix for a still-hidden bug. Also do bit
1062 inversion if required. */
1064 int fixframe ( uchar *buf, int n, TFILE *f )
1068 if ( *buf == 0xc0 || *buf == 0xc8 ) {
1069 for ( i=n; i >= 1 ; i-- )
1072 msg ("W HDLC frame missing initial 0xff" ) ;
1076 if ( buf[1] == 0x03 || buf[1] == 0x13 ) {
1077 for ( i=0 ; i < n ; i++ )
1078 buf[i]=normalbits[buf[i]] ;
1079 msg ("W bit-reversed HDLC frame, reversing bit order" ) ;
1080 f->ibitorder = f->ibitorder == normalbits ? reversebits : normalbits ;
1087 /* Read HDLC frame data. Returns 0 if OK, 1 on frame error, 3 on
1088 timeout, invalid response or too-long frame. */
1090 int receive_frame_data ( TFILE *f, uchar *buf, int n, int *len )
1094 for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
1095 if ( i < n ) buf[ i ] = c ;
1099 err = msg ( "E3timed out reading frame data" ) ;
1103 switch ( cmd ( f, 0, TO_RTCMD ) ) {
1109 err = msg ( "W1frame error" ) ;
1112 err = msg ( "E3no response after frame data" ) ;
1115 err = msg ( "E3wrong response after frame data" ) ;
1122 err = msg ( "E3frame too long (%d, > %d max bytes)", i, n ) ;
1124 if ( len ) *len = i ;
1128 #endif /* UCLINUX */
1131 /* Get a Class 1 command or response frame. An attempt to match
1132 and combine T.30 "Response Received?" and "Command Received?"
1133 protocol flowcharts.
1135 When receiving commands returns after first correct
1136 non-optional frame or after the time given by getcmd has
1137 elapsed. This is instead of looping through main flowchart.
1139 When receiving responses returns on the first detected
1140 non-optional frame, after timeout T4, or on errors.
1142 Returns immediately if gets a +FCERROR response so can retry
1143 as data carrier. Returns DCN as a valid frame instead of
1146 Returns the command/response received, or EOF on timeout or
1152 int getfr ( TFILE *mf, uchar *buf, int getcmd )
1154 int err=0, frame=0, frlen, c, t ;
1155 char remoteid [ IDLEN + 1 ] ;
1159 start = 10*time(0) ;
1161 t = getcmd ? ( getcmd > 1 ? getcmd : T2 ) : T4 ;
1168 c = cmd ( mf, "+FRH=3", t ) ;
1170 c = CONNECT ; /* implied by ATA or ATD */
1174 case EOF: /* time out */
1175 tput ( mf, CAN_STR, 1 ) ;
1176 ckcmd ( mf, 0, 0, TO_ABRT, OK ) ;
1179 case NO: /* S7 time out */
1182 case MODULATION: /* data carrier (or DHS) */
1183 return -msg ( "W-2 wrong carrier" ) ;
1185 case CONNECT: /* frame */
1187 default: /* shouldn't happen */
1188 err = msg ( "E3wrong response to receive-frame command" ) ;
1193 err = receive_frame_data ( mf, buf, MAXFRLEN, &frlen ) ;
1195 if ( ! err && frlen < 3 )
1196 err = msg ( "E3received short frame (%d bytes)", frlen ) ;
1200 frlen = fixframe ( buf, frlen, mf ) ;
1201 logfr ( "received", frname ( buf [ 2 ] ), buf, frlen ) ;
1202 frame = buf [ 2 ] & 0x7f ;
1214 revcpy ( fif , (uchar*) remoteid ) ;
1215 msg ( "I- remote ID -> %*.*s", IDLEN, IDLEN, remoteid ) ;
1221 if ( err && getcmd && ( t -= 10*time(0) - start ) > 0 )
1224 return err ? EOF : frame ;
1228 /* Class 1 send/receive.
1230 The logic in this function is a mess because it's meant to
1231 mirror the flowchart in ITU-T recommendation T.30 which is the
1232 protocol specification.
1237 TFILE *mf, cap local, char *localid,
1238 OFILE *outf, IFILE *inf,
1239 int pages, char *header, faxfont *font,
1240 int maxpgerr, int noretry, int calling )
1242 int err=0, rxpage=0, page=1, t, disbit, good, frame, last, nerr ;
1243 int rxdislen, ppm, try=0, pagetry=0, retry=0, remtx=0, remrx=0 ;
1244 int writepending=0, dp=0 ;
1245 cap remote = { DEFCAP }, session = { DEFCAP } ;
1247 uchar buf [ MAXFRLEN ], *fif=buf+3 ;
1249 if ( ! calling ) goto RX ;
1251 /* Class 1 Transmitter: */
1253 T: /* Transmitter Phase B - wait for DIS or DTC */
1257 frame = getfr ( mf, buf, T1 ) ;
1260 err = msg ( "E3no answer from remote fax" ) ;
1264 if ( frame != DIS && frame != DTC ) {
1265 msg ( "W2 can't open page" ) ;
1269 disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
1272 A: /* decide to send or receive after DIS/DTC */
1274 if ( frame == DIS || frame == DTC ) {
1275 rxdislen = dislen ( fif ) ;
1276 mkcap ( fif, remote, 1 ) ;
1277 remtx = fif[1] & 0x80 ;
1278 remrx = fif[1] & 0x40 ;
1281 msg ( "N remote has %sdocument(s) to send, and can %sreceive",
1282 remtx ? "" : "no ", remrx ? "" : "not " ) ;
1285 if ( ! remrx ) msg ( "W remote cannot receive, trying anyways" ) ;
1288 if ( ! remtx ) msg ( "W remote has nothing to send, trying anyways" ) ;
1294 if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
1295 err = msg ( "E2can't open page" ) ;
1301 mincap ( local, remote, session ) ;
1303 revcpy ( (uchar*) localid, fif ) ;
1305 err = putframe ( TSI | MORE_FR | disbit, buf, IDLEN, mf, -1 ) ;
1307 mkdis ( session, fif, DCSLEN, 0, pages ) ;
1309 err = putframe ( DCS | SUB_FR | disbit, buf, DCSLEN, mf, -1 ) ;
1311 if ( cmd ( mf, "+FTS=8", T3S ) != OK )
1312 msleep ( TMOD ) ; /* if +FTS not supported */
1315 err = puttrain ( mf, c1cmd[SND][TRN][session[BR]],
1316 1.5*cps [ session[BR] ] ) ;
1320 cmd ( mf, "+FRS=1", T3S ) ; /* wait for TCF carrier to drop */
1321 frame = getfr ( mf, buf, 0 ) ;
1324 if ( err || frame < 0 ) {
1336 if ( try >= 3 ) goto C_timeout ;
1340 msg ( "I channel not usable at %d bps", 8*cps[session[BR]] ) ;
1341 remote[BR] = fallback[session[BR]] ;
1342 if ( remote[BR] >= 0 ) goto D_2 ;
1343 else { err = msg ( "E2 channel not usable at lowest speed" ) ; goto C ; }
1349 err = msg ( "E3 invalid response to DCS (0x%02x)", frame ) ;
1353 I: /* send a page */
1355 if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
1356 err = msg ( "E2can't open page" ) ;
1362 ckcmd ( mf, &err, c1cmd [SND][DTA][session[BR]], TO_FT, CONNECT ) ;
1364 err = send_data ( mf, inf, page, pages, local, session, header, font ) ;
1369 err = end_data ( mf, session, 0, 0 ) ;
1371 if ( cmd ( mf, "+FTS=8", T3S ) != OK )
1372 msleep ( TMOD ) ; /* if +FTS not supported */
1374 /* fix ppm if on last page of stdin */
1375 if ( lastpage ( inf ) ) ppm = EOP ;
1379 if ( !err ) err = putframe ( ppm | disbit, buf, 0, mf, -1 ) ;
1382 frame = getfr ( mf, buf, 0 ) ;
1391 fname = inf->page->fname ;
1393 switch ( noretry ? MCF : frame ) { /* common retry logic */
1397 fname = inf->page->fname ;
1398 if ( fname ) msg ( "Isent -> %s", fname ) ;
1406 retry = pagetry < NTXRETRY ;
1409 err = msg ( "E3invalid post-page response (0x%02x)", frame ) ;
1430 nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
1431 if ( remtx ) goto R ; /* poll after sending */
1434 if ( retry ) goto D ;
1445 cmd ( mf, "+FRS=20", T3S ) ; /* wait for ppr carrier to drop */
1446 if ( retry ) goto T ;
1452 E: /* ignore PIN and PIP */
1453 msg ( "W interrupt request ignored" ) ;
1457 /* Class 1 Receiver */
1461 R: /* Receiver Phase B */
1463 if ( ! err ) err = wrpage ( outf, rxpage ) ;
1467 for ( t=0 ; !err && t<T1 ; t+=T2+10 ) {
1469 revcpy ( (uchar*) localid, fif ) ;
1471 err = putframe ( CSI | disbit | MORE_FR, buf, IDLEN, mf, -1 ) ;
1473 mkdis ( local, fif, DEFDISLEN, 1, pages ) ;
1475 err = putframe ( DIS | disbit | SUB_FR, buf, DEFDISLEN, mf, -1 ) ;
1477 frame = getfr ( mf, buf, 0 ) ;
1480 disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
1485 else goto C_timeout ;
1488 F: /* get a command */
1491 frame = getfr ( mf, buf, 1 ) ;
1493 if ( writepending ) { /* do postponed file close/open */
1495 err = wrpage ( outf, rxpage ) ;
1500 if ( frame == -2 ) goto getdata ; /* data carrier detected */
1501 if ( last == EOM ) goto R ;
1502 else { err = msg ("E3 timed out waiting for command" ) ; goto B ; }
1517 mkcap ( fif, session, 0 ) ;
1518 printcap ( "session", session ) ;
1520 cmd ( mf, "+FTS=1", T3S ) ; /* make sure DCS is over */
1522 gettrain ( mf, c1cmd [RCV][TRN][session[BR]], cps[session[BR]], &good ) ;
1524 if ( putframe ( ( good ? CFR : FTT ) | disbit, buf, 0, mf, -1 ) ||
1529 outf->w=pagewidth[session[WD]];
1532 outf->yres=vresolution[session[VR]];
1534 if ( cmd ( mf, c1cmd [RCV][DTA][session[BR]], TO_FT ) != CONNECT )
1535 goto F ; /* +FCERROR -> DCS resent */
1537 if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
1538 good = nerr < maxpgerr ;
1539 msg ( "I-received -> %s", outf->cfname ) ;
1540 writepending=1 ; /* ppm follows immediately, don't write yet */
1545 ckcmd ( mf, 0, 0, TO_RTCMD, NO ) ;
1553 frame &=0xf7 ; /* ignore PRocedure Interrupt bit */
1557 putframe ( ( good ? MCF : RTN ) | disbit, buf, 0, mf, -1 ) ;
1558 if ( good && frame == MPS ) goto getdata ;
1565 err = msg ( "E3 unrecognized command" ) ;
1571 err = msg ( "E3 no command/response from remote" ) ;
1574 putframe ( DCN, buf, 0, mf, -1 ) ;
1577 ckcmd ( mf, 0, "H", TO_RESET, OK ) ; /* hang up */
1580 wrpage ( outf, -1 ) ; /* remove last file */
1584 #endif /* UCLINUX */
1587 /* Check for hangup message. Assumes hsc is initialized to a
1588 negative value. Returns 0 if no hangup message, 1 if there
1589 was one. If perr is not null, sets it to 2 if the hsc was
1590 non-zero (error). */
1592 static int gethsc ( int *hsc, int *perr )
1595 if ( sresponse ( "+FHNG:", hsc ) || sresponse ( "+FHS:", hsc ) ) {
1596 if ( hsc && *hsc > 0 ) {
1597 err = msg ( "E2abnormal termination (code %d)", *hsc ) ;
1598 for ( i=0 ; c2msg[i].min >= 0 ; i++ ) {
1599 if ( *hsc >= c2msg[i].min && *hsc <= c2msg[i].max ) {
1600 msg ( "E %s", c2msg[i].msg ) ;
1603 if ( perr && ! *perr ) {
1614 /* Print remote ID and store DCS values in session as per
1615 responses since last command. */
1617 static void getc2dcs ( cap session )
1620 if ( ( p = sresponse ( "+FTI:", 0 ) ) != 0 ||
1621 ( p = sresponse ( "+FTSI:", 0 ) ) != 0 ) {
1622 msg ( "I- remote ID -> %s", p ) ;
1624 if ( ( p = sresponse ( "+FCS:", 0 ) ) != 0 ||
1625 ( p = sresponse ( "+FDCS:", 0 ) ) != 0 ) {
1626 str2cap ( p, session ) ;
1627 printcap ( "session", session ) ;
1631 /* Wait for a starting character XON or DC2. Display & ignore
1632 any other characters received. */
1634 static void getstartc ( TFILE *mf )
1638 for ( noise=0 ; ( c = tgetc ( mf, TO_C2X ) ) != XON && c != DC2 ; noise++ ) {
1640 msg ( "Wno XON/DC2 received after CONNECT") ;
1643 msg ( "W-+%s", cname ( c ) ) ;
1649 msg ( "W : %d characters received while waiting to send", noise ) ;
1653 /* Class 2 send and receive.
1655 If calling, polls if no files to send, otherwise sends. If
1656 not calling sends documents if files to send, else receives.
1658 When sending, issues +FDIS to change session parameters if
1659 file format changes, then sends +FDT followed by data and a
1660 post-page message determined by format of next page, if any.
1661 Retransmits each page up to NTXRETRY times.
1663 When receiving extracts file format from responses to +FDR or
1664 ATA and saves them in the file. Receives data to a file and
1665 sets page transfer status if too many errors.
1667 Returns 0 if OK or 2 on errors. */
1670 static int c2sndrcv (
1671 TFILE *mf, cap local, char *localid,
1672 OFILE *outf, IFILE *inf,
1675 char *header, faxfont *font,
1676 #endif /* UCLINUX */
1677 int maxpgerr, int noretry, int calling )
1681 #endif /* UCLINUX */
1682 int err=0, done=0, page, pagetry, dp=0 ;
1683 int ppm=0, good, hsc, changed ;
1686 cap session = { 0,0,0,0, 0,0,0,0 } ;
1687 char buf [ CMDBUFSIZE ] ;
1689 hsc=-1 ; /* will be set >= 0 on hangup */
1691 if ( sresponse ( "+FPO", 0 ) ) {
1693 msg ( "N remote has document(s) to send." ) ;
1699 #endif /* UCLINUX */
1703 #endif /* UCLINUX */
1707 if ( pages ) goto pollserver ;
1710 #endif /* UCLINUX */
1716 #endif /* UCLINUX */
1718 /* with +FLP[L]=1 the modem should accept +FDT. */
1725 while ( ! err && ! done ) {
1727 err = rdpage ( inf, dp, &ppm, local, &changed ) ;
1729 if ( ! err && changed ) {
1730 sprintf ( buf, c20 ? "+FIS=%d,%d,%d,%d" : "+FDIS=%d,%d,%d,%d",
1731 local[0], local[1], local[2], local[3] ) ;
1732 ckcmd ( mf, 0, buf, TO_FT, OK ) ;
1733 if ( gethsc ( &hsc, &err ) ) {
1738 ckcmd ( mf, &err, "+FDT", -TO_C2B, CONNECT ) ;
1739 if ( err || gethsc ( &hsc, &err ) ) {
1744 getc2dcs ( session ) ;
1746 if ( ! c20 ) getstartc ( mf ) ;
1748 send_data ( mf, inf, page, pages, local, session
1751 #endif /* UCLINUX */
1756 end_data ( mf, session, ppm, &good ) ;
1758 end_data ( mf, session, 0, 0 ) ;
1760 gethsc ( &hsc, &err ) ;
1762 if ( ! err && hsc < 0 ) {
1763 ckcmd ( mf, &err, ppm == EOP ? "+FET=2" :
1764 ppm == EOM ? "+FET=1" : "+FET=0" , TO_C2PP, OK ) ;
1767 gethsc ( &hsc, &err ) ;
1769 if ( ! err && hsc < 0 ) {
1770 if ( sresponse ( "+FPTS:", &good ) ) {
1771 good &= 1 ; /* odd values mean received OK */
1772 } else { /* no +FPTS and +FHNG probably NG */
1773 good = gethsc ( 0, 0 ) ? 0 :
1774 msg ( "W1no +FPTS response, assumed received" ) ;
1780 if ( noretry ) good = 1;
1783 fname = inf->page->fname ;
1784 if ( fname ) msg ( "Isent -> %s", fname ) ;
1789 nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
1794 if ( pagetry >= NTXRETRY )
1795 err = msg ( "E2too many page send retries" ) ;
1798 if ( gethsc ( &hsc, &err ) ) done=1 ;
1800 if ( good && lastpage ( inf ) ) {
1808 /* Class 2 Receive */
1812 /* with +FSP[L]=1 and +FPO[LL]: the modem should now accept +FDR. */
1816 getc2dcs ( session ) ; /* get ATA responses */
1819 for ( page=0 ; ! err && ! done ; page++ ) {
1821 if ( ! ( err = wrpage ( outf, page ) ) ) {
1822 c = cmd ( mf, "+FDR", -TO_C2R ) ;
1827 getc2dcs ( session ) ;
1829 outf->w=pagewidth[session[WD]];
1832 outf->yres=vresolution[session[VR]];
1834 tput ( mf, &startchar, 1 ) ;
1836 if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
1837 good = nerr < maxpgerr ;
1838 msg ( "I-received -> %s", outf->cfname ) ;
1843 ckcmd ( mf, &err, 0, TO_C2EOR, OK ) ;
1845 if ( err || gethsc ( &hsc, &err ) ) {
1846 wrpage ( outf, +1 ) ;
1847 wrpage ( outf, -1 ) ;
1853 msg ( "Wreception errors" ) ;
1854 ckcmd ( mf, 0, c20 ? "+FPS=2" : "+FPTS=2", T3S, OK ) ;
1855 if ( gethsc ( &hsc, &err ) ) continue ;
1860 wrpage ( outf, -1 ) ; /* no more pages */
1862 if ( gethsc ( &hsc, &err ) ) continue ;
1866 wrpage ( outf, -1 ) ; /* oops */
1867 err = msg ( "E3receive (+FDR) command failed") ;
1872 #endif /* UCLINUX */
1876 if ( hsc < 0 ) ckcmd ( mf, 0, c20 ? "+FKS" : "+FK", TO_RESET, OK ) ;
1882 /* Dial the phone number given by string s. If nowait is true
1883 adds a ';' to the dial string to avoid waiting for a
1884 CONNECTion (might allow ersatz polling). Also resets the
1885 global "nframes" if appropriate so getfr() and putframe() know
1886 not to issue +FRH/+FTH. Returns 0 if dialed OK, 1 if busy, 2
1889 static int dial ( TFILE *f, char *s, int nowait )
1892 char c, dsbuf [ 128 ], *p ;
1894 sprintf ( dsbuf, nowait ? "D%.126s;" : "D%.127s" , s ) ;
1895 msg ( "Idialing %s", dsbuf+1 ) ;
1897 c = cmd ( f, dsbuf, TO_A ) ;
1899 if ( ( p = sresponse ( "+FCSI:", 0 ) ) != 0 ||
1900 ( p = sresponse ( "+FCI:", 0 ) ) != 0 ) {
1901 msg ( "I- remote ID -> %s", p ) ;
1904 if ( nowait && c == OK ) {
1907 } else if ( c1 && c == CONNECT ) {
1908 msg ( "Iconnected" ) ;
1910 } else if ( !c1 && c == OK ) {
1911 msg ( "Iconnected" ) ;
1912 } else if ( c == BUSY ) {
1913 err = msg ( "W1number is busy" ) ;
1915 err = msg ( "E2dial command failed" ) ;
1918 gethsc ( &hsc, err ? 0 : &err ) ;
1924 /* Figure out which mode the modem answered in (fax, data, voice
1925 or none) based on modem class and responses to the previous
1926 command. Sets crate (connect rate) for DATAMODE and hsc
1927 (hangup status code) if detects a class 2 hangup message. */
1929 enum connectmode { NONE, DATAMODE, FAXMODE, VOICEMODE } ;
1932 enum connectmode ansmode ( int *crate, int *hsc )
1934 enum connectmode mode = NONE ;
1937 if ( c1 && sresponse ( "CONNECT", &x ) ) {
1938 mode = x ? DATAMODE : FAXMODE ;
1941 if ( !c1 && sresponse ( "OK", 0 ) ) {
1945 if ( !c1 && ( sresponse ( "CONNECT", &x ) || sresponse ( "+FDM", 0 ) ) ) {
1949 if ( sresponse ( "DATA", 0 ) || sresponse ( "CONNECT DATA", 0 ) ) {
1951 sresponse ( "CONNECT", &x ) ;
1954 if ( sresponse ( "FAX", 0 ) || sresponse ( "+FCO", 0 ) ) {
1958 if ( sresponse ( "VCON", 0 ) ) {
1962 if ( gethsc ( hsc, 0 ) ) {
1966 if ( DATAMODE && x ) *crate = x ;
1970 #endif /* UCLINUX */
1973 /* Answer the phone. Remove our lock if sharing device with
1974 outgoing calls. If waiting for call, wait for modem activity,
1975 else answer phone. Figure out what mode we answered in and
1976 handle call appropriately. Re-lock if necessary. Exec *getty
1977 or *vcmd for data or voice calls. */
1980 int answer ( TFILE *f, char **lkfile,
1981 int wait, int share, int softaa,
1982 char *getty, char *vcmd, char *acmd )
1985 int crate=19200, hsc=-1, i ;
1986 enum connectmode mode=NONE ;
1988 if ( ! err && share ) {
1989 err = ttymode ( f, COMMAND ) ;
1991 err = unlockall ( lkfile ) ;
1994 if ( ! err && wait ) {
1995 msg ( "Iwaiting for activity") ;
1997 msg ( "Iactivity detected") ;
2000 if ( ! err && share ) {
2001 msleep ( 500 ) ; /* let other programs lock port */
2002 err = lockall ( lkfile, 1 ) ;
2004 err = msg ( "W1can't answer: can't lock device" ) ;
2006 err = ttymode ( f, COMMAND ) ; /* in case it was changed silently */
2009 for ( i=0 ; ! err && mode == NONE && ( i==0 || ( i==1 && softaa ) ) ; i++ ) {
2011 c = cmd ( f, wait ? 0 : acmd, ( i==0 && softaa ) ? TO_DATAF : TO_A ) ;
2013 if ( c == DATA ) cmd ( f, c1 ? "O" : 0, TO_A ) ; /* +FAE=1 weirdness */
2015 mode = ansmode ( &crate, &hsc ) ;
2019 msg ( "Idata call answered") ;
2020 if ( getty && *getty ) {
2021 char buf [ MAXGETTY ] ;
2022 if ( ckfmt ( getty, 6 ) ) {
2023 err = msg ( "E3 too many %%d escapes in command (%s)", getty ) ;
2025 sprintf ( buf, getty, crate, crate, crate, crate, crate, crate ) ;
2026 msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
2027 execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
2028 err = msg ( "ES2exec failed:" ) ;
2031 err = msg ( "E2no getty command defined for data call" ) ;
2036 msg ( "Ifax call answered") ;
2039 msg ( "Ivoice call answered") ;
2040 if ( vcmd && *vcmd ) {
2041 char buf [ MAXGETTY ] ;
2042 if ( ckfmt ( vcmd, 6 ) ) {
2044 sprintf ( buf, vcmd, f->fd, f->fd, f->fd, f->fd, f->fd, f->fd ) ;
2045 msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
2046 execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
2047 err = msg ( "ES2exec failed:" ) ;
2050 err = msg ( "E2no voice command defined for voice call" ) ;
2054 if ( i==0 && softaa && hsc < 0 && getty && *getty ) {
2055 int j ; /* switch to fax for 2nd try */
2056 for ( j=0 ; j<3 ; j++ )
2057 if ( cmd ( f, c1 ? "+FCLASS=1" :
2058 ( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), -TO_RESET )
2063 err = msg ( "E3unable to answer call") ;
2067 err = msg ( "E3can't happen(answer)" ) ;
2075 #endif /* UCLINUX */
2078 /* Initialize modem. Determine class to use and issue
2079 class-specific fax initialization commands. If poll is true,
2080 issues commands to enable polling also. Returns 0 or 3 if a
2081 mandatory setup command fails. */
2083 static int modem_init ( TFILE *mf, cap c, char *id,
2084 int calling, int poll, int capsset, int *preverse )
2086 int err=0, t=-TO_RESET ;
2087 char buf [ CMDBUFSIZE ], model [ CMDBUFSIZE ] = "" ;
2088 char **p, *q, *modelq [2][4] = { { "+FMFR?", "+FMDL?", 0 },
2089 { "+FMI?", "+FMM?", "+FMR?", 0 } } ;
2092 /* diasable command echo and get firmware revision */
2094 cmd ( mf, "E0", t ) ;
2096 if ( cmd ( mf, "I3", t ) == OK ) {
2097 getresp ( "", model, CMDBUFSIZE-1 ) ;
2098 strcat ( model, " " ) ;
2101 /* if not already chosen, pick safest class; set it */
2103 if ( ! err && ! c1 && !c2 && ! c20 ) {
2104 if ( cmd ( mf, "+FCLASS=?", t ) != OK ) {
2105 err = msg ("E3 modem does not support fax" ) ;
2107 if ( strinresp ( "2.0" ) ) c20 = 1 ;
2108 else if ( strinresp ( "2" ) ) ;
2110 else if ( strinresp ( "1" ) ) c1 = 1 ;
2111 #endif /* UCLINUX */
2112 else err = msg ("E3 can't determine fax modem class support" ) ;
2114 if ( strstr ( model, "Sportster" ) ) { /* USR Sporsters are buggy */
2118 #endif /* UCLINUX */
2122 ckcmd ( mf, &err, c1 ? "+FCLASS=1" :
2123 ( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), t, OK ) ;
2125 /* get make & model if using Class 2 or 2.0 */
2128 for ( p = modelq [ c20 ] ; ! err && *p ; p++ ) {
2129 if ( cmd ( mf, *p, t ) == OK ) {
2130 getresp ( "", model, CMDBUFSIZE-1 ) ;
2131 strcat ( model, " " ) ;
2135 if ( ! c1 && strstr ( model, "Multi-Tech" ) ) {
2137 msg ("I Multi-Tech bit order set" ) ;
2142 msg ( "I using %sin class %s", model, c1 ? "1" : c20 ? "2.0" : "2" ) ;
2144 /* get maximum modem speed if not already set (Class 1 only) */
2146 if ( ! err && c1 && ! capsset ) {
2148 char *c1spstr [6] = { "24", "48", "72", "96", "121", "145" } ;
2149 ckcmd ( mf, &err, calling ? "+FTM=?" : "+FRM=?", t, OK ) ;
2150 for ( i=0 ; i < 6 && strinresp ( c1spstr[i] ) ; i++ ) ;
2154 /* issue essential commands and set/get modem capabilities (Class 2 only) */
2156 if ( ! err && ! c1 ) {
2159 ckcmd ( mf, 0, "+FIP", t, OK ) ;
2160 ckcmd ( mf, 0, "+FNR=1,1,1,1", t, OK ) ;
2163 ckcmd ( mf, &err, "+FCR=1", t, OK ) ;
2166 if ( cmd ( mf, c20 ? "+FIS?" : "+FDIS?", -t ) == OK &&
2167 ( q = strinresp ( "," ) ) ) {
2168 str2cap ( q-1, c ) ;
2170 msg ( "W can't get modem capabilities, set to default" ) ;
2176 sprintf ( buf, c20 ? "+FCC=%d,%d,%d,%d,%d,%d,%d,%d" :
2177 "+FDCC=%d,%d,%d,%d,%d,%d,%d,%d",
2178 c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7] ) ;
2179 ckcmd ( mf, 0, buf, -t, OK ) ;
2182 sprintf ( buf, c20 ? "+FLI=\"%.*s\"" : "+FLID=\"%.*s\"" ,
2183 CMDBUFSIZE-9, id ) ;
2184 ckcmd ( mf, 0, buf, -t, OK ) ;
2186 if ( ! err && poll ) {
2188 ckcmd ( mf, 0, c20 ? "+FSP=1" : "+FSPL=1", -t, OK ) ;
2190 sprintf ( buf, c20 ? "+FPI=\"%.*s\"" : "+FCIG=\"%.*s\"" ,
2191 CMDBUFSIZE-9, id ) ;
2192 ckcmd ( mf, 0, buf, -t, OK ) ;
2201 /* the following are global so can terminate properly on signal */
2203 static char *icmd[3][ MAXICMD+1 ] = {{0},{0},{0}} ; /* initialization commands */
2204 static TFILE faxdev = { -1, 0,0, {0}, 0, 0 } ; /* modem */
2205 static char *lkfile [ MAXLKFILE+1 ] = {0} ; /* lock file names */
2206 static IFILE ifile = { 0 } ; /* files being sent */
2207 static int locked = 0 ; /* modem locked */
2210 /* print names of files not sent and reset modem before
2213 static int cleanup ( int err )
2215 /* log names of files not sent */
2216 logifnames ( &ifile, "I failed -> %s" ) ;
2218 if ( ! locked && faxdev.fd >= 0 )
2219 end_session ( &faxdev, icmd[2], lkfile, err != 4 ) ;
2221 msg ( "Idone, returning %d (%s)", err,
2222 errormsg [ err >= 0 && err <= 5 ? err : 6 ] ) ;
2228 /* signal handler */
2230 static void onsig ( int sig )
2232 msg ( "E terminating on signal %d", sig ) ;
2233 exit ( cleanup ( 5 ) ) ;
2237 /* Fax send/receive program for Class 1, 2 and 2.0 fax
2238 modems. Returns 0 on success, 1 if number busy or device
2239 locked, 2 for errors, 3 for protocol errors, 4 if no modem
2240 response, 5 on signal. */
2242 int main( int argc, char **argv)
2244 int err=0, doneargs=0, c=0, i ;
2250 #endif /* UCLINUX */
2252 int nicmd[3]={0,0,0}, nlkfile=0, nverb=0 ;
2254 char *faxfile = FAXFILE ;
2256 int softaa=0, share=0, wait=0, reverse=1, ignerr=0, noretry=0, hwfc=0 ;
2258 char *getty = "", *vcmd = "", *acmd=ANSCMD ;
2260 cap local = { DEFCAP } ;
2261 char localid [ IDLEN + 1 ] = DEFID ;
2263 int maxpgerr = MAXPGERR ;
2267 char *header = 0, headerbuf [ MAXLINELEN ] ;
2268 char *fontname = 0 ;
2270 #endif /* UCLINUX */
2276 char *ansfname = DEFPAT ;
2277 char fnamepat [ EFAX_PATH_MAX ] ;
2278 #endif /* UCLINUX */
2280 /* print initial message to both stderr & stdout */
2283 msg ( "I " Version " " Copyright ) ;
2284 argv0 = efaxbasename ( argv0 ) ;
2285 msg ( "A compiled "__DATE__ " " __TIME__ ) ;
2288 while ( ! err && ! doneargs &&
2289 ( c = nextopt ( argc,argv,
2290 "a:c:d:e:f:g:h:i:j:k:l:o:p:q:r:st:v:wx:T" ) ) != -1 ) {
2297 err = str2cap ( nxtoptarg, local ) ;
2301 if ( strlen ( nxtoptarg ) > IDLEN )
2302 msg("Wlocal ID (%s) truncated to %d characters", nxtoptarg, IDLEN ) ;
2303 if ( strspn ( nxtoptarg, " +0123456789" ) != strlen ( nxtoptarg ) )
2304 msg("Wlocal ID (%s) has non-standard characters", nxtoptarg ) ;
2305 sprintf ( localid, "%*.*s", IDLEN, IDLEN, nxtoptarg ) ;
2308 if ( nicmd[0] < MAXICMD ) icmd[0][ nicmd[0]++ ] = nxtoptarg ;
2309 else err = msg ( "E2too many '-i' options");
2312 if ( nicmd[1] < MAXICMD ) icmd[1][ nicmd[1]++ ] = nxtoptarg ;
2313 else err = msg ( "E2too many '-j' options");
2316 if ( nicmd[2] < MAXICMD ) icmd[2][ nicmd[2]++ ] = nxtoptarg ;
2317 else err = msg ( "E2too many '-k' options");
2321 header = nxtoptarg ;
2324 fontname = nxtoptarg ;
2326 #endif /* UCLINUX */
2328 faxfile = nxtoptarg ;
2336 case 'o': /* most protocol options are globals */
2337 for ( ; *nxtoptarg ; nxtoptarg++ )
2338 switch ( *nxtoptarg ) {
2339 case '0' : c20 = 1 ; break ;
2341 case '1' : c1 = 1 ; break ;
2342 #endif /* UCLINUX */
2343 case '2' : c2 = 1 ; break ;
2344 case 'a' : softaa = 1 ; break ;
2345 case 'e' : ignerr = 1 ; break ;
2346 case 'f' : vfc = 1 ; break ;
2347 case 'h' : hwfc = 1 ; break ;
2348 case 'l' : lockpolldelay /= 2 ; break ;
2349 case 'n' : noretry = 1 ; break ;
2350 case 'r' : reverse = 0 ; break ;
2351 case 'x' : startchar = XON ; break ;
2352 case 'z' : cmdpause += T_CMD ; break ;
2353 default : msg ( "Wunrecognized protocol option (%c)", *nxtoptarg ) ;
2357 maxpgerr = atoi( nxtoptarg );
2359 err=msg ("E2bad quality (-q) argument (%s)", nxtoptarg ) ;
2363 ansfname = nxtoptarg ;
2365 #endif /* UCLINUX */
2372 #endif /* UCLINUX */
2375 if ( argv [ argc ] ) err = msg ("E2can't happen(unterminated argv)") ;
2376 if ( ! err ) err = newIFILE ( &ifile, argv + nxtoptind ) ;
2377 pages = argc - nxtoptind - ( c == 'p' ? 1 : 0 ) ;
2378 pages = pages < 0 ? 0 : pages ;
2383 verb[nverb] = nxtoptarg ;
2390 if ( nlkfile < MAXLKFILE ) lkfile[ nlkfile++ ] = nxtoptarg ;
2391 else err = msg ( "E2too many lock files" ) ;
2393 case 'T': /* test: begin+end session */
2397 default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
2401 for ( i=0 ; i<argc ; i++ )
2402 msg ( "Aargv[%d]=%s", i, argv[i]) ;
2404 if ( ! nicmd[2] ) icmd[2][nicmd[2]++] = "H" ; /* default -k command */
2407 readfont ( fontname, &font ) ;
2410 char tmp [ MAXLINELEN ] ;
2412 strftime ( tmp, MAXLINELEN, "%c %%s P. %%%%d", localtime ( &now ) ) ;
2413 sprintf ( header = headerbuf, tmp, localid ) ;
2415 #endif /* UCLINUX */
2418 err = begin_session ( &faxdev, faxfile,
2419 !c1 && !c20 && reverse, /* Class 2 rx bit reversal */
2420 hwfc, lkfile, COMMAND, onsig ) ;
2421 if ( ! err ) err = setup ( &faxdev, icmd[0], ignerr ) ;
2422 if ( ! err ) err = modem_init ( &faxdev, local, localid,
2423 calling, calling && !pages, capsset,
2425 if ( ! err ) err = setup ( &faxdev, icmd[1], ignerr ) ;
2426 if ( err == 1 ) locked = 1 ;
2429 if ( ! err && ! testing ) {
2432 err = dial ( &faxdev, phnum, 0 ) ;
2436 err = answer ( &faxdev, lkfile, wait, share, softaa,
2437 getty, vcmd, acmd ) ;
2438 if ( err == 1 ) locked = 1 ;
2440 #endif /* UCLINUX */
2442 now = time(0) ; /* do it here so use reception time */
2444 strftime ( fnamepat, EFAX_PATH_MAX, ansfname, localtime ( &now ) ) ;
2445 strncat ( fnamepat, ".%03d", EFAX_PATH_MAX - strlen ( fnamepat ) ) ;
2446 newOFILE ( &ofile, O_TIFF_FAX, fnamepat, 0, 0, 0, 0 ) ;
2447 #endif /* UCLINUX */
2452 err = c1sndrcv ( &faxdev, local, localid,
2453 &ofile, &ifile, pages, header, &font,
2454 maxpgerr, noretry, calling ) ;
2456 #endif /* UCLINUX */
2458 err = c2sndrcv ( &faxdev, local, localid,
2459 &ofile, &ifile, pages,
2462 #endif /* UCLINUX */
2463 maxpgerr, noretry, calling ) ;
2468 return cleanup ( err ) ;