www.pudn.com > commutil.zip > FAX.CPP


// ******************************************************************** // 
//                                                                      // 
//      FAX.CPP                                                         // 
//      Copyright (c) 1993, Michael Holmes and Bob Flanders             // 
//      C++ Communication Utilities                                     // 
//                                                                      // 
//      Chapter 7: Facsimile Reception and Transmission                 // 
//      Last changed in chapter 7                                       // 
//                                                                      // 
//      This file contains the functions to implement the Fax class.    // 
//      This class contains all of the support for receiving and        // 
//      transmitting facsimilies.                                       // 
//                                                                      // 
// ******************************************************************** // 
 
#define FAX_CLASS1  "AT +FCLASS=1\r"        // enter FAX class 1 mode 
 
#define FAX_TX_HDLC "AT +FTH=3\r"           // enter HDLC transmit mode 
#define FAX_RX_HDLC "AT +FRH=3\r"           // enter HDLC recieve mode 
 
#define FAX_TX_DATA "AT +FTM=%d\r"          // enter FAX transmit mode 
#define FAX_RX_DATA "AT +FRM=%d\r"          // enter FAX receive mode 
 
#define FAX_TX_SPD  "AT +FTM=?\r"           // get FAX transmit speeds 
#define FAX_RX_SPD  "AT +FRM=?\r"           // get FAX receive speeds 
 
#define FAX_SILENT  "AT +FRS=8\r"           // 80 ms of silence 
#define FAX_SILENT1 "AT +FRS=20\r"          // 200 ms of silence 
 
#define FAX_CLASS1  "AT +FCLASS=1\r"        // enter FAX class 1 mode 
#define FAX_MODEM   "AT +FCLASS=0\r"        // return to non-FAX mode 
 
#define FAX_ANSWER  "AT A\r"                // answer an incoming call 
#define FAX_HANGUP  "AT H\r"                // disconnect from line 
#define FAX_DIAL    "AT DT"                 // dial command 
 
#define FAX_OK      "\r\nOK\r\n"            // OK response 
#define FAX_ERR     "\r\nERROR\r\n"         // ERROR response 
#define FAX_NO_CARR "\r\nNO CARRIER\r\n"    // NO CARRIER response 
#define FAX_CONN    "\r\nCONNECT\r\n"       // CONNECT response 
#define FAX_RING    "\r\nRING\r\n"          // RING message 
 
#define FAX_SETMDM  "AT Q0 V1 E0\r"         // set modem parameters 
#define FAX_RSTMDM  "AT Z\r"                // reset modem 
 
#define FAX_ADDR    0xff                    // value for address byte 
#define FAX_CTL     0xC0                    // value for control field 
#define FAX_CTL_FF  0xC8                    // control field + final frame 
 
#define FAX_FCF_DIS 0x01                    // digital ID signal 
#define FAX_FCF_CSI 0x02                    // called subscriber ID 
#define FAX_FCF_NSF 0x04                    // non-standard facilities 
#define FAX_FCF_DCS 0xC1                    // digital command signal 
#define FAX_FCF_TSI 0xC2                    // transmitting subscriber ID 
#define FAX_FCF_CFR 0x21                    // confirmation to receive 
#define FAX_FCF_FTT 0x22                    // failure to train 
#define FAX_FCF_EOM 0xF1                    // end of message 
#define FAX_FCF_MPS 0xF2                    // multipage signal 
#define FAX_FCF_EOP 0xF4                    // end of procedure 
#define FAX_FCF_MCF 0x31                    // message confirmation 
#define FAX_FCF_DCN 0xDF                    // disconnect 
 
#define FINAL       8                       // final frame 
#define NON_FINAL   0                       // non-final frame 
 
#define TRANSMIT    0                       // connect in send mode 
#define RECEIVE     1                       // connect in receive mode 
 
#define FAX_T9600   0x80                    // 9600 transmit 
#define FAX_T7200   0x40                    // 7200 transmit 
#define FAX_T4800   0x20                    // 4800 transmit 
#define FAX_T2400   0x10                    // 2400 transmit 
 
#define FAX_R9600   0x08                    // 9600 receive 
#define FAX_R7200   0x04                    // 7200 receive 
#define FAX_R4800   0x02                    // 4800 receive 
#define FAX_R2400   0x01                    // 2400 receive 
 
/* ******************************************************************** * 
 * 
 *  Fax specific variables, structs, etc. 
 * 
 * ******************************************************************** */ 
 
char    dis_msg[] =                         // DIS frame data 
          { 0,                              // first byte: 
                                            //   not G1, G2 
          0x70,                             // second byte: 
                                            //   T.4 operation 
                                            //   9600/7200/4800/2400 bps 
                                            //   3.85 lines/mm 
                                            //   one-dimensional coding 
          0x02                              // third byte: 
          };                                //   1728 pels/215mm 
                                            //   A4 paper only 
                                            //   40ms receive time 
 
char    dcs_msg[] =                         // DCS frame data 
          { 0,                              // first byte: 
                                            //   not G1, G2 
          0x40,                             // second byte: 
                                            //   T.4 operation 
                                            //   speed to be set 
                                            //   3.85 lines/mm 
                                            //   one-dimensional coding 
          0x02                              // third byte: 
          };                                //   1728 pels/215mm 
                                            //   A4 paper only 
                                            //   40ms per line 
 
struct HDLC_msg                             // format of HDLC message 
    { 
    UCHAR   addr;                           // address byte (always 0xff) 
    UCHAR   ctl_fld;                        // control field 
    UCHAR   fax_ctl_fld;                    // FAX control field (FCF) 
    UCHAR   data[253];                      // optional data 
    int     len;                            // received frame length 
    }; 
 
struct FxStat                               // Fax status structure 
    { 
    void   *f_parm;                         // Fax init parameter 
    char   f_msg[50];                       // message string 
    char   *f_ptr;                          // pointer to message 
    }; 
 
struct FxHdr                                // Fax file header structure 
    { 
    char   ff_type[3];                      // file type (G3) 
    char   ff_dcs[16];                      // DCS for transfer 
    char   ff_id[21];                       // original station ID 
    char   ff_reserved[88];                 // reserved space 
    }; 
 
 
 
/* ******************************************************************** * 
 * 
 *  Fax class definition 
 * 
 * ******************************************************************** */ 
 
class Fax : Protocol 
    { 
    public: 
        Fax     (Comm *,                    // Fax instance contstructor 
                 char *,                    //   Comm instance, station ID 
                 int (*)(int,               //   status routine 
                         struct FxStat *), 
                 void *);                   //   FxStat parameter 
 
                 ~Fax(void);                // destructor 
 
        void     Send(char *, char *),      // send a fax 
                 Receive(char *);           // receive a fax 
 
    private: 
        Comm    *cp;                        // Comm instance 
        int     Send_CSI(void),             // send optional CSI 
                HDLC_Mode(int),             // set HDLC tx|rx mode 
                Data_Mode(int, int),        // set data tx|rx mode 
                Get_Speeds(void),           // get tx and rx speeds 
                Get_Line(char *, int),      // get a CR-terminate line 
                Get_Char(char *, int),      // get a character 
                Rcv_Hmsg(void),             // receive an HDLC frame 
                Send_Our_ID(UCHAR),         // send our ID to remote 
                Send_Hmsg(UCHAR,UCHAR,int), // send an HDLC frame 
                Send_TSI(void),             // send TSI frame to remote 
                Send_DIS(void),             // send DIS frame to remote 
                Send_DCS(void),             // send DCS frame to remote 
                Init_Modem(void),           // initialize modem 
                (*stat)(int,                // status routine pointer 
                        struct FxStat *), 
                connected;                  // HDLC link active 
        UINT    oldparms,                   // old communications parameters 
                pbi;                        // page buffer index 
        long    oldspeed;                   // old commuincations speed 
        void    Reverse_Bytes(UCHAR *, int),// reverse bits in char array 
                Display_ID(char *, UCHAR *),// display remote station ID 
                Display_Msg(int),           // display a fax_msgs message 
                Reset_Modem(void);          // reset modem and comm parms 
        UCHAR   speeds,                     // speeds supported 
                                            // 1... .... 9600 transmit 
                                            // .1.. .... 7200 
                                            // ..1. .... 4800 
                                            // ...1 .... 2400 
                                            // .... 1... 9600 receive 
                                            // .... .1.. 7200 
                                            // .... ..1. 4800 
                                            // .... ...1 2400 
               *pagebuf,                    // page buffer area 
                Reverse_Byte(UCHAR value);  // reverse bits in a byte 
        char   *station;                    // our station id 
        struct  FxStat fs;                  // fax status structure 
        struct  FxHdr  fh;                  // fax file header structure 
        union   {                           // accesss HDLC msg as chars 
                struct  HDLC_msg hmsg;      // HDLC message area 
                UCHAR   cmsg[256];          // .. same as char array 
                }; 
    }; 
 
 
