📄 efax.c
字号:
3 if no compatible settings possible. */int mincap ( cap local, cap remote, cap session ){ int err=0, i ; int msttab[2][8] = { { 0,1,3,3,5,5,7,7 } , { 0,1,1,3,3,5,5,7 } } ; printcap ( "local ", local ) ; printcap ( "remote ", remote ) ; checkcap ( session ) ; for ( i=0 ; i<NCAP && i!=ST ; i++ ) session[i] = remote[i] < local[i] ? remote[i] : local[i] ; session[ST] = msttab [ session[VR] ] [ remote[ST] ] ; printcap ( "session", session ) ; if ( local[WD] != session[WD] || local[LN] != session[LN] || local[DF] != session[DF] ) err = msg ("W3incompatible local and remote capabilities" ) ; return err ;}/* Scan responses since giving previous command (by cmd()) for a match to string 's' at start of response. If a matching response is found and ip is not null, reads one integer into ip. Returns pointer to start of data field of response string or NULL if not found. */char responses [ MAXRESPB ], *lresponse = 0 ;char *sresponse ( const char *s, int *ip ){ char *p, *r = 0 ; int lens ; lens = strlen ( s ) ; for ( p=responses ; p<lresponse ; p += strlen(p) + 1 ) if ( ! strncmp ( p, s, lens ) ) { r = p + lens ; if ( ip ) sscanf ( r, "%d", ip ) ; } return r ;}/* Search for a match to the string s in a null-terminated array of possible prefix strings pointed to by p. The first character of each prefix string is skipped. Returns pointer to the table entry or NULL if not found. */char *strtabmatch ( char **p, char *s ){ while ( *p && strncmp ( *p+1 , s , strlen ( *p+1 ) ) ) p++ ; return ( ! *p || **p == '-' ) ? NULL : *p ;}/* Send command to modem and check responses. Collects pending (unexpected) responses and then pauses for inter-command delay (cmdpause) if t is negative. Writes command s to modem if s is not null. Reads responses and terminates when a response is one of the prompts in responses[] or if times out in t deciseconds. Repeats command if detects a RING response (probable collision). Returns the first character of the matching prefix string (e.g. 'O' for OK, 'C' for CONNECT, etc.) or EOF if no such response was received within timeout t. */int cmd ( MODEM *m, const char *s , int t ){ char buf [ CMDBUFSIZE ] , *p = "" ; int resplen=0 ; lresponse = responses ; retry: while ( s && tgets ( m, buf, CMDBUFSIZE, t<0 ? m->cmdpause : 0 ) ) msg ( "W- unexpected response \"%s\"", buf ) ; msg ( s ? "C- command \"%s\"" : "C- waiting" , s ) ; if ( s ) { tputs ( m, "AT", t ) ; tputs ( m, s, t ) ; tputcnow ( m, CR, t ) ; } while ( t && ( p = tgets ( m, buf, CMDBUFSIZE, t<0 ? -t : t ) ) ) { msg ( "C- response \"%s\"" , p ) ; if ( ( resplen += strlen ( buf ) + 1 ) <= MAXRESPB ) { strcpy ( lresponse, buf ) ; lresponse += strlen ( buf ) + 1 ; } if ( ( p = strtabmatch ( (char**) prompts, buf ) ) ) break ; if ( ! strcmp ( buf, "RING" ) ) { msleep(100) ; goto retry ; } } return p ? *p : EOF ;}/* Send command to modem and wait for reply after testing (and possibly setting) current error status via err pointer. */void ckcmd ( MODEM *m, int *err, const char *s, int t, int r ){ int c ; if ( ( ! err || ! *err ) && ( c = cmd ( m, s, t ) ) != r ) { msg ( c == EOF ? "E3modem command (%s) timed out" : "E3wrong response to command (%s)", s ? s : "none" ) ; if ( err ) *err = 3 ; }}/* Resynchronize modem from an unknown state. If no immediate response, try pulsing DTR low (needs &D{2,3,4}), and then cancelling data or fax data modes. In each case, discards any responses for about 2 seconds and then tries command ATQ0V1 to enable text responses. Returns 0 if OK or 4 if no response. */int faxsync( MODEM *m ){ int err=0, method=0 ; while ( ! err ) { switch ( method++ ) { case 0 : break ; case 1 : msg ("Isync: dropping DTR") ; ttymode ( m->f, COMMAND ) ; msleep ( 200 ) ; ttymode ( m->f, DROPDTR ) ; msleep ( 200 ) ; ttymode ( m->f, COMMAND ) ; break ; case 2 : msg ("Isync: sending escape") ; tputs ( m, DLE_ETX, -1 ) ; msleep ( 1500 ) ; tputs ( m, "+++", -1 ) ; break ; case 3 : err = msg ("E4sync: modem not responding") ; continue ; } while ( cmd ( m, 0, method ? TO_RESET : 5 ) != EOF ) ; if ( cmd ( m, "Q0V1", -TO_RESET ) == OK ) break ; } return err ;} /* Terminate session. Makes sure modem is responding, sends modem reset commands, removes lock files. Returns 0 if OK, 3 on error.*/int end_session ( MODEM *m, char **zcmd, char **lkfile ){ int err = faxsync ( m ) ; for ( ; ! err && *zcmd ; zcmd++ ) if ( cmd( m, *zcmd, -TO_RESET ) != OK ) err = msg ("E3modem reset command (%s) failed", *zcmd ) ; unlockall ( lkfile ) ; return err ;} /* Signal handler: end session and exit */MODEM *sigm ; char **sigcmd, **siglkfile ;void onsig ( int sig ) { msg ( "Eterminating on signal %d", sig ) ; end_session ( sigm, sigcmd, siglkfile ) ; msg ( "Idone, returning 5" ) ; exit ( 5 ) ; } /* Initialize session. Try locking and opening fax device until opened or get error. Then set tty modes, register signal handler, set up modem. Returns 0 if OK, 2 on errors, 3 if initialization failed, 4 if no modem response. */int begin_session ( MODEM *m, char *fname, int reverse, char **lkfile, char **icmd, int ignerr ){ int i, err=0, busy=0 ; do { err = lockall ( lkfile ) ; if ( ! err ) err = ttyopen ( m->f, fname, reverse ) ; if ( err == 1 ) { if ( busy++ < 1 ) msg ( "Wfax device %s locked or busy. waiting...", fname ) ; msleep ( LOCKPOLLDELAY * 10 ) ; } } while ( err == 1 ) ; if ( ! err ) msg ( "Iopened %s", fname ) ; if ( ! err ) err = ttymode ( m->f, COMMAND ) ; for ( i=0 ; ! err && catch [ i ] ; i++ ) if ( signal ( catch [ i ] , onsig ) == SIG_ERR ) err = msg ( "ES2can't set signal %d handler:", catch [ i ] ) ; if ( !err ) err = faxsync ( m ) ; for ( ; ! err && *icmd ; icmd++ ) if ( cmd ( m, *icmd, -TO_RESET ) != OK && ! ignerr ) err = msg ( "E3modem initialization command (%s) failed", *icmd ) ; return err ;}/* Position input file to start of next page if dp is non-zero or at start of current page if dp is 0. Sets ppm to MPS if there are more pages to be sent with the same format, EOM if there are more pages with a new format, EOP if no more pages. Currently assumes all files are of same format and sets ppm to MPS or EOP only but eventually will handle multi-page files with embedded format information (e.g. TIFF-F) by setting local[VR/WD/LN] and setting ppm to EOM if required. Returns 0 if OK, 2 on errors. */int rdpage ( IFILE *f, int dp, int *ppm, cap local ){ int err=0 ; err = nextipage ( f, dp ) ? 0 : 2 ; *ppm = ! err && f->fname [ 1 ] ? MPS : EOP ; return err ;}/* Terminate previous page if page is non-zero and start next output page if page is non-negative. If page is -1 removes the most recently opened file. Returns 0 if OK, 2 on errors. */int wrpage ( OFILE *f, int page ){ int err=0 ; err = nextopage ( f, page ) ; if ( ! err && page == -1 ) if ( remove ( f->cfname ) ) err = msg ( "ES2can't delete file\n%s:", f->cfname ) ; else msg ( "Iremoved %s", f->cfname ) ; return err ;}/* Send data for one page. Figures out required padding and 198->96 lpi decimation based on local and session capabilitites, substitutes page numbers in header string and enables serial port flow control. Inserts the page header before the input file data. Converts each scan line to T.4 codes and adds padding (FILL) and EOL codes before writing out. Sends RTC when done. Sends DLE-ETX and returns serial port to command mode when done. Returns 0 if OK, non-0 on errors. */int send_data ( MODEM *m, IFILE *f, int page, int pages, cap local, cap session, char *header, pbmfont *font ){ int done=0, err=0, noise=0, bytes=0, pad=0, nr=0, line, pixels ; int i, decimate, pwidth, minlen ; u_char buf [ MAXCODES + 2*EOLBITS/8 + 1 ], *p ; short runs [ MAXRUNS ] ; char headerbuf [ MAXLINELEN ] ; time_t start ; long dt, draintime ; ENCODER e ; newENCODER ( &e ) ; minlen = ( (long) cps[session[BR]] * delay[session[ST]] + 500 ) / 1000 ; decimate = local[VR] > session[VR] ; pwidth = pagewidth [ session [ WD ] ] ; msg ( "N- padding to %d bytes/scan line.%s", minlen, decimate ? " reducing 198->96 lpi." : "" ) ; sprintf ( headerbuf, header, page+1, pages, page+1, pages, page+1, pages ) ; msg ("I- header:[%s]", headerbuf ) ; p = putcode ( &e, EOLCODE, EOLBITS, buf ) ; done = err = ttymode ( m->f, SEND ) ; start = time(0) ; for ( line=0 ; ! done ; line++ ) { if ( line >= HDRSPCE ) { /* read a line */ for ( i=0 ; ! done && i <= decimate ; i++ ) if ( ( nr = readline ( f, runs, &pixels ) ) < 0 ) done = 1 ; } else { /* blank line */ runs[0] = pwidth ; pixels = pwidth ; nr = 1 ; } if ( nr > 0 && line >= HDRSTRT && line < HDRSTRT + font->h ) { int hnr ; short hruns [ MAXRUNS ] ; hnr = texttorun ( (u_char*) headerbuf, font, line-HDRSTRT, hruns, 0 ) ; xshift ( hruns, hnr, HDRSHFT ) ; nr = runor ( runs, nr, hruns, hnr, 0, &pixels ) ; } if ( nr > 0 ) { if ( pixels ) { if ( pixels != pwidth ) nr = xpad ( runs, nr, pwidth - pixels ) ; p = runtocode ( &e, runs, nr, p ) ; while ( p - buf < minlen ) p = putcode ( &e, 0, 8, p ), pad ++ ; p = putcode ( &e, EOLCODE, EOLBITS, p ) ; sendbuf ( m, buf, p - buf, -1 ), bytes += p - buf ; } if ( tdata ( m->f, 0 ) ) noise = 1 ; p = buf ; } } for ( i=0 ; i < RTCEOL ; i++ ) p = putcode ( &e, EOLCODE, EOLBITS, p ) ; p = putcode ( &e, 0, 0, p ) ; sendbuf ( m, buf, p - buf, -1 ), bytes += p - buf ; if ( noise ) msg ("W- characters received while sending" ) ; tputs ( m, DLE_ETX, -1 ) ; /* and tflush() */ dt = time(0) - start ; /* time to drain buffers + 100% + 4s */ draintime = ( 2 * ( bytes / cps[ session[BR] ] + 1 - dt ) + 4 ) * 10 ; draintime = draintime < TO_DRAIN ? TO_DRAIN : draintime ; ckcmd ( m, 0, 0, (int) draintime, OK ) ; dt = time(0) - start ; msg ( "Isent %d lines, %d+%d bytes, %d s %d bps" , line, bytes-pad, pad, (int) dt, (bytes*8)/dt ) ; if ( ! err ) err = ttymode ( m->f, COMMAND ) ; return err ;}/* Read one scan line from fax device. If pointer pels is not null it is used to save pixel count. Returns number of runs stored, EOF on RTC, or -2 on EOF, DLE-ETX or other error. */int readfaxruns ( MODEM *m, DECODER *d, short *runs, int *pels ){ int err=0, c=EOF, x, n ; dtab *tab, *t ; short shift ; short *p, *maxp, *q, len=0 ; maxp = ( p = runs ) + MAXRUNS ; x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */ do { do { while ( shift < 0 ) { c = tgetd ( m->f, TO_CHAR ) ; if ( c < 0 ) { x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */ } else { x = ( x << 8 ) | c ; shift += 8 ; } } t = tab + ( ( x >> shift ) & 0x1ff ) ; tab = t->next ; shift -= t->bits ; } while ( ! t->code ) ; if ( p < maxp ) *p++ = t->code ; } while ( t->code != -1 ) ; d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */ if ( p >= maxp ) msg ( "Wrun length buffer overflow" ) ; /* combine make-up and terminating codes and remove +1 offset in run lengths */ n = p - runs - 1 ; for ( p = q = runs ; n-- > 0 ; ) if ( *p > 64 && n-- > 0 ) { len += *q++ = p[0] + p[1] - 2 ; p+=2 ; } else { len += *q++ = *p++ - 1 ; } n = q - runs ; /* check for RTC and errors */ if ( len ) d->eolcnt = 0 ; else if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ; if ( c < 0 ) err = - 2 ; if ( pels ) *pels = len ; return err ? err : n ;}/* Receive data. Reads scan lines from modem and writes to output file. Checks for errors by comparing received line width and session line width. Check that the output file is still OK and if not, send one CANcel character and wait for protocol to complete. */int receive_data ( MODEM *m, OFILE *f, cap session, int *nerr ){ int err=0, line, nr=0, len ; int pwidth = pagewidth [ session [ WD ] ] ; short runs [ MAXRUNS ] ; DECODER d ; newDECODER ( &d ) ; *nerr = 0 ; for ( line=0 ; nr >= 0 ; line++ ) { nr = readfaxruns ( m, &d, runs, &len ) ; if ( nr >= 0 ) writeline ( f, runs, nr, 1 ) ; if ( ( len != pwidth ) && len && line ) { (*nerr)++ ; if ( *nerr <= MAXERRPRT ) msg ("R-+ (%d:%d)", line, len ) ; } if ( ferror ( f->f ) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -