home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / ddjmag / ddj8910.zip / SMITH.LST < prev    next >
File List  |  1989-09-07  |  40KB  |  1,184 lines

  1. _Finite State Machines for XModem_
  2. by Donald W. Smith
  3.  
  4. [LISTING ONE]
  5.  
  6. /*  CTERM.H   defines for CTERMx series. */
  7.  
  8. #define BUFSIZE     128
  9. #define DISKREAD    (BUFSIZE * 40)
  10.  
  11. /* some ASCII defines */
  12. #define SOH         0x01        /* start of header */
  13. #define EOT         0x04        /* end of transmission */
  14. #define ACK         0x06        /* positive acknowledgement */
  15. #define BS          0x08        /* backspace */
  16. #define CR          0x0D        /* Carriage Return */
  17. #define NAK         0x15        /* Negative acknowledgement */
  18. #define CAN         0x18        /* Cancel */
  19. #define EoF         0x1A        /* End of File (used for name) */
  20. #define ESC         0x1B        /* ASCII escape key */
  21. #define CRC         0x43        /* ASCII 'C' (CRC mode request) */
  22. #define BADNAME     0x75        /* Received bad name checksum */
  23. #define TIMEOUT     -1          /* for state machine logic */
  24.  
  25. static char *SPEED_LIST[] =
  26.  {   "50",   "75",  "110",  "135",  "150",  "300",  "600",  "1200",
  27.    "1800", "2000", "2400", "3600", "4800", "7200", "9600", "19200",
  28.    "28800", "38400", "57600" };
  29.  
  30. static int SPEED_VALS[] =
  31.  {   50,   75,  110,  135,  150,  300,  600,  1200,
  32.    1800, 2000, 2400, 3600, 4800, 7200, 9600, 19200,
  33.    28800, 38400, 57600,  0 };  /* zero for anchor */
  34.  
  35. static char *PARITY_LIST[] =
  36.  { "NONE", "NONE", "ODD", "EVEN", };    /* matches L_CTRL p_enable + p_even */
  37.  
  38. static char *STOP_LIST[] =
  39.  { "ONE", "TWO" };                      /* matches L_CTRL p_two_stops */
  40.  
  41. static char *BITS_LIST[] =
  42.  { "FIVE", "SIX", "SEVEN", "EIGHT" };
  43.  
  44. /* Define some common values for the lctrl bit fields */
  45. #define ate1none   0x03;
  46. #define sev1even   0x1A;
  47.  
  48. typedef int (*action)();      /* action is a pointer to a function */
  49.  
  50. struct event_entry {
  51.    char comment[20];      /* for commented reading and tracing capability */
  52.    action act;            /* pointer to action function */
  53.    int param;             /* parameter to pass to function */
  54.    enum send_state next_state;  /* from an enumerated list of states */
  55.   };
  56.  
  57. /* The following enumeration is used in all modules */
  58. enum modes { M_Cmd, M_Term, M_Config, M_XSend, M_XRecv };
  59.  
  60. /* This struct maps the data packets for the protocol */
  61. typedef struct pkt {
  62.   unsigned char soh;
  63.   unsigned char pkt;
  64.   unsigned char pkt_cmp;
  65.   unsigned char data[BUFSIZE];
  66.   unsigned char crc1;
  67.   unsigned char crc2;
  68. } XPKT;
  69.  
  70. /*  Defines used for keyfun(). - Map exactly to BIOS intr 16h 0 and 1 */
  71. #define KEYHIT     1
  72. #define READNEXT   0
  73. #define BIOS_KEY 0x16        /* for int86 call for keyboard interrupt */
  74.  
  75. /* The following defines are used to map scan codes for f keys and specials */
  76. #define HOME 0x4700
  77. #define PGUP 0x4900
  78. #define END  0x4F00
  79. #define PGDN 0x5100
  80. #define INS  0x5200
  81. #define DEL  0x5300
  82. #define CBRK 0x0000      /* Book says 0x5400.  I see 0x0000 */
  83.  
  84.  
  85. [LISTING TWO]
  86.  
  87. #define PORT  2              /* currently a define 12/16/88 */
  88. #define NAMESIZE   24        /* Used by xmsend and recv */
  89. #define TXTRIES     5        /* Transmit retries */
  90. #define RXTRIES    10        /* Receive retries */
  91.  
  92. /* Parameters to pass to Send Action Make_Pkt and Recv Action Frame_Wait */
  93. #define RESEND      0
  94. #define INIT        1
  95. #define NEXT        2
  96.  
  97. /*  The following declaration is used to pass config info to Config_Comm().  */
  98. typedef struct {     /* Map to UART Line Control bits */
  99.         unsigned wlen      : 2;       /* Word length - 5 */
  100.         unsigned two_stops : 1;       /* 1: Two stops,   0: One stop */
  101.         unsigned parity    : 2;       /* 00, 01: NONE, 10 ODD, 11 EVEN */
  102.         unsigned p_stuck   : 1;       /* 1: Stuck,       0: Normal */
  103.         unsigned set_break : 1;       /* 1: Send break   0: Stop break */
  104.         unsigned div_latch : 1;       /* 1: See divisors 0: See data bytes */
  105.         unsigned           : 8;
  106.       } L_CTRL;
  107.  
  108.   typedef union {
  109.         unsigned char lctrl;
  110.         L_CTRL lbits;
  111.       } U_BITS;
  112.  
  113.   typedef struct
  114.     {
  115.       unsigned speed;       /* value from atoi() of value from speed array */
  116.       U_BITS ubits;
  117.     } S_INIT;
  118.  
  119.  
  120.  
  121. [LISTING THREE]
  122.  
  123. /* XMRECV.C:  Xmodem receive state machine processing */
  124.  
  125. #include <stdio.h>
  126. #include <ctype.h>
  127. #include <string.h>
  128. #include "cterm.h"     /* common defines and structs for cterm */
  129. #include "commn.h"                  /* brings in S_INIT struct */
  130.  
  131. enum recv_state
  132.   { S_Init_Recv, S_Incoming, S_First_What, S_DePktize, S_Exit };
  133.  
  134. #define TRACE 1       /* to turn on state machine tracing */
  135. /* #define SMTRACE 1 */
  136. #ifdef SMTRACE
  137. static char *state_list[] =
  138.   {"Init_Recv", "Incoming", "First_What", "De-Pktize", "Exit"};
  139. #endif
  140.  
  141. #define RECV_EVENTS    4         /* # of events per RECV state */
  142.  
  143. /* Variables local to this file only */
  144. static char r_fname[NAMESIZE+1]; /* name of file to open */
  145. static FILE *r_fptr = NULL;      /* file pointer or number to use */
  146. static int sohor = SOH;          /* location to store first char of pkt hdr */
  147.  
  148. static int pkt = 1;               /* expected packet number */
  149. static S_INIT prev_conf;          /* save prev (parity) conf */
  150. static int virgin = 1;            /* 0 = beyond initial NAK stage */
  151.  
  152. /* EXTERNAL variables */
  153. extern int comport;                /* which comm port to use (from CTERM) */
  154. extern int crc;                    /* flag for CRC (!0) or checksum  (0) */
  155. extern unsigned crcaccum;          /* from xmutil */
  156. extern unsigned char checksum;     /* ditto */
  157. extern S_INIT cur_config;          /* from CTERM.  For timeout calc */
  158. extern enum modes mode;            /* ditto  term mode or... */
  159. extern int eschar;                 /* ditto   escape character variable */
  160. extern int keyfun(int);            /* ditto  BIOS keyboard I/O */
  161. extern unsigned int fgetsnn(FILE *, char *, int);
  162.  
  163. /* Messages posted by A_Recv_End */
  164. /*  If declared as char *user_msg, can't be used in state table.
  165.  *  No variables allowed.  But this way creates constants! */
  166. extern char user_msg[];
  167. extern char cancel[];
  168. extern char badwrite[];
  169. extern char eof_msg[];
  170. extern char giveup[];
  171. extern char badcomm[];
  172.  
  173. /************  Receive Actions: ********************/
  174.  
  175. /* ----- A_Prep_Recv: Prompts for file to receive, attempts open.
  176.  *   Returns: 0 if O.K., 1 if open fails, 2 is user abort.     */
  177. A_Prep_Recv( char *fname )
  178. {
  179.   int retval;
  180.  
  181.   fputs("\n Please Input file name to receive: ",stdout);
  182.   fgetsnn (stdin, fname, NAMESIZE );
  183.   if ( (fname[0] == eschar) || (fname[0] == '\0') )
  184.     return(2);
  185.   if ( (r_fptr = fopen (fname, "wb")) == NULL ) {
  186.     printf("\n Cannot open %s.  Try again.\n", fname);
  187.     return(1);
  188.   }
  189.  
  190.   prev_conf = cur_config;                 /* save entry config */
  191.   cur_config.ubits.lctrl = ate1none;      /* Force things to 8/1/N */
  192.   Config_Comm( comport, cur_config );
  193.  
  194.   eat_noise();                  /* clear out any garbage from buffer */
  195.   return(0);
  196. }
  197.  
  198. /* ----- A_Frame_Wait: Send a ctrl char, wait for reply.  -------
  199.  *   Returns: 0: OK, 1: comm error, 2: timeout, 3: no retries. */
  200. A_Frame_Wait(int which)
  201. {
  202.   char inch;
  203.   int errval;                       /* returned from reads and writes */
  204.   int numread = 1;
  205.   static int passes;                /* give up after 10 retries */
  206.   static char last;
  207.   int retval = 0;                   /* Running value to return */
  208.  
  209.   if (virgin)  {                    /* Waiting for first answer to NAK */
  210.     switch (which) {
  211.       case INIT:   crc = 0;         /* Go for CRC first -- fallthru will flip */
  212.                    passes = RXTRIES;
  213.                    pkt = 1;         /* Initialize to first expected pkt num */
  214.       case RESEND: crc = !crc;      /* flip global flag */
  215.                    last = (crc == 0) ? NAK : CRC;
  216.                    break;
  217.       default:     retval = 3;      /* Should not occur... but */
  218.     }
  219.   }
  220.   else {                            /* Not virgin.  Normal Retry logic */
  221.     switch (which) {
  222.       case NEXT:   last = ACK;
  223.                    passes = RXTRIES;
  224.                    break;
  225.       case RESEND: if (passes-- == 0) {
  226.                      last = CAN;
  227.                      retval = 3;
  228.                      passes = RXTRIES;      /* Reset to default */
  229.                    }
  230.                    else
  231.                      last = NAK;
  232.                    break;
  233.      default:      retval = 3;      /* An ounce of prevention */
  234.     }
  235.   }
  236.  
  237.   errval = writecomm( &last, 1);
  238.   if (errval != 0)
  239.     return(1);             /* Get out now! */
  240.  
  241.   eat_noise();             /* clear out any garbage */
  242.  
  243.   if (retval != 3) {
  244.     errval = read_comm( &numread, &inch, 10000 );
  245.     if (errval == TIMEOUT)
  246.       return (2);
  247.     else {                                  /* Got a live one! */
  248.       sohor = inch;                         /* set global */
  249.       if ( (virgin) && (inch == SOH) ) {    /* We're rolling! */
  250.         printf("\n\nReceiving file %s using %s.\n",
  251.                          r_fname,(crc == 0) ? "CheckSum" : "CRC" );
  252.         fputs("\nAwaiting packet # 0001",stdout);
  253.         virgin = 0;                         /* flip the local flag */
  254.       }
  255.     }
  256.   }
  257.   return(retval);
  258. }
  259.  
  260. /* ----- A_Which_Ctrl:  Parses first char received. -------------
  261.  *  Returns:  0: SOH, 1: CAN, 2: EOT, 3: unexpected (junk)     */
  262. A_Which_Ctrl(char *lead)
  263. {
  264.   switch (*lead) {
  265.     case SOH:  return(0);
  266.     case CAN:  return(1);
  267.     case EOT:  return(2);
  268.     default:   return(3);
  269.   }
  270. }
  271.  
  272. /* ----- CRC_Good:  Calculates the CRC/Checksum. ----------------
  273.  * Returns:  0 if OK, 2 if error                               */
  274. CRC_Good(char *buf, int crcflag, unsigned char crchi, unsigned char crclo)
  275. {
  276.   register int i;
  277.  
  278.   crcaccum = 0;  /* zero out global crc and checksum value */
  279.   checksum = 0;
  280.  
  281.   for (i = 0; i < BUFSIZE; i++, buf++)
  282.     updcrc(*buf);
  283.   updcrc(0);                     /* Xmodem deviant CRC calc */
  284.   updcrc(0);
  285.  
  286.   if (crcflag == 0) {
  287.     if (crchi != checksum)
  288.       return(2);
  289.   }
  290.   else {
  291.     if ( (crclo + ( crchi << 8)) != crcaccum )
  292.       return(2);
  293.   }
  294.   return(0);
  295. }
  296.  
  297. /* ----- Action Validate: After SOH, validates the xmodem header.
  298.  * Returns: 0: OK, 1: bad header, 2: bad CRC, 3: char timeout. */
  299. A_Validate(int *crcflag )
  300. {
  301.   int retval;
  302.   int readnum = (*crcflag == 0) ? 131 : 132; /* pass to read_comm */
  303.   int togo    = readnum;                     /* if partial, running count */
  304.   int msecs;                                 /* how long to wait */
  305.   XPKT r_pkt;                                /* packet receive buffer */
  306.   unsigned char *diskbuf = (unsigned char *) &r_pkt.data;
  307.   unsigned char *curptr  = (char *) &r_pkt.pkt;   /* Rem: got SOH already */
  308.   long frame_bits = ( (BUFSIZE + 3) * 10 *1000L );
  309.  
  310.   printf("\b\b\b\b%4d",pkt);        /* Allow up to 9999 frames */
  311.  
  312.   while (readnum != 0) {
  313.     msecs =  (int)( frame_bits / (long)cur_config.speed );
  314.     delay(msecs);                     /* Let the interrupt handler work */
  315.     retval  = read_comm( &readnum, curptr, msecs );
  316.     curptr  = curptr + readnum;       /* adjust curptr to next avail loc */
  317.     readnum = (togo -= readnum);      /* adjust BOTH to remainder of pkt */
  318.  
  319.     if (retval == TIMEOUT) {          /* Give it one more second if short */
  320.       togo = 1;                       /* prep togo for 1 char read test */
  321.       retval = read_comm( &togo, curptr, 1000);
  322.       if (retval == TIMEOUT)          /* Bad news.  Dead line */
  323.         return(3);
  324.       curptr++;                       /* recovered!   adjust and try again */
  325.       togo = --readnum;
  326.     }
  327.     frame_bits = togo * 10;           /* Adjust by bits per character */
  328.   }
  329.  
  330.   if (~r_pkt.pkt != r_pkt.pkt_cmp) {
  331.     return(1);
  332.   }
  333.   if ( r_pkt.pkt != (pkt % 256) )
  334.     if ( r_pkt.pkt == ( (pkt - 1) & 0xFF )  ) {
  335.       return(0);            /* duplicate packet!  Ack and ignore */
  336.     }
  337.     else
  338.       return(1);        /* Nak and retry.. probably useless but... */
  339.  
  340.   retval = CRC_Good(diskbuf, *crcflag, r_pkt.crc1, r_pkt.crc2);
  341.   if (retval != 0) {
  342.     return(2);
  343.   }
  344.  
  345.   fwrite(diskbuf, BUFSIZE, 1, r_fptr);
  346.   pkt++;
  347.   return (0);
  348. }
  349.  
  350. /* ----- Action EatRest: Eats the rest of a packet. ---------- */
  351. A_EatRest(int calories)
  352. {
  353.   int toeat = calories;
  354.   int retval = 0;
  355.   long frame_bits;
  356.   char junkbuf[BUFSIZE + 4];
  357.  
  358.   if (calories > BUFSIZE)
  359.     calories = BUFSIZE + 4;
  360.   frame_bits = ( calories * 10 * 1000L);
  361.   delay( (unsigned)(frame_bits/(long)cur_config.speed) + 500 );
  362.  
  363.   while (retval != TIMEOUT) {
  364.     retval = read_comm( &toeat, junkbuf, 1000);
  365.     toeat = 1;
  366.   }
  367.   retval = A_Frame_Wait(RESEND);
  368.   return(retval);
  369. }
  370.  
  371. /* ----- Action Recv_End: Only way out of Recv state machine.  */
  372. A_Recv_End ( char *reason )
  373. {
  374.   char eotc = ACK;             /* just in case we really Receive the file */
  375.  
  376.   if (r_fptr != NULL) {        /* Did we even get started??? */
  377.     if (reason != eof_msg) {   /* Started, but bad news during xfer */
  378.       eotc = CAN;
  379.       unlink(r_fname);         /* deletes the old file */
  380.     }
  381.     fclose(r_fptr);
  382.     writecomm(&eotc, 1);
  383.     Config_Comm( comport, prev_conf );   /* Put whatever parity back in */
  384.   }
  385.  
  386.   printf("\n *** Ending session.  %s.\a\n",reason);
  387.  
  388.   virgin = 1;
  389.   mode = M_Cmd;
  390.   return (RECV_EVENTS - 1);   /* last event always has next state S_Exit */
  391. }
  392.  
  393. /************  R E C E I V E    S T A T E    T A B L E  ****************/
  394.  struct event_entry recv_machine[(int)S_Exit][RECV_EVENTS] =
  395.  { /* S_Init_Recv */
  396.    { {  "fname O.K"  , A_Frame_Wait   , INIT         , S_Incoming      },
  397.      {  "fname bad"  , A_Prep_Recv    , (int)r_fname , S_Init_Recv     },
  398.      {  "user abort" , A_Recv_End     , (int)user_msg, S_Exit          },
  399.      {  "comm error" , A_Recv_End     , (int)badcomm , S_Exit          } },
  400.    /* S_Incoming */
  401.    { {  "got one"    , A_Which_Ctrl   , (int)&sohor  , S_First_What    },
  402.      {  "comm error" , A_Recv_End     , (int)badcomm , S_Exit          },
  403.      {  "timeout"    , A_Frame_Wait   , RESEND       , S_Incoming      },
  404.      {  "no retries" , A_Recv_End     , (int)giveup  , S_Exit          } },
  405.    /* S_First_What */
  406.    { {  "got SOH"    , A_Validate     , (int)&crc    , S_DePktize      },
  407.      {  "got CAN"    , A_Recv_End     , (int)cancel  , S_Exit          },
  408.      {  "got EOT"    , A_Recv_End     , (int)eof_msg , S_Exit          },
  409.      {  "got junk!"  , A_EatRest      , BUFSIZE      , S_Incoming      } },
  410.    /* S_DePktize */
  411.    { {  "pkt OK"     , A_Frame_Wait   , NEXT         , S_Incoming      },
  412.      {  "bad hdr"    , A_EatRest      , BUFSIZE      , S_Incoming      },
  413.      {  "bad CRC"    , A_Frame_Wait   , RESEND       , S_Incoming      },
  414.      {  "timeout"    , A_Frame_Wait   , RESEND       , S_Incoming      } }
  415.  };
  416.  
  417.  
  418. /* -------------- Xmodem Receive state machine --------------- */
  419. xmodem_recv()
  420. {
  421.    char inkey;                     /* place for user to abort */
  422.    int  event;                     /* event returned from action */
  423.    int  prevent;                   /* previous event */
  424.    struct event_entry *cur_entry;  /* pointer to current row/col of sm */
  425.    action new_action;              /* next action to perform */
  426.    enum send_state cur_state  = S_Init_Recv;
  427.  
  428.    event = A_Prep_Recv(r_fname);
  429.  
  430.    while (mode == M_XRecv)
  431.    {
  432.      prevent = event;      /* save the previous event for next state */
  433.      cur_entry = &recv_machine[(int)cur_state][event];
  434.  
  435. #ifdef SMTRACE
  436.      printf("State: %16s, Event: %2d, Note: %20s\n",
  437.           state_list[(int)cur_state], event, cur_entry->comment );
  438. #endif
  439.  
  440.      /* Based on the current state and event, execute action(param) */
  441.      new_action = cur_entry->act;
  442.      event = new_action(cur_entry->param);
  443.      cur_state  = recv_machine[(int)cur_state][prevent].next_state;
  444.  
  445.      if ( keyfun(KEYHIT) ) {
  446.        inkey = (char) keyfun(READNEXT);    /* Truncate to key only */
  447.        if (inkey == eschar)
  448.          A_Recv_End(user_msg);
  449.      }
  450.    }
  451.    return (0);
  452. }
  453.  
  454. [LISTING FOUR]
  455.  
  456. /* XMSEND.C   Xmodem Send state machine processing.   */
  457.  
  458. #include <conio.h>        /* for putch call */
  459. #include <ctype.h>
  460. #include <io.h>           /* for filelength call */
  461. #include <stdio.h>
  462. #include <string.h>
  463. #include "cterm.h"
  464. #include "commn.h"    /* brings in S_INIT struct and defines */
  465.  
  466. enum send_state
  467.  { S_Init_Send, S_Sync_Wait, S_Make_Pkt, S_Send_Pkt, S_Data_Response, S_Exit };
  468.  
  469. #ifdef TRACE
  470. char *state_list[] =
  471.   {"Init_Send", "Sync_Wait", "Make_Pkt", "Send_Pkt", "Data_Response", "Exit"};
  472. #endif
  473.  
  474. #define SEND_EVENTS    4      /* number of events handled per send state */
  475.  
  476. /* Variables local to this file only */
  477. static char s_fname[NAMESIZE+1];     /* name of file to open */
  478. static FILE *s_fptr = NULL;         /* file pointer or number to use */
  479. static XPKT s_pkt;                   /* packet to send */
  480. static S_INIT prev_conf;        /* saves previous bits, parity during xfer */
  481.  
  482. /* EXTERNAL variables and functions */
  483. extern int comport;                /* which comm port to use (from CTERM) */
  484. extern unsigned crcaccum;          /* from xmutil */
  485. extern unsigned char checksum;     /* ditto */
  486. extern int crc;                    /* ditto */
  487. extern S_INIT cur_config;          /* from CTERMx.  For send time calc */
  488. extern int eschar;                 /* ditto   escape character variable */
  489. extern enum modes mode;            /* ditto   term mode or... */
  490. extern int keyfun(int);            /* ditto   BIOS call to keyboard */
  491.  
  492. /*  If declared as char *user_msg, can't be used in state table.
  493.  *  No variables allowed.  But this way creates constants! */
  494. extern char user_msg[];
  495. extern char nonak[];
  496. extern char cancel[];
  497. extern char badread[];
  498. extern char eof_msg[];
  499. extern char giveup[];
  500.  
  501. /************  Send Actions: ********************/
  502.  
  503. /* ----- A_Get_Fname:  Prompts for file to transmit, opens. -----
  504.  * Returns: 0: OK, 1: open failed, 2: user abort. ------------ */
  505. A_Get_Fname( char *fname )
  506. {
  507.   long fbytes;
  508.   int  frecs;
  509.   int  fsecs;
  510.  
  511.   printf("\n Please Input file name to transmit: ");
  512.   fgetsnn (stdin, fname, NAMESIZE );
  513.   if ( (fname[0] == eschar) || (fname[0] == '\0') )
  514.     return(2);
  515.   if ( (s_fptr = fopen (fname, "rb")) == NULL ) {
  516.     printf("\n Cannot open %s.  Try again.\n", fname);
  517.     return(1);
  518.   }
  519.   fbytes = filelength( fileno(s_fptr) );
  520.   frecs = (  (fbytes / BUFSIZE) + ( (fbytes % BUFSIZE == 0) ? 0 : 1 )  );
  521.   /* The following adds time for turn around (ACK/NAK), but no errors */
  522.   fsecs = (int) ( (fbytes * 10) / (cur_config.speed / 2 ) );
  523.  
  524.   printf("\n File %s: %4d records, est. min:sec  %3d:%2d at %d bps.\n",
  525.               fname, frecs, fsecs / 60, fsecs % 60, cur_config.speed  );
  526.  
  527.   prev_conf = cur_config;                 /* save entry config */
  528.   cur_config.ubits.lctrl = ate1none;      /* Force things to 8/1/N */
  529.   Config_Comm( comport, cur_config );
  530.  
  531.   eat_noise();        /* Clear out any garbage in the input queue */
  532.   return(0);
  533. }
  534.  
  535. /* ----- A_Init_Wait: Waits for initial sync character. ---------
  536.  *  Returns: The value returned from A_Wait().                 */
  537. A_Init_Wait(int expected)
  538. {
  539.   static int tries  =  2;   /* try initial CRC, then once more */
  540.   static int passes = 10;   /* give up after 10 junk reads */
  541.   static int last;
  542.   int retval;
  543.  
  544.   switch(expected) {
  545.     case CRC:  last = CRC;     /* If we really want CRC... */
  546.                break;
  547.     case NAK:  last = NAK;     /* or if we only want Checksum */
  548.                break;
  549.     case NEXT: if (--tries == 0) {     /* want to switch? */
  550.                  last = (last == CRC) ? NAK : CRC;
  551.                  tries = 2;
  552.                }
  553.   }
  554.   printf("\rAwaiting %s...",(last == CRC) ? "CRC" : "NAK");
  555.   retval = A_Wait(last);
  556.   if (retval != 0) {
  557.     if (passes-- == 0)
  558.       return(3);           /* cancelled */
  559.     else
  560.      return(retval);
  561.   }
  562.  
  563.   passes = 10;                      /* reset passes counter */
  564.   crc = (last == CRC) ? 1 : 0;
  565.   return(retval);
  566. }
  567.  
  568. /* ------ A_Wait:  Waits for appropriate time for a character.
  569.  * Returns: 0: match, 1: if other, 2: timeout, 3: cancel.      */
  570. A_Wait( int expected)
  571. {
  572.   char inch;
  573.   int errval;
  574.   int numread = 1;
  575.   int retval = 0;
  576.  
  577.   errval = read_comm( &numread, &inch, (expected == SOH) ?  1000 : 10000 );
  578.   if ( numread > 0 ) {
  579.     if (inch == (char) expected)
  580.       retval = 0;
  581.     else retval = (inch == CAN) ? 3 : 1 ;
  582.   }
  583.   else
  584.    if (errval == TIMEOUT)
  585.      retval = 2;
  586.  
  587.   return (retval);
  588. }
  589.  
  590. /* ----- Action Make_Pkt: Reads from disk, formats packet. -----
  591.  * Returns:  0: OK, 1: disk trouble, 2: EOF found.            */
  592. A_Make_Pkt(int which )
  593. {
  594.   register int i;
  595.   int errval;
  596.   unsigned int lo_crc;
  597.   static int pkt;
  598.   static unsigned char *diskbuf = (unsigned char *) &s_pkt.data;
  599.   static unsigned char *curptr;   /* where are we now? */
  600.  
  601.   crcaccum = 0;  /* zero out global crc and checksum value */
  602.   checksum = 0;
  603.  
  604.   for (curptr = diskbuf, i = 0; i < BUFSIZE; i++, curptr++) {
  605.     if ( (errval = getc(s_fptr)) == EOF )
  606.       break;
  607.     *curptr = errval;
  608.     updcrc(errval);
  609.   }
  610.   if (i == 0)
  611.     return(2);                   /* That's all folks! */
  612.  
  613.   for ( ; i < BUFSIZE; i++, curptr++) {    /* Zero fill the rest of packet */
  614.     *curptr = 0;
  615.     updcrc(0);
  616.   }
  617.  
  618.   if (which == INIT) {
  619.     printf("\n\nSending file %s using %s.\n",
  620.                         s_fname,(crc == 0) ? "CheckSum" : "CRC");
  621.     pkt = 1;
  622.   }
  623.   else pkt = (++pkt % 256);
  624.  
  625.   s_pkt.soh     = SOH;
  626.   s_pkt.pkt     = pkt;
  627.   s_pkt.pkt_cmp = ~pkt;
  628.   updcrc(0);     /* finish off xmodem variation */
  629.   updcrc(0);
  630.   lo_crc = crcaccum;
  631.   if (crc != 0) {
  632.     s_pkt.crc1 = (crcaccum >> 8);    /* high byte first */
  633.     s_pkt.crc2 = lo_crc;
  634.   }
  635.   else
  636.     s_pkt.crc1 = checksum;
  637.  
  638.   return (0);
  639. }
  640.  
  641. /* ----- Action Send_Pkt: Send a packet out the comm port. ------
  642.  * Returns:  0: OK, 1: write err, 2: no retries, 3: cancelled  */
  643. A_Send_Pkt( int why )
  644. {
  645.   static int retries = TXTRIES; /* If not general, make a global table */
  646.   int errval;
  647.  
  648.   switch (why) {
  649.     case NEXT:    retries = TXTRIES;
  650.                   putch('.');         /* show we are making progress */
  651.                   break;
  652.     case NAK:
  653.     case TIMEOUT:
  654.     case RESEND:  --retries;
  655.                   putch('R');
  656.   }
  657.  
  658.   if (!retries) {
  659.     retries = TXTRIES;
  660.     return (2);
  661.   }
  662.  
  663.   errval = writecomm( (char *) &s_pkt, (crc != 0) ? 133 : 132 );
  664.   if (errval)
  665.     return(1);
  666.  
  667.   eat_noise();            /* clear out any garbage */
  668.   return (0);
  669. }
  670.  
  671. /* ----- Action Send_End:  Only way out of the Send state. --- */
  672. A_Send_End ( char *reason )
  673. {
  674.   char eotc = EOT;             /* just in case we really transmit the file */
  675.   int notdone = 1;             /* have we received an ACK to our EOT? */
  676.   int eotries = 10;            /* Should be enough for most */
  677.  
  678.   if (s_fptr != NULL) {        /* Did we even get started??? */
  679.     if (reason != eof_msg) {   /* Started, but bad news */
  680.       eotc = CAN;
  681.       writecomm(&eotc, 1);
  682.     }
  683.     else                       /* eof = We did it!  Send our EOT and get out */
  684.       while ( (notdone != 0) && (eotries--) ) {
  685.         writecomm(&eotc, 1);
  686.         notdone = A_Wait(ACK);
  687.       }
  688.     fclose(s_fptr);
  689.     Config_Comm( comport, prev_conf );   /* Put whatever parity back in */
  690.   }
  691.  
  692.   printf("\n *** Ending session.  %s.\a\n",reason);
  693.  
  694.   mode = M_Cmd;
  695.   return (SEND_EVENTS - 1);    /* last event always has next state S_Exit */
  696. }
  697.  
  698. /***************  S E N D   S T A T E    T A B L E  *******************/
  699.  struct event_entry send_machine[(int)S_Exit][SEND_EVENTS] =
  700.  { /* S_Init_Send */
  701.    { {  "fname O.K"  , A_Init_Wait    , CRC          , S_Sync_Wait     },
  702.      {  "fname bad"  , A_Get_Fname    , (int)s_fname , S_Init_Send     },
  703.      {  "user abort" , A_Send_End     , (int)user_msg, S_Exit          },
  704.      {  "no retries" , A_Send_End     , (int)nonak   , S_Exit          } },
  705.    /* S_Sync_Wait */
  706.    { {  "in sync"    , A_Make_Pkt     , INIT         , S_Make_Pkt      },
  707.      {  "unexpected" , A_Init_Wait    , NEXT         , S_Sync_Wait     },
  708.      {  "timeout"    , A_Init_Wait    , CRC          , S_Sync_Wait     },
  709.      {  "cancelled"  , A_Send_End     , (int)cancel  , S_Exit          } },
  710.    /* S_Make_Pkt */
  711.    { {  "pkt ready"  , A_Send_Pkt     , NEXT         , S_Send_Pkt },
  712.      {  "bad disk?"  , A_Send_End     , (int)badread , S_Exit          },
  713.      {  "done!"      , A_Send_End     , (int)eof_msg , S_Exit          },
  714.      {  "trouble!"   , A_Send_End     , (int)giveup  , S_Exit          } },
  715.    /* S_Send_Pkt */
  716.    { {  "sent O.K."  , A_Wait         , ACK          , S_Data_Response },
  717.      {  "comm error" , A_Send_Pkt     , RESEND       , S_Send_Pkt      },
  718.      {  "no retries" , A_Send_End     , (int)giveup  , S_Exit          },
  719.      {  "cancelled"  , A_Send_End     , (int)cancel  , S_Exit          } },
  720.    /* S_Data_Response */
  721.    { {  "ack rcvd."  , A_Make_Pkt     , NEXT         , S_Make_Pkt      },
  722.      {  "not ack"    , A_Send_Pkt     , NAK          , S_Send_Pkt      },
  723.      {  "timeout"    , A_Send_Pkt     , TIMEOUT      , S_Send_Pkt      },
  724.      {  "cancelled"  , A_Send_End     , (int)cancel  , S_Exit          } }
  725.  };
  726.  
  727.  
  728. /*  -------------------- Send state machine ------------------ */
  729. xmodem_send()
  730. {
  731.    char inkey;                     /* In case the user wants to abort */
  732.    int  event;                     /* event returned from action */
  733.    int  prevent;                   /* previous event */
  734.    struct event_entry *cur_entry;  /* pointer to current row/col of sm */
  735.    action new_action;              /* next action to perform */
  736.    enum send_state cur_state  = S_Init_Send;
  737.  
  738.    event = A_Get_Fname(s_fname);
  739.  
  740.    while (mode == M_XSend) {
  741.      prevent = event;      /* save the previous event for next state */
  742.      cur_entry = &send_machine[(int)cur_state][event];
  743.  
  744. #ifdef TRACE
  745.      printf("State: %16s, Event: %2d, Note: %20s\n",
  746.           state_list[(int)cur_state], event, cur_entry->comment );
  747. #endif
  748.  
  749.      /* Based on the current state and event, execute action(param) */
  750.      new_action = cur_entry->act;
  751.      event = new_action(cur_entry->param);
  752.      cur_state  = send_machine[(int)cur_state][prevent].next_state;
  753.  
  754.      if ( keyfun(KEYHIT) ) {            /* from CTERM */
  755.        inkey = (char) keyfun(READNEXT); /* Truncate high order */
  756.        if (inkey == eschar)
  757.          A_Send_End(user_msg);
  758.      }
  759.    }
  760.    return (0);
  761. }
  762.  
  763.  
  764. [LISTING FIVE]
  765.  
  766. /* CTERM1.C  by Donald W. Smith.  CIS 76515,3406.
  767.  *    A minimal terminal emulator to demonstrate the use of state
  768.  *  machine driven communications protocols using the C language.
  769.  *  Use makect1. to compile. */
  770.  
  771. #define VERSION fputs("\n\t CTERM 1.11:  4/26/89 DWS\n\n",stdout)
  772. #define BUFLEN   200
  773. #define LINELEN  80   /* Max user input length.  Lots of slack */
  774.  
  775. #include <conio.h>
  776. #include <ctype.h>              /* for Turbo C is... functions */
  777. #include <dos.h>
  778. #include <process.h>                      /* For system() call */
  779. #include <signal.h>          /* Ctrl-C and Ctrl-Break handling */
  780. #include <stdio.h>
  781. #include <stdlib.h>                       /* For system() call */
  782. #include "cterm.h"                 /* defines for cterm series */
  783. #include "commn.h"        /* defines shared by myint and cterm */
  784. #include "getargs.h"                     /* for getargs access */
  785.  
  786. #define CMDDIM(x)   (sizeof(x)/sizeof(x[0]))
  787.  
  788. /* --------- GLOBALS ------------------  */
  789. enum modes mode = M_Cmd;                 /* Term, Config, etc. */
  790. char            inbuf[BUFLEN+1];
  791. char            outbuf[LINELEN+1];    /* Used for short output */
  792. S_INIT          cur_config = {1200};    /* Current port config */
  793. int             comport = 1;       /* Current comm port number */
  794. int             bbsmode = 0;     /* BBS (8,1,N) or T16 (7,1,E) */
  795. int             eschar  = ESC;    /* keyboard escape character */
  796. FILE           *cap_fptr;         /* file ptr for capture file */
  797. static union    REGS rg;                  /* used for keyfun() */
  798.  
  799. /* ---------- External variables -------------- */
  800. extern  unsigned _heaplen = 4096;     /* TurboC 1.5 and above  */
  801.  
  802. /* ---------- External routines ----------- */
  803. /* -- From myint -- */
  804. extern void     Config_Comm( int port, S_INIT conf );
  805. extern S_INIT   Get_Conf( int port );
  806. extern int      incomm();
  807. extern int      Inst_IH(void interrupt (*faddr)(), int comnum);
  808. extern int      Remove_IH();
  809. extern int      writecomm(unsigned char *buf, int len);
  810. extern int      xmit_break();
  811. /* -- from xmutil -- */
  812. extern int           read_comm(int *num, char *buf, int wait);
  813. /* -- from object only files -- */
  814. extern int getargs( int, char**, ARG *, int, int (*usage)() );
  815.  
  816. ARG Argtab[] = {
  817.   {  'b', BOOLEAN,  &bbsmode,    "BBS mode (8,1,N) vs. T16)" },
  818.   {  'c', INTVAR,   &comport,           "1 = COM1, 2 = COM2" },
  819.   {  'e', INTVAR,   &eschar,            "Escape char (0x..)" },
  820.   {  's', INTVAR,   &cur_config.speed,        "speed in bps" } };
  821.  
  822. /* ----- fgetsnn:  Gets a line from file, replacing \n with NULL.
  823.  *       Return # chars, or EOF                                */
  824. int fgetsnn(FILE *fp, char *s, int size)
  825. {
  826.   register int i;
  827.   int c;
  828.  
  829.   for (i = 0; (i < size-1)             &&
  830.               (c = fgetc(fp)) != EOF   &&
  831.               (c != '\n')                 ; ++i)
  832.     s[i] = c;
  833.   s[i] = '\0';
  834.   if (c == EOF)
  835.     return(EOF);
  836.  
  837.   return(i);
  838. }
  839.  
  840. /* ----- capture_sw:  Enables saving sessions to (PC) disc. -----
  841.  *  Returns:  0 O.K.  1 if open fails, 2 if ESC hit.           */
  842. int capture_sw()
  843. {
  844.   static char cap_fname[NAMESIZE + 1] = "capture.fil";
  845.   char cap_temp[NAMESIZE + 1];
  846.   static int cap_sw = 0;              /* capture on/off switch */
  847.  
  848.   if (cap_sw == 0) {              /* Open the file for capture */
  849.     fprintf(stdout,"\n Capture to file <%s> or : ",cap_fname);
  850.     fgetsnn (stdin, cap_temp, NAMESIZE );
  851.     if (cap_temp[0] == eschar)
  852.       return(2);
  853.     if (cap_temp[0] != '\0')
  854.       strncpy(cap_fname, cap_temp, NAMESIZE);
  855.     if ( (cap_fptr = fopen (cap_fname, "a+t")) == NULL ) {
  856.       printf("\n Cannot open %s.  Try again.\n", cap_fname);
  857.       return(1);
  858.     }
  859.     cap_sw = 1;
  860.   }
  861.   else {       /* we are already capturing.  Close and get out */
  862.     fclose( cap_fptr );
  863.     cap_sw = 0;
  864.   }
  865.   return(0);
  866. }
  867.  
  868. /* ----- keyfun:  Use to call BIOS keyboard input services ------
  869.  *  Use instead of keypressed and bioskey to prevent DOS ^C's. */
  870. int keyfun(int serv)
  871. {
  872.   rg.h.ah = serv;
  873.   int86(BIOS_KEY, &rg, &rg);
  874.   if (serv == KEYHIT)
  875.     return ((rg.x.flags & 0x40) == 0);
  876.   else
  877.     return (rg.x.ax);
  878. }
  879.  
  880. /* ----- term:  Emulates a dumb terminal. -------------------- */
  881. void term()
  882. {
  883.   register int i;
  884.   int  keyin;                   /* Key = scan code + ASCII val */
  885.   char gochar;
  886.   int redd;
  887.   int ret_code,
  888.       wait_ret;
  889.   char *tail = inbuf;              /* for tail of input buffer */
  890.   static int cap_flag = 0;        /* Is capture turned on now? */
  891.  
  892.   while (mode == M_Term) {
  893.     redd = BUFLEN / 2;               /* Go for half at a time. */
  894.     ret_code = read_comm (&redd, inbuf, 10);       /* 10 msecs */
  895.     if ( (ret_code != 0) && (ret_code != TIMEOUT) )
  896.       fprintf (stderr, "read_comm error %x\n", ret_code );
  897.  
  898.     if ( redd > 0 ) {                /* Reading was productive */
  899.       tail[redd] = 0;                          /* plant a null */
  900.  
  901.       for ( i = 0; i < redd; i++) {      /* check for specials */
  902.         if ( isprint( gochar = inbuf[i] & 0x7F) ||  /* zero hi */
  903.            (    isspace(gochar)               ) ||  /* CR,LF.. */
  904.                 gochar == BS                           ) {
  905.            putch(gochar);
  906.            if (cap_flag)
  907.              fputc(gochar, cap_fptr);
  908.         }   /* printable test */
  909.       }   /* for loop */
  910.     }   /* end if reading was productive */
  911.  
  912.     if ( keyfun(KEYHIT) ) {
  913.       keyin = keyfun(READNEXT);    /* Retrieve Scan Code + Key */
  914.       gochar = keyin & 0xFF;                      /* truncates */
  915.       if (gochar == 0) {          /* Function key or a special */
  916.         switch (keyin) {
  917.           case PGUP: mode = M_XSend;
  918.                      xmodem_send();
  919.                      mode = M_Term;
  920.                      break;
  921.           case PGDN: mode = M_XRecv;
  922.                      xmodem_recv();
  923.                      mode = M_Term;
  924.                      break;
  925.           case CBRK: xmit_break();
  926.                      break;
  927.           case INS:  if (capture_sw() == 0)
  928.                        cap_flag = !cap_flag;
  929.           default:   break;
  930.         }
  931.       }
  932.       else {         /* Some plain old ascii character came in */
  933.         if ( gochar != eschar ) {
  934.           outbuf[0] = gochar;
  935.           writecomm(outbuf, 1);
  936.         }
  937.         else                                 /* ESCAPE entered */
  938.           mode = M_Cmd;                 /* leave terminal mode */
  939.       }
  940.     }  /* end if keypressed */
  941.   }    /*  while mode = M_Term */
  942.   return;
  943. }
  944.  
  945. /* ----- off_to_dos: Prompts for command to pass to dos. ----- */
  946. void off_to_dos()
  947. {
  948.   char buf[LINELEN];
  949.  
  950.   fputs("\nInput DOS Command (CR returns to menu)\nDOS>",stdout);
  951.  
  952.   while ( fgetsnn(stdin, buf, LINELEN) ) {  /* > 1 means got 1 */
  953.     system(buf);
  954.     fputs("\nDOS>",stdout);
  955.   }
  956. }
  957.  
  958. /* ----- config:  Prompts for new config (speed or default).-- */
  959. void config()
  960. {
  961.   S_INIT work;
  962.   char *cptr;
  963.   char buf[LINELEN];
  964.   unsigned inval;
  965.   int inlen;
  966.   int i = 0;
  967.  
  968.   work = Get_Conf(comport);             /* a struct assignment */
  969.  
  970.   fputs("\n Current config shows:\n", stdout);
  971.  
  972.   printf("%5u, parity %s, %s stops, %d bits/char.\n",
  973.     work.speed,
  974.     PARITY_LIST[work.ubits.lbits.parity],
  975.     STOP_LIST[work.ubits.lbits.two_stops],
  976.     work.ubits.lbits.wlen + 5);
  977.  
  978.   fputs("0 = T16 ( 7, 1, Even )\n", stdout);
  979.   fputs("1 = BBS ( 8, 1, None )\n", stdout);
  980.   fputs("other = new speed value\n", stdout);
  981.  
  982.   inlen = fgetsnn(stdin, buf, LINELEN);   /* Got one parameter */
  983.   if (inlen > 0) {
  984.     inval = atoi(buf);
  985.     if (inval == 0) {
  986.       work.ubits.lctrl = sev1even;                 /* 7,1,EVEN */
  987.       bbsmode = 0;
  988.     }
  989.     if (inval == 1) {
  990.       work.ubits.lctrl = ate1none;                 /* 8,1,NONE */
  991.       bbsmode = 1;
  992.     }
  993.     if (inval > 1) {
  994.       while ( SPEED_VALS[i] && SPEED_VALS[i] != inval )
  995.         i++;
  996.       if (SPEED_VALS[i] == 0)
  997.         printf("\n Speed %d unavailable.\n",inval);
  998.       else {   /* found a valid new speed */
  999.         work.speed = inval;
  1000.       }
  1001.     }
  1002.     Config_Comm(comport, work);
  1003.     cur_config = work;                  /* Publish the results */
  1004.   }
  1005.   else
  1006.     fputs("\n Exiting Config mode.\n",stdout);
  1007.  
  1008.   if ( inlen == (LINELEN - 1) )
  1009.     while (fgetc(stdin) != EOF)     /* Purge garbage in buffer */
  1010.       ;
  1011.  
  1012.   mode = M_Cmd;
  1013. }
  1014.  
  1015.  
  1016. /* ----- prompt_wait: Prompts with string, parses command. ------
  1017.  * Returns: The (int) index number of the command entered.     */
  1018. prompt_wait ( char *prompt, char *cmds[], int n, int help  )
  1019. {
  1020.   char buffer[LINELEN];    /* used by fgetsnn */
  1021.   int i = 0;
  1022.  
  1023.   while (i == 0) {              /* Don't bite on CR only input */
  1024.     printf("\n%s",prompt);
  1025.     i   = fgetsnn(stdin, buffer, LINELEN );
  1026.   }
  1027.  
  1028.   if ( i == (LINELEN - 1) )
  1029.     while (fgetc(stdin) != EOF)     /* Purge garbage in buffer */
  1030.       ;
  1031.  
  1032.   for ( i = 0 ; i < n ; i++ )
  1033.     if ( *cmds[i] == toupper(buffer[0]) )  /* Match first char */
  1034.       return(i);
  1035.   return (help);           /* not found... return help default */
  1036. }
  1037.  
  1038. /* ----- main_help:  Shows canned help message --------------- */
  1039. void main_help()
  1040. {
  1041. static char *details[] =
  1042.   { "Config  : Set up comm parameters.\n",
  1043.     "Dos,    : Calls DOS with commands.\n",
  1044.     "Help,   : See this help info.\n",
  1045.     "Quit    : Exit program.\n",
  1046.     "Rcvx,   : Receive file using Xmodem.\n",
  1047.     "Sendx,  : Send file using Xmodem.\n",
  1048.     "Term,   : Dumb terminal mode.\n"        };
  1049.  
  1050.   register int i;
  1051.  
  1052.   fputs("\n Valid commands are:\n",stdout);
  1053.   for (i = 0 ; i < CMDDIM(details) ; i++)
  1054.     printf("%s",details[i]);
  1055. }
  1056.  
  1057. /* ----- main_menu:  Prompts for input, dispatches if valid -- */
  1058. void main_menu()
  1059. {
  1060.   static char *prompt     = "CTERM>";
  1061.   static char *maincmds[] =
  1062.     { "CONFIG",
  1063.       "DOS",
  1064.       "HELP",             /* used for default below (index 2 ) */
  1065.       "QUIT",
  1066.       "RCVX",
  1067.       "SENDX",
  1068.       "TERM"   };
  1069.  
  1070.   while (mode == M_Cmd) {
  1071.     switch ( prompt_wait(prompt, maincmds, CMDDIM(maincmds), 2 ))
  1072.     {
  1073.        case 0:  mode = M_Config;
  1074.                 config();
  1075.                 break;
  1076.        case 1:  off_to_dos();
  1077.                 break;
  1078.        case 2:  main_help();
  1079.                 break;
  1080.        case 3:  printf("\n *** Closing Comm port %d.", comport);
  1081.                 close_comm();
  1082.                 exit(1);
  1083.        case 4:  mode = M_XRecv;
  1084.                 xmodem_recv();
  1085.                 fputs("\nReturned from xmodem recv!\n",stdout);
  1086.                 break;
  1087.        case 5:  mode = M_XSend;
  1088.                 xmodem_send();
  1089.                 fputs("\nReturned from xmodem send!\n",stdout);
  1090.                 break;
  1091.        case 6:  mode = M_Term;
  1092.                 eat_noise();
  1093.                 term();
  1094.                 break;
  1095.        default: main_help();
  1096.     }  /* end switch */
  1097.   }  /* end while */
  1098. }
  1099.  
  1100. /* ----- Catch_23:  Traps ^C + ^Break during user I/O. ------- */
  1101. void Catch_23()
  1102. {
  1103.   signal(SIGINT, Catch_23);      /*  Re-install self each time */
  1104.   return;
  1105. }
  1106.  
  1107. /* ----- Catch_24:  Traps Disk (Abort, Retry, Fail?) errors -- */
  1108. int Catch_24(int errval, int ax, int bp, int si)
  1109. {
  1110.   char msg[25];
  1111.   int drive;
  1112.  
  1113.   if (ax < 0) {          /* device error */
  1114.     bdosptr( 0x09, "device error$", 0);
  1115.     hardretn(-1);
  1116.   }
  1117.  
  1118.   drive = (ax & 0x00FF);
  1119.   sprintf(msg, "disc error on drive %c $", 'A' + drive);
  1120.   bdosptr( 0x09, msg, 0);
  1121.   hardretn(2);
  1122. }
  1123.  
  1124. /* ----- usage:  Give user quick help before exit. ----------- */
  1125. usage()
  1126. {
  1127.   printf("\n Defaults: T16, 1200 bps, 8,1,NONE, COM1, ESC.\n");
  1128. }
  1129.  
  1130. /* ----- main:  Gets the ball rolling!  ---------------------- */
  1131. main( int argc, char *argv[] )
  1132. {
  1133.   int error;
  1134.  
  1135.   VERSION;
  1136.   signal(SIGINT, Catch_23);
  1137.   harderr(Catch_24);
  1138.  
  1139.   argc = getargs( argc, argv, Argtab, CMDDIM(Argtab), usage );
  1140.  
  1141.   error = init_comm(comport, bbsmode);
  1142.   if (error != 0) {
  1143.     fprintf(stderr,"\n *** Comm Port %d Init FAILED!",comport);
  1144.     return(2);
  1145.   }
  1146.  
  1147.   fprintf(stderr,"\n *** Comm Port %d Init O.K. *** ", comport);
  1148.   main_menu();
  1149.   return(1);
  1150. }
  1151.  
  1152.  
  1153. [LISTING SIX]
  1154.  
  1155. #Make file for cterm series Turbo C.  1/23/89 DWS
  1156. #3/20/89:  Added getargs and stoi (.obj only) from Holub
  1157. #use Make -fmakect1
  1158. #Small memory model
  1159. MDL = s
  1160. LIB = c:\turboc\lib
  1161.  
  1162. #implicit rules
  1163. # To add debug info: TCC (-v), TLINK (/v)
  1164. .c.obj:
  1165.    tcc -c -m$(MDL) $<
  1166.  
  1167. #explicit rules
  1168. cterm1.exe: commint.obj xmsend.obj xmrecv.obj xmutil.obj cterm1.obj
  1169.      tlink  ..\lib\c0s cterm1 xmsend xmrecv xmutil commint getargs stoi, \
  1170.      cterm1, , ..\lib\cs
  1171.  
  1172. xmsend.obj:  xmsend.c  cterm.h  commn.h
  1173.  
  1174. xmrecv.obj:  xmrecv.c  cterm.h  commn.h
  1175.  
  1176. commint.obj: commint.c          commn.h  commint.h
  1177.  
  1178. xmutil.obj:  xmutil.c  cterm.h  commn.h  commint.h
  1179.  
  1180. cterm1.obj:  cterm1.c  cterm.h  commn.h
  1181.  
  1182. #end makefile
  1183.  
  1184.