/* ******************************************************************** 
 * 
 *  Fax -- Fax instance constuctor 
 * 
 * ********************************************************************/ 
 
Fax::Fax(Comm *ci,                          // comm instance 
         char *sid,                         // .. our station ID (telno) 
         int (*sr)(int, struct FxStat *),   // .. status routine 
         void *w)                           // .. fax status parameter 
 
{ 
 
cp = ci;                                    // save comm instance pointer 
stat = sr;                                  // .. and status routine 
station = sid;                              // .. and station ID 
 
connected = 0;                              // no HDLC connection active 
 
fs.f_parm = w;                              // save fax status parameter 
 
strcpy(fh.ff_type, "G3");                   // preset the file type 
memset(fh.ff_reserved, ' ',                 // .. and clear reserved area 
            sizeof(fh.ff_reserved)); 
 
pagebuf = new UCHAR[1024];                  // allocate page buffer 
 
} 
 
 
 
/* ******************************************************************** 
 * 
 *  ~Fax -- destructor 
 * 
 * ********************************************************************/ 
 
Fax::~Fax(void)                             // Fax destructor 
 
{ 
delete pagebuf;                             // free pagebuf memory 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Init_Modem -- initialize the fax modem 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; OK response from modem 
 *            1 = ERROR response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::Init_Modem(void) 
 
{ 
int     rc,                                 // return code 
        i;                                  // work counter for loops 
char    buf[80],                            // work buffer for speeds 
       *c,                                  // work pointer 
       *t;                                  // token work pointer 
 
 
cp->Write("\r");                            // send a  to modem 
Purge(cp, 2);                               // .. kill any receive messages 
 
oldspeed = cp->GetSpeed();                  // get the old link speed 
oldparms = cp->Set8n();                     // .. and old comm parameters 
                                            // .. while setting 8,n,1 
 
cp->SetBPS(19200L);                         // set new comm speed 
cp->SetBPS(9600L);     // set new comm speed 
 
for (rc = i = 0; (rc == 0) && i++ < 3;)     // try three times 
    { 
    cp->Write(FAX_SETMDM);                  // set the modem parameters 
 
    rc = wait_for(FAX_OK, FAX_ERR, 2);      // wait for OK response 
    } 
 
if (rc < 0)                                 // q. user cancellation? 
    return(rc - 1);                         // a. yes .. return error 
 
speeds = 0;                                 // reset speeds supported 
 
cp->Write(FAX_CLASS1);                      // place modem in CLASS 1 mode 
 
rc = wait_for(FAX_OK, FAX_ERR, 5) - 1;      // wait for modem response 
 
if (rc)                                     // q. modem respond ok? 
   return(rc);                              // a. no .. tell the caller 
 
cp->Write(FAX_TX_SPD);                      // retrieve transmit speeds 
 
rc = Get_Line(buf, 5);                      // kill the first CR 
rc = Get_Line(buf, 5);                      // get the line from the mode 
 
if (rc)                                     // q. any error? 
   return(rc);                              // a. yes.. return w/error 
 
Purge(cp, 1);                               // kill additional characters 
 
c = (*buf == '\n') ? buf+1 : buf;           // select start point 
 
while ((t = strtok(c, ",")) != 0)           // while there are tokens 
    { 
    c = NULL;                               // continue searching buf 
 
    i = atoi(t);                            // get the token's value 
 
    switch(i)                               // for various values.. 
        { 
        case 24:                            // 2400 found 
             speeds |= FAX_T2400;           // .. set set the speed flag 
             break; 
 
        case 48:                            // 4800 found 
             speeds |= FAX_T4800;           // .. set set the speed flag 
             break; 
 
        case 72:                            // 7200 found 
             speeds |= FAX_T7200;           // .. set set the speed flag 
             break; 
 
        case 96:                            // 9600 found 
             speeds |= FAX_T9600;           // .. set set the speed flag 
             break; 
        } 
    } 
 
cp->Write(FAX_RX_SPD);                      // retrieve receive speeds 
 
rc = Get_Line(buf, 5);                      // kill the first CR 
rc = Get_Line(buf, 5);                      // get the line from the modem 
 
if (rc)                                     // q. any error? 
   return(rc);                              // a. yes.. return w/error 
 
Purge(cp, 1);                               // kill additional characters 
 
c = (*buf == '\n') ? buf+1 : buf;           // select start point 
 
while ((t = strtok(c, ",")) != 0)           // while there are tokens 
    { 
    c = NULL;                               // continue searching buf 
 
    i = atoi(t);                            // get the token's value 
 
    switch(i)                               // for various values.. 
        { 
        case 24:                            // 2400 found 
             speeds |= FAX_R2400;           // .. set set the speed flag 
             break; 
 
        case 48:                            // 4800 found 
             speeds |= FAX_R4800;           // .. set set the speed flag 
             break; 
 
        case 72:                            // 7200 found 
             speeds |= FAX_R7200;           // .. set set the speed flag 
             break; 
 
        case 96:                            // 9600 found 
             speeds |= FAX_R9600;           // .. set set the speed flag 
             break; 
        } 
    } 
 
return(0);                                  // return ok 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Reset_Modem -- reset modem and communications parameters 
 * 
 * ******************************************************************** */ 
 
void Fax::Reset_Modem(void) 
 
{ 
int     rc;                                 // return code 
 
cp->Write("\r");                            // send a  to modem 
cp->DTR();                                  // lower DTR 
Purge(cp, 1);                               // .. kill any receive messages 
 
cp->SetBPS(oldspeed);                       // reset the comm speed 
cp->SetLine(oldparms);                      // .. and the comm parms 
 
cp->Write(FAX_RSTMDM);                      // try to reset the modem 
 
switch (rc = wait_for(FAX_OK, FAX_ERR, 2))  // based on response 
    { 
    case -1:                                // user pressed escape 
        return;                             // .. return without message 
 
    case 0:                                 // time out 
    case 1:                                 // received OK 
    case 2:                                 // received ERROR 
        Display_Msg(37+rc);                 // display appropriate message 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_Our_ID -- send our ID to the other station 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; CONNECT response from modem 
 *            1 = NO CARRIER response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_Our_ID(UCHAR ctl_byte)        // FAX control field 
 
{ 
int     i, j;                               // work variables 
UCHAR  *ch;                                 // work pointer 
 
ch = hmsg.data;                             // get the data address 
 
for (i = 0; i < 20; ch[i++] = 0x04);        // set to 'reversed' blanks 
 
if (station != NULL)                        // q. station ID set? 
    {                                       // a. yes .. 
    i = strlen(station);                    // get station ID length 
 
    i = i > 20 ? 20 : i;                    // max ID length = 20 
 
    for (j = 0; i--;)                       // for each char in station ID 
        { 
        if (strchr("0123456789+ ",          // q. valid ID character? 
                       station[i]) != 0) 
            ch[j++] =                       // a. yes.. reverse & copy it 
                   Reverse_Byte(station[i]); 
        } 
    } 
 
return(Send_Hmsg(NON_FINAL, ctl_byte, 20)); // send the HDLC message 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  Display_ID -- display remote station ID 
 * 
 * ******************************************************************** */ 
 
void Fax::Display_ID(char *pf,              // prefix 
                    UCHAR *id)              // id string 
 
{ 
char    cw[21];                             // work area 
 
int     i, j;                               // work variables 
 
for (i = 20; i--;)                          // backscan the ID 
    if (id[i] != ' ')                       // q. blank? 
        break;                              // a. no .. get out now 
 
if (i++ == -1)                              // q. ID found? 
   return;                                  // a. no .. leave now 
 
 
for (j = 0; i--; cw[j++] = id[i]);          // copy the ID in reverse 
cw[j] = 0;                                  // .. and end the string 
 
sprintf(fs.f_msg, "%s %s\r\n", pf, cw);     // put message in buffer 
(stat)(1, &fs);                             // .. and display it 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  Rcv_Hmsg -- receive an HDLC frame 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successfully received 
 *            1 = NO CARRIER or error 
 * 
 * ******************************************************************** */ 
 
int Fax::Rcv_Hmsg(void) 
 
{ 
int     rc,                                 // return code 
        loop = 1,                           // loop until finished 
        dleflag = FALSE;                    // dle not seen yet 
 
char    *nxtchar,                           // next receive address 
        wc;                                 // work for char reads 
 
long    timer;                              // workspace for timer 
 
hmsg.len = 0;                               // initialize length 
nxtchar = (char *) cmsg;                    // .. and next receive pointer 
 
if ((rc = HDLC_Mode(RECEIVE)) != 0)         // q. connect ok? 
   return(rc);                              // a. no .. leave w/error 
 
timer = SECS(5);                            // start 5 second timer 
 
while(loop)                                 // enter a loop .. 
    switch(cp->Read(nxtchar, &wc, &wc))     // attempting to read bytes 
        { 
        case -1:                            // no character available 
            if (TimeOut(&timer))            // q. timeout? 
                return(-1);                 // a. yes.. tell the caller 
 
            if ((stat)(0, &fs))             // q. user press escape? 
                return(-2);                 // a. yes.. tell the caller 
 
            continue;                       // else .. continue loop 
 
        case 0:                             // character received 
            hmsg.len++;                     // increment received count 
 
            if (dleflag)                    // q. previous char DLE? 
               {                            // a. yes .. 
               dleflag = FALSE;             // .. reset the flag 
 
               if (*nxtchar == ETX)         // q. DLE ETX sequence? 
                   loop = 0;                // a. yes .. exit the loop 
               } 
 
            else if (*nxtchar == DLE)       // q. char a DLE? 
               dleflag = TRUE;              // a. yes .. show true 
 
            nxtchar++;                      // point at next character 
            break;                          // end this case 
 
        default:                            // lost characters 
            return(1);                      // .. show receive unsuccessful 
        } 
 
rc = wait_for(FAX_OK, FAX_ERR, 5) - 1;      // OK should follow message 
 
Reverse_Bytes(cmsg, hmsg.len - 2);          // reverse the bits 
 
return(rc);                                 // return to caller 
 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_Hmsg -- send an HDLC frame 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; CONNECT response from modem 
 *            1 = NO CARRIER response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_Hmsg(UCHAR finalflg,          // final flag 
                   UCHAR ctl_byte,          // fax control field 
                   int   len)               // length of data bytes 
 
{ 
int     rc;                                 // return code 
 
hmsg.addr        = FAX_ADDR;                // set the address field 
hmsg.ctl_fld     = FAX_CTL | finalflg;      // .. and the control field 
hmsg.fax_ctl_fld = ctl_byte;                // .. and fax control field 
hmsg.data[len]   = DLE;                     // .. add DLE 
hmsg.data[len+1] = ETX;                     // .. and ETX 
 
Reverse_Bytes(cmsg, len+3);                 // reverse the bits 
 
if (NOT connected)                          // q. connected already? 
   {                                        // a. no .. connect now 
   rc = HDLC_Mode(TRANSMIT);                // attempt the connection 
 
   if (rc)                                  // q. connect ok? 
      return(rc);                           // a. no .. leave w/error 
 
   connected = TRUE;                        // else .. show connected. 
   } 
 
cp->Write((char *) cmsg, len+5);            // send to remote 
 
if (finalflg)                               // q. final flag on? 
   {                                        // a. yes .. 
   rc = wait_for(FAX_OK, FAX_ERR, 5) - 1;   // .. wait for OK 
   connected = FALSE;                       // .. and we're not connected 
   } 
 
 else 
   rc = wait_for(FAX_CONN,                  // else .. wait for CONNECT 
                 FAX_NO_CARR, 60) - 1;      // .. or NO CARRIER 
 
 
return(rc);                                 // return to caller 
 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_CSI -- Send the called subscriber ID signal to caller 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; CONNECT response from modem 
 *            1 = NO CARRIER response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_CSI(void) 
 
{ 
 
return(Send_Our_ID(FAX_FCF_CSI));           // send CSI frame to caller 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_TSI -- Send the transmitting subscriber ID signal 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful 
 *            1 = unsuccessful 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_TSI(void) 
 
{ 
 
return(Send_Our_ID(FAX_FCF_TSI));           // send TSI frame 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_DIS -- Send the digital ID signal to caller 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful 
 *            1 = unsuccesful 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_DIS(void) 
 
{ 
int     i;                                  // work variable 
 
for (i = 3; i--; hmsg.data[i] = dis_msg[i]);// copy the DIS to hmsg.data 
 
return(Send_Hmsg(FINAL, FAX_FCF_DIS, 3));   // send the DIS 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send_DCS -- Send the digital control signal to called station 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful 
 *            1 = unsuccesful 
 * 
 * ******************************************************************** */ 
 
int Fax::Send_DCS(void) 
 
{ 
int     i;                                  // work variable 
 
for (i = 3; i--; hmsg.data[i] = dcs_msg[i]);// copy the DCS to hmsg.data 
 
return(Send_Hmsg(FINAL, FAX_FCF_DCS, 3));   // send the DCS 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  HDLC_Mode -- Enter HDLC mode (transmit or receive) 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; CONNECT response from modem 
 *            1 = ERROR response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::HDLC_Mode(int dir)                 // setup for HDLC tx or rx 
                                            // 0 = transmit, 1 = receive 
{ 
int rc;                                     // return code 
 
if ((dir == TRANSMIT) && NOT connected)     // q. we transmitting? 
    {                                       // a. yes .. 
    cp->Write(FAX_SILENT);                  // .. request 80ms silence 
 
    rc = wait_for(FAX_OK,                   // wait for response 
                  FAX_ERR, 5) - 1; 
 
    if (rc)                                 // q. any problems? 
        return(rc);                         // a. yes .. return the error 
    } 
 
else if (connected)                         // q. connected already? 
    {                                       // a. yes .. 
    connected = FALSE;                      // .. reset connect status 
    return(0);                              // .. and return ok 
    } 
 
 
cp->Write(dir ? FAX_RX_HDLC : FAX_TX_HDLC); // start requested HDLC mode 
 
rc = wait_for(FAX_CONN, FAX_NO_CARR, 60)-1; // see if we connect 
 
return(rc);                                 // return to caller 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Data_Mode() -- Enter Fax Data mode (transmit or receive) 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful; CONNECT response from modem 
 *            1 = NO CARRIER response from modem 
 * 
 * ******************************************************************** */ 
 
int Fax::Data_Mode(int dir,                 // 0 = transmit, 1 = receive 
                   int speed)               // speed for transfer 
 
{ 
int rc;                                     // return code 
char    msg[20];                            // work area for message 
 
if ((dir == TRANSMIT) && NOT connected)     // q. we transmitting? 
    {                                       // a. yes .. 
    cp->Write(FAX_SILENT);                  // .. request 80ms silence 
 
    rc = wait_for(FAX_OK,                   // wait for response 
                  FAX_ERR, 5) - 1; 
 
    if (rc)                                 // q. any problems? 
        return(rc);                         // a. yes .. return the error 
    } 
 
sprintf(msg,                                // in message area .. 
        dir ? FAX_RX_DATA : FAX_TX_DATA,    // .. select direction of xfer 
        speed);                             // .. and build message 
 
 
cp->Write(msg);                             // write to modem 
 
rc = wait_for(FAX_CONN, FAX_NO_CARR, 60)-1; // see if we connect 
 
return(rc);                                 // return to caller 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Get_Line -- retrieve a CR-terminated line of information 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful 
 *            1 = data overrun 
 * 
 * ******************************************************************** */ 
 
int Fax::Get_Line(char *buf,                // buffer to contain info 
                  int  secs)                // length of timeout 
 
{ 
int     loop = TRUE;                        // loop condition 
char    wc;                                 // work for msr, lsr 
long    timer;                              // timer value 
 
timer = SECS(secs);                         // initialize timer 
 
while(loop)                                 // for as long as necessary .. 
    { 
    switch(cp->Read(buf, &wc, &wc))         // attempting to read a byte 
        { 
        case -1:                            // no character available 
            if (TimeOut(&timer))            // q. timeout? 
                return(-1);                 // a. yes.. tell the caller 
 
            if ((stat)(0, &fs))             // q. user press escape? 
                return(-2);                 // a. yes.. tell the caller 
 
            continue;                       // else .. continue loop 
 
        case 0:                             // character received 
            if (*buf == '\r')               // q. character a CR? 
               {                            // a. yes .. 
               *buf = 0;                    // .. null it out 
               loop = FALSE;                // .. and end the loop 
               } 
 
            else                            // else .. 
               buf++;                       // .. point to next char position 
 
            break;                          // end this case 
 
        default:                            // lost characters 
            return(1);                      // .. show receive unsuccessful 
        } 
    } 
 
return(0);                                  // show we finished ok 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Get_Char -- retrieve a single character 
 * 
 *  returns: -2 = User pressed ESC 
 *           -1 - Timeout 
 *            0 = successful 
 *            1 = data overrun 
 * 
 * ******************************************************************** */ 
 
int Fax::Get_Char(char *c,                  // character to retrieve 
                  int  secs)                // length of timeout 
 
{ 
int     loop = TRUE;                        // loop condition 
char    wc;                                 // work for msr, lsr 
long    timer;                              // timer value 
 
timer = SECS(secs);                         // initialize timer 
 
while(loop)                                 // for as long as necessary .. 
    { 
    switch(cp->Read(c, &wc, &wc))           // attempting to read a byte 
        { 
        case -1:                            // no character available 
            if (TimeOut(&timer))            // q. timeout? 
                return(-1);                 // a. yes.. tell the caller 
 
            if ((stat)(0, &fs))             // q. user press escape? 
                return(-2);                 // a. yes.. tell the caller 
 
            continue;                       // else .. continue loop 
 
        case 0:                             // character received 
            loop = FALSE;                   // .. end the loop 
            break; 
 
        default:                            // lost characters 
            return(1);                      // .. show receive unsuccessful 
        } 
    } 
 
return(0);                                  // show we finished ok 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Display_Msg -- display a message without parameters 
 * 
 * ******************************************************************** */ 
 
void Fax::Display_Msg(int msgno) 
 
{ 
 
fs.f_ptr = fax_msgs[msgno];                 // set the address 
(stat)(2, &fs);                             // display the message 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Reverse_Byte() -- Reverse the bits in a byte 
 * 
 * ******************************************************************** */ 
 
UCHAR Fax::Reverse_Byte(UCHAR value)        // byte to reverse 
 
{ 
 
__asm  mov cx, 8                            // cx = bits to shift 
__asm  mov al, value                        // al = starting value 
 
top_loop:                                   // top of reverse loop 
__asm  shl  ah, 1                           // shift ah up by one 
 
__asm  shr  al, 1                           // shift out a bit 
__asm  adc  ah, 0                           // .. add carry into ah 
 
__asm  loop top_loop                        // .. until all bits moved 
 
__asm  mov  value, ah                       // save reversed value 
 
return(value);                              // .. and return it 
 
} 
 
 
/* ******************************************************************** * 
 * 
 *  Reverse_Bytes -- reverse the bits in the bytes of a string 
 * 
 * ******************************************************************** */ 
 
void Fax::Reverse_Bytes(UCHAR *str,         // string to reverse 
                        int    len)         // length of string 
 
{ 
 
while(len--)                                // while there are bytes.. 
     { 
     *str = Reverse_Byte(*str);             // .. reverse the bits 
     str++;                                 // .. next byte 
     } 
 
} 
 
 
 
/* ******************************************************************** 
 * 
 *  Receive -- Receive a Facsimile transmission 
 * 
 * ******************************************************************** */ 
 
void Fax::Receive(char *faxname) 
 
{ 
int     rc = 0,                             // return code 
        speed,                              // link speed 
        fileopen = FALSE,                   // receive file not open 
        dcs_received = FALSE,               // DCS not yet received 
        i,                                  // work variable 
        error = 0,                          // error number 
        dleflag = FALSE,                    // DLE sequence found flag 
        loop = TRUE;                        // loop until complete 
 
FILE   *faxfile;                            // fax file 
 
char    c;                                  // work character 
 
ULONG   pagelen,                            // length of received page 
        reclen,                             // offset of page record length 
        et,                                 // elapsed time 
        st;                                 // work for timer 
 
enum    RcvStates                           // FAX receive states 
          { 
          PhaseA,                           // phase A - make connection 
          AwaitCall,                        // wait for a call 
          AnswerCall,                       // answer incoming call 
          SendCSI,                          // send our CSI frame, if needed 
          PhaseB,                           // negotiate session parameters 
          GetFrames,                        // get HDLC frames 
          SetSpeed,                         // set the link speed 
          PrepTCF,                          // prepare to receive TCF 
          FailConnTCF,                      // fail the TCF connect 
          RcvTCF,                           // receive/test TCF 
          RcvTCF1,                          // get rest of TCF 
          FailTCF,                          // fail the TCF 
          Confirm,                          // confirm training check frame 
          PhaseC,                           // receive the FAX data 
          RcvPage,                          // receive 1 page of data 
          EndPage,                          // page complete 
          PhaseD,                           // post message procedure 
          PhaseE,                           // call is done .. disconnect 
          RcvError,                         // error during receive 
          UserCan,                          // user cancelled transmission 
          ExitFax                           // exit FAX receive 
          } state; 
 
strcpy(fh.ff_id, "                    ");   // blank out the ID string 
state = PhaseA;                             // start in Phase A 
 
if ((faxfile = fopen(faxname, "wb+"))       // q. receive file open ok? 
                    == NULL) 
     {                                      // a. no .. 
     error = 9;                             // .. set the error code 
     state = RcvError;                      // .. and declare an error 
     } 
 
fileopen = TRUE;                            // show the file is open 
 
while(loop)                                 // top of state machine 
  switch(state)                             // perform next state 
    { 
    case PhaseA:                            // phase A - make connection 
         Display_Msg(1);                    // update the status 
         if ((rc = Init_Modem()) != 0)      // q. modem init ok? 
             { 
             error = 1;                     // a. no .. exit now 
             state = RcvError;              // .. declare the error 
             continue; 
             } 
 
         state = AwaitCall;                 // else .. wait for a ring 
 
         Display_Msg(2);                    // update the status 
         break;                             // end this state 
 
    case AwaitCall:                         // wait for a call 
         rc = wait_for(FAX_RING,            // wait for a RING from modem 
                       FAX_RING, 10); 
 
         if (rc > 0)                        // q. RING arrive? 
             {                              // a. yes .. 
             state = AnswerCall;            // .. go answer the call 
             continue; 
             } 
 
         if (rc == -1)                      // q. user press ESC? 
            state = UserCan;                // a. yes .. exit now 
 
         break;                             // else .. continue waiting 
 
    case AnswerCall:                        // answer incoming call 
         Display_Msg(3);                    // update the status 
         cp->Write(FAX_ANSWER);             // send the answer command 
 
         rc = wait_for(FAX_CONN,            // wait for a carrier 
                       FAX_NO_CARR, 60); 
 
         if (--rc == 0)                     // q. connect? 
             { 
             state = SendCSI;               // a. yes .. send our CSI 
             connected = TRUE;              // .. show we're connected 
             } 
 
         else                               // else .. 
             { 
             state = RcvError;              // .. process general error 
             error = 2;                     // .. show the error type 
             } 
 
         break;                             // .. next state 
 
    case SendCSI:                           // send our CSI frame 
         Display_Msg(4);                    // update the status 
         rc = Send_CSI();                   // .. send our ID 
 
         if (rc)                            // q. error? 
             state = RcvError;              // a. yes .. declare the error 
 
         else 
             state = PhaseB;                // select next state 
 
         break; 
 
    case PhaseB:                            // negotiate session parameters 
         Display_Msg(5);                    // 
         state = ((rc = Send_DIS()) != 0) ? // q. DIS send go ok? 
                                 RcvError : // a. no .. declare error 
                                 GetFrames; // else .. get frames 
         break; 
 
    case GetFrames:                         // get HDLC frames 
         if ((rc = Rcv_Hmsg()) != 0)        // q. any error getting a frame? 
             if (rc < 0)                    // a. yes .. timeout or ESC? 
                 {                          // a. yes .. 
                 error = 3;                 // .. error receiving HDLC frame 
                 state = RcvError;          // .. leave with error. 
                 continue;                  // .. continue process 
                 } 
//           else                           // else .. NO CARRIER 
//               { 
//               cp->Write(FAX_SILENT1);    // wait for 200ms silence 
// 
//               rc = wait_for(FAX_OK,      // wait for the OK response 
//                             FAX_ERR, 
//                             10) - 1; 
// 
//               state = SetSpeed;          // .. and set the speed 
//               } 
 
         if (rc)                            // q. any error yet? 
             {                              // a. yes .. 
             state = RcvError;              // .. declare an error 
             continue; 
             } 
 
         switch(hmsg.fax_ctl_fld)           // process based on message type 
             { 
             case FAX_FCF_TSI:              // transmitting subscriber ID 
                Display_Msg(6);             // update the status 
                Reverse_Bytes(hmsg.data,    // .. reset the bytes to normal 
                                     20); 
                hmsg.data[20] = 0;          // .. end string in zero 
 
                strcpy((char *) fh.ff_id,   // .. copy it to file header 
                       (char *) hmsg.data); 
 
                Display_ID("Called by:",    // .. and display the remote ID 
                                hmsg.data); 
                break; 
 
            case FAX_FCF_DCN:               // disconnect 
                Display_Msg(16);            // update the status 
                state = PhaseE;             // .. and hang up 
                continue; 
 
             case FAX_FCF_DCS:              // digital command signal 
                Display_Msg(7);             // update the status 
                memcpy(fh.ff_dcs, hmsg.data,// .. move the DCS to the header 
                          hmsg.len - 7); 
 
                dcs_received = 1;           // show we got DCS 
                break; 
             } 
 
         if (hmsg.ctl_fld == FAX_CTL_FF)    // q. final frame? 
             {                              // a. yes .. 
             state = SetSpeed;              // .. set link speed 
 
             fseek(faxfile, 0L, SEEK_SET);  // go to start of file 
             fwrite(&fh, 128, 1, faxfile);  // .. and write the header 
             } 
 
         break; 
 
    case SetSpeed:                          // set the link speed 
         if (dcs_received == 0)             // q. dcs received? 
             {                              // a. no .. 
             error = 4;                     // .. show the error 
             state = RcvError;              // .. and go to error state 
             continue;                      // .. continue with next state 
             } 
 
         switch (fh.ff_dcs[1] & 0x30)       // get modulation value 
             { 
             case 0x00:                     // q. speed 2400? 
                 speed = 24;                // a. yes . set speed 
                 i = 44;                    // .. and message 
                 break; 
 
             case 0x10:                     // q. speed 4800? 
                 speed = 48;                // a. yes .. set speed 
                 i = 43;                    // .. and message 
                 break; 
 
             case 0x30:                     // q. speed 7200? 
                 speed = 72;                // a. yes .. set speed 
                 i = 42;                    // .. and message 
                 break; 
 
             case 0x20:                     // q. speed 9600? 
                 speed = 96;                // a. yes .. set speed 
                 i = 41;                    // .. and message 
                 break; 
             } 
 
         Display_Msg(i);                    // display speed message 
         state = PrepTCF;                   // next, prepare to test link 
         break; 
 
    case PrepTCF:                           // prepare to receive TCF 
         rc = Data_Mode(RECEIVE, speed);    // start the receive 
 
         if (rc == 1)                       // q. did connection fail? 
             state = FailConnTCF;           // a. yes.. fail TCF connect 
 
         else if (rc != 0)                  // q. other failure? 
             state = RcvError;              // a. yes .. declare an error 
 
         else 
             { 
             state = RcvTCF;                // receive training check frame 
             Display_Msg(9);                // update the status 
             } 
 
         break; 
 
    case FailConnTCF:                       // fail the TCF connect 
         rc = Send_Hmsg(FINAL,              // send failure to train 
                        FAX_FCF_FTT, 0); 
 
         if (rc)                            // q. retrain send fail? 
             state = RcvError;              // a. yes .. declare an error 
 
         else 
             state = GetFrames;             // get a new set of frames 
 
         break; 
 
    case RcvTCF:                            // receive/test TCF 
         rc = Get_Char(&c, 5);              // wait a max of 5 secs for data 
 
         if (rc != 0)                       // q. any error 
            {                               // a. yes .. 
            state = RcvError;               // .. declare an error 
            continue;                       // .. and continue processing 
            } 
 
         if ((UCHAR) c == 0)                // q. zero byte? 
            state = RcvTCF1;                // a. yes .. receive the rest 
 
         break; 
 
 
    case RcvTCF1:                           // get rest of TCF 
         st = get_time();                   // get time of day in ticks 
 
         for(;;)                            // get characters 
             { 
             rc = Get_Char(&c, 1);          // get a character 
 
             if (rc != 0)                   // q. any error? 
                 {                          // a. yes .. 
                 Purge(cp, 1);              // .. purge the comm line 
                 error = 7;                 // .. TCF too short 
                 state = RcvError;          // .. declare an error 
                 break;                     // .. and exit loop 
                 } 
 
             if (c != 0)                    // q. end of sequence? 
                 break;                     // a. yes .. exit loop 
             } 
 
         if (rc != 0)                       // q. was there an error? 
             continue;                      // a. yes .. continue process 
 
         et = elapsed_time(st);             // calculate elapsed time 
 
         if ((et < 24) || (et > 31))        // q. 1.5 seconds +/- 10%? 
             {                              // a. yes .. 
             Purge(cp, 1);                  // .. purge the comm line 
             state = FailTCF;               // .. declare train failure 
             continue;                      // .. and continue loop 
             } 
 
         rc = wait_for(FAX_NO_CARR,         // wait for no carrier 
                       FAX_ERR, 5) - 1;     // .. or other information 
 
         if (rc != 0)                       // q. NO CARRIER received? 
             {                              // a. no .. 
             state = RcvError;              // .. declare receieve error 
             continue;                      // .. and continue loop 
             } 
 
         state = Confirm;                   // else .. confirm TCF 
         break;                             // .. and continue processing 
 
    case FailTCF:                           // fail the TCF 
         Display_Msg(19);                   // update the status 
 
         rc = Send_Hmsg(FINAL,              // send failure to train 
                        FAX_FCF_FTT, 0); 
 
         if (rc)                            // q. retrain send fail? 
             state = RcvError;              // .. declare an error 
 
         else 
             state = GetFrames;             // get a new set of frames 
 
         break; 
 
    case Confirm:                           // confirm training check frame 
         Display_Msg(10);                   // update the status 
         rc = Send_Hmsg(FINAL,              // send the confirmation 
                        FAX_FCF_CFR, 0); 
 
         if (rc != 0)                       // q. confirmation go ok? 
            { 
            state = RcvError;               // a. no .. show the error 
            continue;                       // .. continue processing 
            } 
 
         state = PhaseC;                    // start receiving FAX 
         break; 
 
    case PhaseC:                            // receive the FAX data 
         rc = Data_Mode(RECEIVE, speed);    // start receiving the FAX 
 
         if (rc != 0)                       // q. receive start ok? 
            { 
            state = RcvError;               // a. no .. show the error 
            continue;                       // .. continue processing 
            } 
 
         Display_Msg(11);                   // update the status 
 
         state = RcvPage;                   // receive a page of data 
 
         dleflag = FALSE;                   // set no DLE seen yet 
 
         pbi = 0;                           // reset  the page buffer index 
         pagelen = 0;                       // .. and no page data yet 
 
         fseek(faxfile, 0L, SEEK_END);      // go to end of file 
         reclen = ftell(faxfile);           // .. save the position 
         fwrite(&pagelen, 4, 1, faxfile);   // .. write the page's length 
         break; 
 
    case RcvPage:                           // receive 1 page of data 
         rc = Get_Char(&c, 1);              // get a character 
 
         if (rc != 0)                       // q. receive ok? 
             {                              // a. no .. 
             state = RcvError;              // .. declare an error 
             continue;                      // .. and continue processing 
             } 
 
         pagebuf[pbi++] = c;                // save the character 
         pagelen++;                         // increment bytes in page 
 
 
         if (pbi == 1024)                   // q. buffer full? 
             {                              // a. yes .. 
             fwrite(pagebuf, 1024, 1,       // .. write out a page 
                       faxfile); 
 
             pbi = 0;                       // reset page buffer index 
             } 
 
         if (dleflag)                       // q. previous char DLE? 
             {                              // a. yes .. 
             dleflag = FALSE;               // .. reset the flag 
 
             if (c == ETX)                  // q. DLE ETX sequence? 
                 {                          // a. yes .. 
                 state = EndPage;           // .. process end of page 
                 Display_Msg(12);           // update the status 
                 } 
             } 
 
         else if (c == DLE)                 // q. char a DLE? 
             dleflag = TRUE;                // a. yes .. show true 
 
         break;                             // continue processing 
 
    case EndPage:                           // Page complete 
         rc = wait_for(FAX_NO_CARR,         // we should now have no carrier 
                       FAX_ERR, 5) - 1; 
 
         if (pbi)                           // buffer contain data? 
             fwrite(pagebuf, pbi, 1,        // a. yes.. write out a page 
                       faxfile); 
 
         fseek(faxfile, reclen, SEEK_SET);  // set postion to length record 
         fwrite(&pagelen, 4, 1, faxfile);   // update the page's length 
         fseek(faxfile, 0L, SEEK_END);      // .. return to end of file 
 
         if (rc != 0)                       // q. correct response? 
             {                              // a. no .. 
             state = RcvError;              // .. declare an error 
             continue; 
             } 
 
         state = PhaseD;                    // start post message procedure 
         break; 
 
    case PhaseD:                            // post message procedure 
         rc = Rcv_Hmsg();                   // get the next message 
 
         if (rc != 0)                       // q. correct response? 
             {                              // a. no .. 
             state = RcvError;              // .. declare an error 
             continue; 
             } 
 
         i = hmsg.fax_ctl_fld;              // save the response 
 
         rc = Send_Hmsg(FINAL,              // send the final frame 
                        FAX_FCF_MCF, 0);    // .. message confirmation 
 
         if (rc != 0)                       // q. correct response? 
             {                              // a. no .. 
             state = RcvError;              // .. declare an error 
             continue; 
             } 
 
         switch(i)                          // based on the response.. 
            { 
            case FAX_FCF_MPS:               // multi-page signal 
                Display_Msg(13);            // update the status 
                state = PhaseC;             // .. receive next page 
                break; 
 
            case FAX_FCF_EOM:               // end of message 
                Display_Msg(14);            // update the status 
                state = PhaseB;             // .. renegotiate & continue 
                break; 
 
            case FAX_FCF_EOP:               // end of procedure 
                Display_Msg(15);            // update the status 
                state = PhaseE;             // .. disconnect now 
                break; 
 
            case FAX_FCF_DCN:               // disconnect 
                Display_Msg(16);            // update the status 
                state = PhaseE;             // .. and hang up 
                continue; 
 
            default: 
                state = RcvError;           // unknown response 
                break; 
            } 
 
         break;                             // continue processing 
 
    case PhaseE:                            // call is done .. disconnect 
         cp->Write(FAX_HANGUP);             // hangup the modem 
         state = ExitFax;                   // .. and exit the fax receive 
         break; 
 
    case RcvError:                          // error during receive 
         Display_Msg(17);                   // update the status 
 
         if (rc == -2)                      // q. user cancellation? 
            {                               // a. yes .. 
            state = UserCan;                // .. show the reason 
            continue;                       // .. continue processing 
            } 
 
         else if (error)                    // q. error set? 
             {                              // a. yes .. 
             fs.f_ptr = fax_errors[error];  // .. set the message pointer 
             (stat)(2, &fs);                // .. and display it 
             } 
 
         else if (rc == -1)                 // q. timeout? 
             Display_Msg(20);               // a. yes .. show the message 
 
         else if (rc == 1)                  // q. unexpected response? 
             Display_Msg(21);               // a. yes .. tell the user 
 
         state = ExitFax;                   // .. set new state 
         break; 
 
    case UserCan:                           // user cancelled transmission 
         Display_Msg(18);                   // update the status 
         state = ExitFax;                   // .. set new state 
         break; 
 
    case ExitFax:                           // exit FAX receive 
         if (fileopen)                      // q. fax file open? 
             fclose(faxfile);               // a. yes .. close the file 
 
         Reset_Modem();                     // reset the modem 
 
         loop = FALSE;                      // .. end the loop 
         break;                             // .. and return to caller 
    } 
} 
 
 
 
/* ******************************************************************** 
 * 
 *  Send -- Send a Facsimile 
 * 
 * ******************************************************************** */ 
 
void Fax::Send(char *faxname,               // fax file name 
               char *telno)                 // telephone number 
 
{ 
int     rc = 0,                             // return code 
        speed,                              // link speed 
        nextspeed,                          // next link speed 
        fileopen = FALSE,                   // receive file not open 
        dis_received = FALSE,               // DIS not yet received 
        i,                                  // work variable 
        error = 0,                          // error number 
        loop = TRUE;                        // loop until complete 
 
FILE   *faxfile;                            // fax file 
 
char    dis[16],                            // DIS received 
        csi[21],                            // CSI received 
        c,                                  // work character 
        w;                                  // work register 
 
ULONG   pagelen,                            // length of received page 
        st;                                 // work for elapsed time 
 
enum    SndStates                           // FAX send states 
          { 
          PhaseA,                           // phase A - make connection 
          PhaseB,                           // get HDLC frames 
          SetSpeed,                         // set the link speed 
          SendTSI,                          // select speed, send ID & DCS 
          SendTCF,                          // send the TCF 
          GetConfTCF,                       // get TCF confirmation 
          PhaseC,                           // send the document 
          SendPage,                         // send a page of data 
          PhaseD,                           // post message process 
          AnotherPage,                      // another page to send 
          AllSent,                          // send complete 
          PhaseE,                           // disconnect 
          SndError,                         // error during send 
          UserCan,                          // user cancelled transmission 
          ExitFax                           // exit FAX receive 
          } state; 
 
strcpy(fh.ff_id, "                    ");   // blank out the ID string 
state = PhaseA;                             // start in Phase A 
 
if ((faxfile = fopen(faxname, "rb"))        // q. send file open ok? 
                    == NULL) 
    {                                       // a. no .. 
    error = 9;                              // .. set the error code 
    state = SndError;                       // .. and declare an error 
    } 
 
fread(&fh, 128, 1, faxfile);                // read the header 
 
if (strcmp(fh.ff_type, "G3"))               // q. G3 file? 
    {                                       // a. no .. 
    error = 13;                             // .. show invalid format 
    state = SndError;                       // .. and declare an error 
    } 
 
fileopen = TRUE;                            // show the file is open 
 
fread(&pagelen, 4, 1, faxfile);             // read in the first page length 
 
while(loop)                                 // top of state machine 
  switch(state)                             // perform next state 
    { 
    case PhaseA:                            // phase A - make connection 
         Display_Msg(1);                    // update the status 
         if ((rc = Init_Modem()) != 0)      // q. modem init ok? 
             { 
             error = 1;                     // a. no .. exit now 
             state = SndError;              // .. declare the error 
             continue; 
             } 
 
         Display_Msg(22);                   // update the status 
 
         cp->Write(FAX_DIAL);               // send the dial command 
         cp->Write(telno);                  // .. and the phone number 
         cp->Write('\r');                   // .. and finish the command 
 
         rc = wait_for(FAX_CONN,            // wait for a connection 
                       FAX_NO_CARR, 120); 
 
         if (--rc)                          // q. any error 
             {                              // a. yes .. 
             if (rc == 1)                   // q. connection fail? 
                error = 10;                 // a. yes .. show connect failed 
 
             state = SndError;              // declare an error 
             continue;                      // .. go to next state 
             } 
 
         state = PhaseB;                    // get frames 
         connected = TRUE;                  // ... show we are connected 
         break; 
 
    case PhaseB:                            // get HDLC frames 
         if ((rc = Rcv_Hmsg()) != 0)        // q. any error getting a frame? 
             if (rc < 0)                    // a. yes .. timeout or ESC? 
                 {                          // a. yes .. 
                 error = 3;                 // .. error receiving HDLC frame 
                 state = SndError;          // .. leave with error. 
                 continue;                  // .. continue process 
                 } 
             else                           // else .. NO CARRIER 
                 { 
                 cp->Write(FAX_SILENT1);    // wait for 200ms silence 
 
                 rc = wait_for(FAX_OK,      // wait for the OK response 
                               FAX_ERR, 
                               10) - 1; 
 
                 state = SetSpeed;          // .. and set the speed 
                 } 
 
         if (rc)                            // q. any error yet? 
             {                              // a. yes .. 
             state = SndError;              // .. declare an error 
             continue; 
             } 
 
         switch(hmsg.fax_ctl_fld)           // process based on message type 
             { 
             case FAX_FCF_CSI:              // called subscriber ID 
                Display_Msg(23);            // update the status 
                Reverse_Bytes(hmsg.data,    // .. reset the bytes to normal 
                                     20); 
                hmsg.data[20] = 0;          // .. end string in zero 
 
                strcpy(csi,                 // .. copy it to work area 
                       (char *) hmsg.data); 
 
                Display_ID("Connected to:", // .. and display the remote ID 
                             (UCHAR *) csi); 
                break; 
 
             case FAX_FCF_DIS:              // digital information signal 
                Display_Msg(24);            // update the status 
                memcpy(dis, hmsg.data,      // .. move the DIS to work area 
                          hmsg.len - 7); 
 
                dis_received = 1;           // show we got DIS 
                break; 
 
             case FAX_FCF_NSF:              // Non-standard facilities 
                Display_Msg(8);             // update the status 
                break;                      // .. we don't process these 
             } 
 
         break; 
 
    case SetSpeed:                          // set the link speed 
         if (dis_received == 0)             // q. dis received? 
             {                              // a. no .. 
             error = 11;                    // .. show the error 
             state = SndError;              // .. and go to error state 
             continue;                      // .. continue with next state 
             } 
 
         switch (dis[1] & 0x30)             // select initial speed 
             { 
             case 0x30:                     // q. speed 7200? 
             case 0x20:                     // q. speed 9600? 
                 nextspeed = 96;            // a. yes .. set speed 
                 break; 
 
             case 0x10:                     // q. speed 4800? 
                 nextspeed = 48;            // a. yes .. set speed 
                 break; 
 
             case 0x00:                     // q. speed 2400? 
                 nextspeed = 24;            // a. yes . set speed 
                 break; 
             } 
 
         state = SendTSI;                   // next, tell 'em about us 
         break; 
 
    case SendTSI:                           // select speed, send ID & DCS 
         dcs_msg[1] &= 0xcf;                // set off speed bits 
 
         switch (speed = nextspeed)         // based on selected speed 
           { 
           case 96:                         // q. speed = 9600? 
               dcs_msg[1] |= 0x20;          // a. yes .. set speed 
 
               if ((dis_msg[1] | 0x30)      // q. 7200 supported? 
                                == 0x30) 
                   nextspeed = 72;          // a. yes .. next speed = 7200 
 
               else 
                   nextspeed = 48;          // else .. next speed = 4800 
 
               i = 41;                      // select speed message 
               break; 
 
           case 72:                         // q. speed = 7200? 
               dcs_msg[1] |= 0x30;          // a. yes .. set speed 
               nextspeed = 48;              // .. next speed = 4800 
               i = 42;                      // select speed message 
               break; 
 
           case 48:                         // q. speed = 4800? 
               dcs_msg[1] |= 0x10;          // a. yes .. set speed 
               nextspeed = 24;              // .. next speed = 2400 
               i = 43;                      // select speed message 
               break; 
 
           case 24:                         // q. speed = 2400? 
               error = 12;                  // a. yes.. can't connect 
               state = SndError;            // .. declare the error 
               i = 44;                      // select speed message 
               continue;                    // .. continue state machine 
           } 
 
         Display_Msg(25);                   // update the status 
         rc = Send_TSI();                   // .. send our ID 
 
         if (rc)                            // q. error? 
             { 
             state = SndError;              // a. yes .. 
             continue;                      // .. declare the error 
             } 
 
         Display_Msg(26);                   // update status again 
 
         state = ((rc = Send_DCS()) != 0) ? // q. DCS send go ok? 
                                 SndError : // a. no .. declare error 
                                  SendTCF ; // else .. send the check frame 
         break; 
 
    case SendTCF:                           // send the TCF 
         Display_Msg(i);                    // show speed 
         Display_Msg(27);                   // update the status 
 
         rc = Data_Mode(TRANSMIT, speed);   // go to data mode 
 
         if (rc)                            // q. any error? 
             {                              // a. yes .. 
             state = (rc != 1) ? SndError : // .. select general error 
                               GetConfTCF;  // .. or just connection failure 
             continue; 
             } 
 
         cp->Write(0);                      // Send the TCF 
 
         for (st = get_time();              // .. let the modem send zeroes 
                 elapsed_time(st) < 28;);   // .. for 1.5 seconds 
 
         cp->Write(DLE);                    // .. ended with DLE 
         cp->Write(ETX);                    // .. ETX 
 
 
         rc = wait_for(FAX_NO_CARR,         // wait for no carrier or OK 
                       FAX_OK, 5);          // .. for 5 seconds 
 
         if (rc-- < 1)                      // q. error? 
             state = SndError;              // a. yes .. declare the error 
 
         else 
             state = GetConfTCF;            // else .. try for confirmation 
 
         break; 
 
    case GetConfTCF:                        // get TCF confirmation 
         Display_Msg(28);                   // update the status 
 
         if ((rc = Rcv_Hmsg()) != 0)        // q. any error getting a frame? 
             {                              // a. yes .. 
             error = 3;                     // .. error receiving HDLC frame 
             state = SndError;              // .. leave with error. 
             continue;                      // .. continue process 
             } 
 
         switch(hmsg.fax_ctl_fld)           // process based on message type 
             { 
             case FAX_FCF_CFR:              // confirmation 
                  state = PhaseC;           // send the document 
                  break; 
 
             case FAX_FCF_FTT:              // failure to train 
                  Display_Msg(30);          // update the status 
                  state = SendTSI;          // next speed and retry session 
                  break; 
 
             default:                       // unknown message 
                  error = 21;               // unexpected response 
                  state = SndError;         // .. declare an error 
                  break; 
             } 
 
         break; 
 
    case PhaseC:                            // send the document 
         Display_Msg(29);                   // update the status 
         rc = Data_Mode(TRANSMIT, speed);   // .. set data mode 
 
         if (rc)                            // q. any error? 
             {                              // a. yes .. 
             state = SndError;              // .. declare an error 
             continue; 
             } 
 
         state = SendPage;                  // else .. send the page 
         break; 
 
    case SendPage:                          // send a page of data 
         i = pagelen > 128 ?                // determine number of bytes 
                128 : (int) pagelen;        // .. to read and send 
 
         pagelen -= i;                      // decrement page length 
 
         fread(pagebuf, i, 1, faxfile);     // read data from the file 
         cp->Write((char *) pagebuf, i);    // .. and send the data 
 
         st = SECS(10);                     // for max of 10 seconds 
 
         while ((cp->OCount() > 256) &&     // let the buffer empty 
                                error == 0) // .. while there's no error 
             if (time_out(&(long) st))      // q. timeout? 
                 error = 15;                // a. yes .. set the error code 
 
         if (error)                         // q. error occur? 
             {                              // a. yes .. 
             state = SndError;              // .. declare an error 
             continue;                      // .. and continue processing 
             } 
 
         if (cp->Read(&c, &w, &w) == 0)     // q. char available? 
             if (c == XOFF)                 // a. yes .. is it XOFF? 
                {                           // a. yes .. 
                Display_Msg(35);            // .. show XOFF received 
 
                while(cp->Read(&c, &w, &w)  // .. wait for another char 
                             == 0xffff); 
 
                Display_Msg(36);            // .. erase XOFF message 
                } 
 
 
         if (pagelen == 0)                  // q. page complete? 
             state = PhaseD;                // a. yes .. continue 
 
         break; 
 
    case PhaseD:                            // post message process 
         fread(&pagelen, 4, 1, faxfile);    // read next page length 
 
         while (cp->OEmpty() != 0);         // wait for transmit complete 
 
         rc = wait_for(FAX_OK,              // should get OK 
                       FAX_ERR, 5) - 1; 
 
         if (rc)                            // q. did send complete? 
             {                              // a. no .. 
             state = SndError;              // .. declare an error 
             continue; 
             } 
 
         Display_Msg(31);                   // update the status 
 
         if (pagelen)                       // q. more pages? 
             state = AnotherPage;           // a. yes ... send another 
 
         else 
             state = AllSent;               // else .. all sent 
 
         break; 
 
    case AnotherPage:                       // another page to send 
         Display_Msg(32);                   // update the status 
 
         rc = Send_Hmsg(FINAL,              // send the multipage signal 
                        FAX_FCF_MPS, 0); 
 
         if (rc != 0)                       // q. send ok? 
            {                               // a. no .. 
            state = SndError;               // .. declare an error 
            continue;                       // .. continue processing 
            } 
 
         if ((rc = Rcv_Hmsg()) != 0)        // q. any error getting a frame? 
             {                              // a. yes .. 
             error = 3;                     // .. error receiving HDLC frame 
             state = SndError;              // .. leave with error. 
             continue;                      // .. continue process 
             } 
 
         switch(hmsg.fax_ctl_fld)           // process based on message type 
             { 
             case FAX_FCF_MCF:              // message confirmation 
                  state = PhaseC;           // send next page 
                  break; 
 
             default: 
                  error = 14;               // unexpected response 
                  state = SndError;         // .. declare an error 
                  continue; 
             } 
         break; 
 
    case AllSent:                           // send complete 
         Display_Msg(33);                   // update the status 
 
         rc = Send_Hmsg(FINAL,              // send end of procedure 
                        FAX_FCF_EOP, 0); 
 
         if (rc != 0)                       // q. send ok? 
            {                               // a. no .. 
            state = SndError;               // .. declare an error 
            continue;                       // .. continue processing 
            } 
 
         if ((rc = Rcv_Hmsg()) != 0)        // q. any error getting a frame? 
             {                              // a. yes .. 
             error = 3;                     // .. error receiving HDLC frame 
             state = SndError;              // .. leave with error. 
             continue;                      // .. continue process 
             } 
 
         switch(hmsg.fax_ctl_fld)           // process based on message type 
             { 
             case FAX_FCF_MCF & 0x7f:       // message confirmation 
                  state = PhaseE;           // send next page 
                  break; 
 
             default: 
                  error = 14;               // unexpected response 
                  state = SndError;         // .. declare an error 
                  continue; 
             } 
         break; 
 
    case PhaseE:                            // disconnect 
         Display_Msg(34);                   // update the status 
 
         rc = Send_Hmsg(FINAL,              // send end of procedure 
                        FAX_FCF_DCN, 0); 
 
         if (rc != 0)                       // q. send ok? 
            {                               // a. no .. 
            state = SndError;               // .. declare an error 
            continue;                       // .. continue processing 
            } 
 
         state = ExitFax;                   // exit fax send procedure 
         break; 
 
 
    case SndError:                          // error during send 
         Display_Msg(17);                   // update the status 
 
         if (rc == -2)                      // q. user cancellation? 
            {                               // a. yes .. 
            state = UserCan;                // .. show the reason 
            continue;                       // .. continue processing 
            } 
 
         else if (error)                    // q. error set? 
             {                              // a. yes .. 
             fs.f_ptr = fax_errors[error];  // .. set the message pointer 
             (stat)(2, &fs);                // .. and display it 
             } 
 
         else if (rc == -1)                 // q. timeout? 
             Display_Msg(20);               // a. yes .. show the message 
 
         else if (rc == 1)                  // q. unexpected response? 
             Display_Msg(21);               // a. yes .. tell the user 
 
         state = ExitFax;                   // .. set new state 
         break; 
 
    case UserCan:                           // user cancelled transmission 
         Display_Msg(18);                   // update the status 
         state = ExitFax;                   // .. set new state 
         break; 
 
    case ExitFax:                           // exit FAX receive 
         if (fileopen)                      // q. fax file open? 
             fclose(faxfile);               // a. yes .. close the file 
 
         Reset_Modem();                     // .. reset the modem 
 
         loop = FALSE;                      // .. end the loop 
         break;                             // .. and return to caller 
    } 
}