home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / XBBS7200.ZIP / XBBS7200.TAR / sealink / sealink.c < prev   
Encoding:
C/C++ Source or Header  |  1989-03-04  |  31.1 KB  |  1,080 lines

  1. /*****************************************************************************
  2.  *    SEAlink - Sliding window file transfer protocol
  3.  *
  4.  *    @(#) sealink.c 2.9 89/03/02 
  5.  *    UNIX SVR2 and BSD versions by Scott Reynolds
  6.  *    uucp: clmqt!scott
  7.  *
  8.  *    additional SysV modifications by Sanford Zelkovitz, without whose
  9.  *    help this couldn't have been accomplished
  10.  *
  11.  *    Based on:
  12.  *    MS-DOS Version 1.20, created on 08/05/87 at 17:51:40
  13.  *   (C)COPYRIGHT 1986, 87 by System Enhancement Associates ALL RIGHTS RESERVED
  14.  *    By:  Thom Henderson
  15.  *
  16.  *    Mr. Henderson had no hand in this UNIX port; please don't bother
  17.  *    him with questions about this program!
  18.  *
  19.  *    Description:
  20.  *
  21.  *    The intent of SEAlink is to provide a file transfer protocol that
  22.  *    does not suffer from propagation delays, such as are introduced
  23.  *    by satellite relays or packet switched networks.
  24.  ****************************************************************************/
  25.  
  26. /*
  27.  * The following flags are for compiling on different systems.
  28.  */
  29. #define SYSV            /* Compile for SYS V i/o calls    */
  30. /* #define BSD            /* Compile for BSD i/o calls    */
  31.  
  32. /*
  33.  * Define NO_MEM if your system doesn't have a
  34.  * working memset() function
  35.  */
  36. /* #define NO_MEM        /* memset() doesn't work    */
  37.  
  38. /*
  39.  * Define NO_NAP if there is no nap() function.
  40.  *
  41.  * If you use nap() be aware that it causes a MUCH greater load
  42.  * on the processor.
  43.  *
  44.  * With NO_NAP defined the XModem compatibility is reduced; if
  45.  * this is not a concern, define this for greater efficiency.
  46.  */
  47. #define NO_NAP            /* nap() doesn't work        */
  48.  
  49. /*
  50.  * Define CRCTABLE to use the fast table lookup CRC calculation;
  51.  * a slower calculation-based method can be compiled to reduce the
  52.  * amount of process memory required by commenting this out.
  53.  */
  54. #define    CRCTABLE        /* use CRC lookup table        */
  55.  
  56. /*
  57.  * The section of code that is compiled when NAKEOT is defined is in the
  58.  * original MS-DOS version 1.16 code.  Its purpose is to send a NAK when
  59.  * an EOT is received during rcvfile(), apparently to confirm that this is
  60.  * indeed the end of file.  However, in certain (apparently non-standard)
  61.  * versions of the protocol, it is possible that the program will report an
  62.  * error when in fact there isn't one.  Comment this out at your discretion.
  63.  */
  64. #define NAKEOT                /* define to NAK EOT's        */
  65.  
  66. #include <stdio.h>
  67. #include <string.h>
  68. #include <ctype.h>
  69. #include <signal.h>
  70. #include <setjmp.h>
  71. #include <sys/ioctl.h>
  72. #include <sys/types.h>
  73. #include <sys/stat.h>
  74. #include <time.h>
  75. #ifdef    SYSV            /* use System V I/O control    */
  76. #include <termio.h>
  77. #endif    /* SYSV */
  78. #ifdef    BSD            /* use BSD I/O control        */
  79. #include <sgtty.h>
  80. #endif    /* BSD */
  81. #ifndef    NO_MEM
  82. #include <memory.h>
  83. #endif    /* NO_MEM */
  84.  
  85. /* Various system constants */
  86. #define    WINDOW    6            /* maximum size of window    */
  87. #define    S_NAK    0            /* NAK condition for sendack()    */
  88. #define    S_ACK    1            /* ACK condition for sendack()    */
  89. #define    NONE    0            /* neither send nor receive    */
  90. #define    SEND    1            /* send mode            */
  91. #define    RECV    2            /* receive mode            */
  92. #define    TENYEAR    (time_t) 315532800L    /* GMT offset for 1970 <-> 1980    */
  93.  
  94. /* SEAlink block zero data structure */
  95. struct zeros
  96. {   
  97.     long    flen;            /* file length            */
  98.     time_t    fstamp;            /* file date/time stamp        */
  99.     char    fnam[17];        /* original file name        */
  100.     char    prog[15];        /* sending program name        */
  101.     char    noacks;            /* true if ACKing not required    */
  102.     char    fill[87];        /* reserved for future use    */
  103. };
  104.  
  105. /* ASCII mnemonic values */
  106. #define    ACK    0x06
  107. #define    NAK    0x15
  108. #define    SOH    0x01
  109. #define    EOT    0x04
  110. #define    CAN    0x18
  111.  
  112. static int    outblk;            /* number of next block to send    */
  113. static int    ackblk;            /* number of last block ACKed    */
  114. static int    blksnt;            /* number of last block sent    */
  115. static int    slide;            /* true if sliding window    */
  116. static int    ackst;            /* ACK/NAK state        */
  117. static int    numnak;            /* number of sequential NAKs    */
  118. static int    chktec;            /* 1 = CRC, 0 = checksum        */
  119. static int    toterr;            /* total number of errors    */
  120. static int    ackrep;            /* true when ACK, NAK reported    */
  121. static int    ackseen;        /* count of sliding ACKs seen    */
  122. static int    xferdone = 0;        /* done with transfer (recvr)    */
  123. static int    debug = 0;        /* debugging flag        */
  124. static int    sld_flag = 1;        /* sliding windows allowed    */
  125. static int    ackless = 1;        /* true if ACKs not required    */
  126. static char    outb[133];        /* block to use for output    */
  127.  
  128. /* the program name MUST be 14 characters plus a '\0' terminator */
  129. #ifdef    SYSV
  130. char    progname[15] = "SEAlink/SYSV  ";
  131. #endif    /* SYSV */
  132. #ifdef    BSD
  133. char    progname[15] = "SEAlink/BSD   ";
  134. #endif    /* BSD */
  135.  
  136. /* the debug filename is a local preference */
  137. char    *dbugfile = "/tmp/sealink.log";
  138.  
  139.  
  140. #ifdef    NO_NAP
  141. /*
  142.  * Need this to do a (very) rough approximation of nap().
  143.  * Used by alarm() in com_getc()
  144.  */
  145. jmp_buf    tohere;
  146. #endif
  147.  
  148. /* CRC computation logic */
  149. #ifdef    CRCTABLE
  150. unsigned short crc_tab[256] =         /* CRC lookup table */
  151. {
  152.     0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 
  153.     0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 
  154.     0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 
  155.     0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 
  156.     0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 
  157.     0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 
  158.     0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 
  159.     0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 
  160.     0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 
  161.     0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 
  162.     0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 
  163.     0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 
  164.     0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 
  165.     0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 
  166.     0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 
  167.     0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 
  168.     0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 
  169.     0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 
  170.     0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 
  171.     0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 
  172.     0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 
  173.     0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 
  174.     0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 
  175.     0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 
  176.     0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 
  177.     0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 
  178.     0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 
  179.     0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 
  180.     0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 
  181.     0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 
  182.     0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 
  183.     0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
  184. };
  185.  
  186. /*
  187.  * crc_update performs CRC calculation using crc_tab[].
  188.  * Note:  Don't need to "flush" with zeroes with this formula.
  189.  */
  190. #define    crc_update(CRC, C)    ((CRC << 8) ^ crc_tab[(CRC >> 8)^C])
  191. #define    crc_finish(CRC)        (CRC)
  192.  
  193. #else    /* otherwise, don't use CRC table */
  194.  
  195. unsigned short crc_update(crc, c)    /* calculate a CRC value */
  196. register unsigned    crc;
  197. register int    c;
  198. {
  199.     int    count;
  200.  
  201.     for (count = 8; --count >= 0;) {
  202.         if (crc & 0x8000) {
  203.             crc <<= 1;
  204.             crc += (((c <<= 1) & 0400)   !=  0);
  205.             crc ^ = 0x1021;
  206.         } else {
  207.             crc <<= 1;
  208.             crc += (((c <<= 1) & 0400)   !=  0);
  209.         }
  210.     }
  211.     return crc;
  212. }
  213.  
  214. /* finish CRC calculation by "flushing" with zeroes */
  215. #define crc_finish(C)    crc_update(crc_update(C, 0), 0)
  216. #endif
  217.  
  218. unsigned    alarm();
  219. void        sendabort();
  220. void        shipblk();
  221.  
  222. #ifdef    NO_MEM
  223. char        *memset();
  224. #endif    /* NO_MEM */
  225.  
  226. main(argc, argv)
  227. int    argc;
  228. char    *argv[];
  229. {
  230.     int        c;        /* used to get options */
  231.     int        mode = NONE;    /* SEND, RECV files */
  232.     int        noerr;        /* no error in transmission */
  233.     char        *fn;        /* current filename to send/recv */
  234.     int        getopt();
  235.     unsigned    sleep();
  236.     int        xmtfile();
  237.     char        *rcvfile();
  238.     extern int    opterr;        /* used by getopt() */
  239.     extern int    optind;        /*        "         */
  240.     extern char    *optarg;    /*        "         */
  241. #ifdef    SYSV
  242.     struct termio    oldtty, tty;
  243. #endif /* SYSV */
  244. #ifdef    BSD
  245.     struct sgttyb    oldtty, tty;
  246. #endif /* BSD */
  247.  
  248.     mode = 0;
  249.     fn = NULL;
  250.     opterr = 0;
  251.     while ((c = getopt(argc, argv, "dfors:")) != EOF) {
  252.         switch (c) {
  253.         case 'd':
  254.             debug = 1;    /* use debug file */
  255.             break;
  256.         case 'f':
  257.             sld_flag = 0;    /* no sliding window */
  258.             break;
  259.         case 'o':
  260.             ackless = 0;    /* no overdrive mode */
  261.             break;
  262.         case 's':
  263.             mode = SEND;
  264.             fn = optarg;
  265.             break;
  266.         case 'r':
  267.             mode = RECV;
  268.             break;
  269.         default:
  270.             mode = NONE;
  271.             break;
  272.         }
  273.     }
  274.     switch (mode) {
  275.     case RECV:
  276.         fputs("sealink: ready to receive\n", stderr);
  277.         break;
  278.     case SEND:
  279.         fputs("sealink: ready to send\n", stderr);
  280.         break;
  281.     default:
  282.         if (debug)
  283.             printf("%s 2.9 sealink.c 89/03/02\n\n", progname);
  284.         printf("   SEAlink sliding window file transfer protocol\n");
  285.         printf("v1.20  (C) 1986, 1987 System Enhancement Associates\n");
  286.         printf("  ALL RIGHTS RESERVED   written by Thom Henderson\n");
  287.         printf("      UNIX version written by Scott Reynolds\n\n");
  288.         printf("Usage: sealink -[dfo]s filename...\n");
  289.         printf("       sealink -[dfo]r [filename...]\n");
  290.         printf("Options:\n");
  291.         printf("  -d                Debug output to temporary file\n");
  292.         printf("  -f                Force no sliding window\n");
  293.         printf("  -o                Shut down overdrive mode\n");
  294.         printf("  -s filename...    Send the file(s) specified\n");
  295.         printf("  -r [filename...]  Receive file(s)\n");
  296.         exit(1);
  297.     }
  298.     if (debug) {
  299.         time_t    tim, time();
  300.         char    *ctime();
  301.  
  302.         printf("Sending debug output to %s\n", dbugfile);
  303.         freopen(dbugfile, "a", stderr); /* open log file */
  304.         setbuf(stderr, NULL);
  305.         (void) time(&tim);
  306.         fputs(ctime(&tim), stderr);
  307.     }
  308.     fflush(stdout);            /* flush output before anything else */
  309.  
  310. #ifdef    SYSV
  311.     (void) ioctl(0, TCGETA, &oldtty);    /* get terminal parameters */
  312.     if (debug)
  313.         fputs("tty parameters read\n", stderr);
  314.     tty = oldtty;            /* copy them, then set new */
  315.     tty.c_iflag = IGNBRK;        /* No input filter; ignore break */
  316.     tty.c_oflag = 0;            /* Use transparent output */
  317.     tty.c_lflag &= ~(ECHO|ICANON|ISIG);    /* disable echo, signals */
  318.     tty.c_cc[VMIN] = 0;        /* AT&T Sys V: return immediately */
  319.     tty.c_cc[VTIME] = 0;        /*   if no characters can be read */
  320.     tty.c_cflag &= ~PARENB;        /* Leave baud rate, disable parity */
  321.     tty.c_cflag &= ~CSIZE;        /* reset data bits */
  322.     tty.c_cflag |= CS8;        /* set 8 bit data */
  323.     (void) ioctl(0, TCSETAW, &tty);    /* go after setting terminal */
  324. #endif /* SYSV */
  325. #ifdef BSD
  326.     (void) ioctl(0, TIOCGETP, &oldtty);    /* get terminal parameters */
  327.     if (debug)
  328.         fputs("tty parameters read\n", stderr);
  329.     tty = oldtty;            /* copy them, then set new */
  330.     tty.sg_flags = RAW;        /* raw mode (8 bit, no processing */
  331.     (void) ioctl(0, TIOCSETP, &tty);    /* go after setting terminal */
  332. #endif /* BSD */
  333.     if (debug)
  334.         fputs("tty parameters set\n", stderr);
  335.  
  336.     if (mode == SEND) {
  337.         do {
  338.             if (noerr = xmtfile(fn))
  339.                 sleep(2);    /* wait a few before next */
  340.             if (optind < argc)
  341.                 fn = argv[optind++];
  342.             else
  343.                 fn = NULL;
  344.         } while (noerr && fn  != NULL);
  345.         if (noerr)        /* no errors, send end marker */
  346.             (void) xmtfile("");
  347.     } else {
  348.         do {
  349.             if (optind < argc)    /* if filename given, use it */
  350.                 fn = argv[optind++];
  351.             else        /* otherwise get one from remote */
  352.                 fn = "";
  353.             if (noerr = (rcvfile(fn)  != NULL))
  354.                 sleep(2);    /* wait a few before next */
  355.         } while (noerr);    /* go until done/err */
  356.         noerr = xferdone;    /* set no error if done */
  357.     }
  358.  
  359. #ifdef    SYSV
  360.     (void) ioctl(0, TCSBRK, 1);    /* Wait for output to drain */
  361.     (void) ioctl(0, TCFLSH, 2);    /* Flush input queue */
  362.     (void) ioctl(0, TCSETAW, &oldtty);    /* Restore original modes */
  363.     (void) ioctl(0, TCXONC, 1);    /* Restart output */
  364. #endif /* SYSV */
  365. #ifdef    BSD
  366.     (void) ioctl(0, TIOCSETP, &oldtty);
  367. #endif /* BSD */
  368.     if (debug) {
  369.         fputs("tty parameters restored\n", stderr);
  370.         (void) fclose(stderr);
  371.     }
  372.  
  373.     exit(!noerr);            /* and return error status */
  374.     /*NOTREACHED*/
  375. }
  376.  
  377. /*
  378.  * chkout() returns non-zero if stdout and stderr are sending to
  379.  * different files/devices, zero if the same
  380.  */
  381. int chkout()
  382. {
  383.     struct stat    so, se;
  384.  
  385.     (void)fstat(1, &so);
  386.     (void)fstat(2, &se);
  387.     return (so.st_rdev  != se.st_rdev);
  388. }
  389.  
  390. #ifdef    NO_NAP
  391. /*
  392.  * alarmint() is called when an alarm signal is caught.
  393.  */
  394. int alarmint()
  395. {
  396.     longjmp(tohere, EOF);        /* return EOF to indicate timeout */
  397. }
  398. #endif    /* NO_NAP */
  399.  
  400. /*
  401.  * com_getc(timeout) reads a character from file descriptor 0
  402.  * timeout is in tenths of seconds
  403.  * EOF returned if no character was available to read
  404.  *
  405.  * If timeout is 0, this routine will return immediately regardless of
  406.  * the status of the read.  If timeout > 0, there will be a minimum of
  407.  * one to two seconds before returning if nap() does not work.
  408.  */
  409. int com_getc(timeout)
  410. register int    timeout;
  411. {
  412.     static char    byt[2];        /* buffer to read characters into  */
  413.  
  414. #ifdef    BSD
  415.     if (!timeout) {            /* if no timeout then no alarms       */
  416.         long        len;    /* number of buffered characters   */
  417.  
  418.         (void) ioctl(0, FIONREAD, &len);    /* check buffer       */
  419.                     /* read character if available       */
  420.         if (len > 0L && read(0, byt, 1) == 1)
  421.             return (byt[0] & 0377);    /* return the character       */
  422.         return EOF;        /* error or none available       */
  423.     }
  424. #endif /* BSD */
  425. #ifdef    SYSV
  426.     if (!timeout) {            /* if no timeout then no alarms       */
  427.         if (read(0, byt, 1) == 1)    /* if character was read,  */
  428.             return (byt[0] & 0377);    /* return the character       */
  429.         return EOF;        /* error or none available       */
  430.     }
  431. #endif /* SYSV */
  432.  
  433. #ifdef    NO_NAP
  434. /* There's a timeout value, so now we get to use alarm() */
  435.     timeout = ((timeout-1)/10)+1;    /* round to seconds           */
  436.     if (timeout == 1)        /* minimum of 2 seconds for alarm, */
  437.         timeout++;        /*   since 1 may not be any delay  */
  438.     if (setjmp(tohere)) {        /* if the alarm went off       */
  439.         if (debug)        /* timeout message if debugging       */
  440.             fputs("Read: timeout\n", stderr);
  441.         return EOF;        /* return EOF (longjmp call)       */
  442.     }
  443.     signal(SIGALRM, alarmint);    /* set up alarm signal catching       */
  444.     alarm((unsigned)timeout);    /* set alarm time           */
  445.     while (read(0, byt, 1) != 1)    /* Go until we read a character       */
  446.         ;            /*   (or the alarm goes off!)       */
  447.     alarm(0);            /* reset alarm               */
  448.     signal(SIGALRM, SIG_DFL);    /* and turn off signal catching       */
  449.     return (byt[0] & 0377);        /* return the character           */
  450. #else    /* NO_NAP undefined -- nap() works */
  451.     do {
  452.         if (read(0, byt, 1) == 1)    /* did we read a char?       */
  453.             return (byt[0] & 0377);    /* yes, return it       */
  454.         (void) nap(100L);    /* sleep for a little while       */
  455.     } while (--timeout);        /* loop until time runs out       */
  456.     if (debug)            /* timeout message if debugging       */
  457.         fputs("Read: timeout\n", stderr);
  458.     return EOF;
  459. #endif    /* NO_NAP */
  460. }
  461.  
  462. /*  File transmitter logic */
  463.  
  464. int xmtfile(name)            /* transmit a file */
  465. char *name;                /* name of file to send */
  466. {
  467.     FILE    *f, *fopen();        /* file to send */
  468.     int    endblk;            /* block number of EOT */
  469.     struct stat    fst;        /* data about file */
  470.     struct zeros    zero;        /* block zero data */
  471.     char    *basename;        /* base filename */
  472.  
  473.     if (name && *name) {        /* if sending a file */
  474.         if ((char *)(f = fopen(name, "r")) == NULL) {
  475.             fprintf(stderr, "Can't read %s\n", name);
  476.             return 0;
  477.         }
  478.  
  479.         memset((char *)&zero, 0, sizeof(zero)); /* clear data block */
  480.  
  481.         tzset();
  482.         stat(name, &fst);        /* get file information    */
  483.         zero.flen = (long)fst.st_size;
  484.         zero.fstamp = fst.st_mtime - timezone;    /* adjust f/TZ    */
  485.         if (daylight)            /* if daylight savings,    */
  486.             zero.fstamp -= 3600L;    /* subtract an hour    */
  487.         if (zero.fstamp < 0L)
  488.             zero.fstamp = (time_t) 0;
  489.         if ((basename = strrchr(name, '/')) == NULL) {
  490.             strcpy(zero.fnam, name);
  491.         } else {
  492.             basename++;
  493.             strcpy(zero.fnam, basename);
  494.         }
  495.         if (debug)
  496.             fprintf(stderr, "basename: %s\n", zero.fnam);
  497.         strcpy(zero.prog, progname);
  498.         zero.noacks = ackless;
  499.  
  500.         endblk = (int)((zero.flen+127L)/128L)+1;
  501.     } else {
  502.         endblk = 0;        /* fake for no file */
  503.         if (debug)
  504.             fputs("send transfer complete\n", stderr);
  505.     }
  506.  
  507.     outblk = 1;            /* set starting state */
  508.     ackblk = (-1);
  509.     blksnt = slide = ackst = numnak = toterr = ackrep = ackseen = 0;
  510.     chktec = 2;            /* undetermined */
  511.  
  512.     while (ackblk < endblk) {    /* while not all there yet */
  513.         if (outblk <= ackblk + ((slide && sld_flag)? WINDOW : 1)) {    
  514.             if (outblk < endblk) {    
  515.                 if (outblk > 0)
  516.                     sendblk(f, outblk);
  517.                 else
  518.                     shipblk((unsigned char *)&zero, 0);
  519.                 if (ackless && slide && sld_flag)
  520.                     ackblk = outblk;
  521.             } else if (outblk == endblk) {    
  522.                 outb[0] = EOT;
  523.                 write(1, outb, 1);
  524.                 if (debug)
  525.                     fputs("sent EOT\n", stderr);
  526.             }
  527.             outblk++;
  528.         }
  529.  
  530.         ackchk();
  531.         if (numnak>10)
  532.             goto abort;
  533.     }
  534.  
  535.     if (endblk)
  536.         (void) fclose(f);
  537.     if (debug && toterr>2)
  538.         fprintf(stderr, "%d errors/%d blocks\n", toterr, blksnt);
  539.     return 1;                          /* exit with good status */
  540.  
  541. abort:
  542.     if (endblk)
  543.         (void) fclose(f);
  544.     if (debug) {
  545.         fputs("TRANSMIT ABORTED\n", stderr);
  546.         if (toterr)
  547.             fprintf(stderr, "%d errors/%d blocks\n", toterr, blksnt);
  548.     }
  549.     sendabort();
  550.     return 0;                          /* exit with bad status */
  551. }
  552.  
  553. /*
  554.  * The various ACK/NAK states are:
  555.  *  0:   Ground state, ACK or NAK expected.
  556.  *  1:   ACK received
  557.  *  2:   NAK received
  558.  *  3:   ACK, block# received
  559.  *  4:   NAK, block# received
  560.  *  5:   Returning to ground state
  561.  */
  562. static ackchk()                /* check for ACK or NAK */
  563. {
  564.     int    c;            /* one byte of data */
  565.     static int    rawblk;        /* raw block number */
  566.  
  567.     ackrep = 0;            /* nothing reported yet */
  568.  
  569.     while ((c = com_getc(0))  != EOF) {
  570.         if (c == CAN) {                /* CANcel received? */
  571.             if ((c = com_getc(20)) == CAN) {    /* get two  */
  572.                 numnak = 11;        /* OK, let's abort! */
  573.                 ackst = 0;
  574.                 if (debug)
  575.                     fputs("received cancel\n", stderr);
  576.             }
  577.             return;                /* break out of here */
  578.         }
  579.         if (ackst == 3 || ackst == 4) {        /* Windowed ACK/NAK */
  580.             slide = 0;    /* assume this will fail */
  581.                     /* see if we believe the number */
  582.             if (rawblk == (c^0xff)) {
  583.                 rawblk = outblk - ((outblk-rawblk)&0xff);
  584.                 if (rawblk >= 0 && rawblk <= outblk
  585.                         && rawblk > outblk-128) {
  586.                     /* we have sliding window! */
  587.                     if (ackst == 3) {
  588.                         ackblk = ackblk > rawblk ? ackblk : rawblk;
  589.                         slide = 1;
  590.                         if (ackless && ++ackseen>10) {
  591.                             ackless = 0;
  592.                             if (debug)
  593.                                 fputs("- Overdrive disengaged\n", stderr);
  594.                         }
  595.                     } else {    
  596.                         outblk = rawblk<0? 0 : rawblk;
  597.                         slide = numnak<4;
  598.                     }
  599.                     if (debug)
  600.                         fprintf(stderr, "%s %d == \n", ackst == 3?"ACK":"NAK", rawblk);
  601.                     ackrep = 1;  /* we reported something */
  602.                 }
  603.             }
  604.             ackst = 5;    /* return to ground state */
  605.         }
  606.  
  607.         if (ackst == 1 || ackst == 2) {    
  608.             rawblk = c;
  609.             ackst += 2;
  610.         }
  611.  
  612.         if (!slide || ackst == 0) {    
  613.             if (c == ACK) {    
  614.                 if (!slide) {    
  615.                     ackblk++;
  616.                     if (debug)
  617.                         fprintf(stderr, "ACK %d --\n", ackblk);
  618.                     ackrep = 1;    /* reported an ACK */
  619.                 }
  620.                 ackst = 1;
  621.                 numnak = 0;
  622.             } else if (c == 'C' || c == NAK) {    
  623.                 /* if method not determined yet */
  624.                 if (chktec>1)    /* then do what rcver wants */
  625.                     chktec = (c == 'C');
  626. #ifdef    SYSV
  627.                 (void) ioctl(0, TCFLSH, 1); /* purge output */
  628. #endif /* SYSV */
  629. #ifdef    BSD
  630. /* for now this code is commented out.  It causes more complications to have
  631.  * it installed -- my wish list would include for a BSD ioctl() to purge only
  632.  * the output, but at the moment things will just have to stay this way. */
  633. /*                (void) ioctl(0, TIOCFLUSH, 0); /* purge i/o */
  634. #endif /* BSD */
  635.                 if (!slide) {    
  636.                     outblk = ackblk+1;
  637.                     if (debug)
  638.                         fprintf(stderr, "NAK %d --\n", ackblk+1);
  639.                     ackrep = 1;    /* reported a NAK */
  640.                 }
  641.                 ackst = 2;
  642.                 numnak++;
  643.                 if (blksnt)
  644.                     toterr++;
  645.             }
  646.         }
  647.         if (ackst == 5)
  648.             ackst = 0;
  649.     }
  650. }
  651.  
  652. static sendblk(f, blknum)        /* send one block */
  653. FILE *f;                /* file to read from */
  654. int blknum;                                /* block to send */
  655. {
  656.     long        blkloc;        /* address of start of block */
  657.     unsigned char    buf[128];    /* one block of data */
  658.  
  659.     if (blknum  != blksnt+1) {    /* if jumping */
  660.         blkloc = (long)(blknum-1) * 128L;
  661.         fseek(f, blkloc, 0);    /* move where to */
  662.     }
  663.     blksnt = blknum;
  664.  
  665.     memset(buf, 26, 128);        /* fill buffer with control Zs */
  666.     fread(buf, 1, 128, f);        /* read in some data */
  667.     shipblk(buf, blknum);        /* pump it out to the receiver */
  668. }
  669.  
  670. static void shipblk(blk, blknum)    /* physically ship a block */
  671. unsigned char    *blk;            /* data to be shipped */
  672. int    blknum;                /* number of block */
  673. {
  674.     register unsigned short    crc = 0;    /* CRC check value */
  675.     register int    n;        /* index */
  676.     unsigned char    *b = blk;    /* data pointer */
  677.  
  678.     outb[0] = SOH;            /* block header */
  679.     outb[1] = blknum;            /* block number */
  680.     outb[2] = blknum^0xff;        /* block number check value */
  681.  
  682.     for(n = 0;n < 128;n++) {    /* ship the data */
  683.         if (chktec)
  684.             crc = crc_update(crc, *b);
  685.         else
  686.             crc += *b;
  687.         outb[n+3] = (*b++);
  688.     }
  689.     crc = crc_finish(crc);
  690.  
  691.     if (chktec) {            /* send proper check value */
  692.         outb[131] = crc>>8;
  693.         outb[132] = crc&0xff;
  694.         write(1, outb, 133);
  695.     } else {
  696.         outb[131] = crc&0xff;
  697.         write(1, outb, 132);
  698.     }
  699.  
  700.     if (debug)
  701.         fprintf(stderr, "sent block %d\n", blknum);
  702.  
  703.     return;
  704. }
  705.  
  706. /*  File receiver logic */
  707.  
  708. char *rcvfile(name)            /* receive file */
  709. char    *name;                /* name of file */
  710. {
  711.     int    c;            /* received character        */
  712.     int    tries;            /* retry counter        */
  713.     int    blknum;            /* desired block number        */
  714.     int    inblk;            /* this block number        */
  715.     FILE    *f;            /* file to receive to        */
  716.     char    buf[128];        /* data buffer            */
  717.     char    tmpname[100];        /* name of temporary file    */
  718.     static char    outname[100];    /* name of final file        */
  719.     struct zeros    zero;        /* file header data storage    */
  720.     int    endblk;            /* block number of EOT if known    */
  721.     long    left;            /* bytes left to output        */
  722.     int    getblock();        /* block receiver, status    */
  723.     int    fcopy();        /* copy source file to dest    */
  724.     void    setstamp();        /* set date/time stamp of file    */
  725.     int    cnvrt;            /* flag -- convert filename?    */
  726.     char    *onp;            /* use to convert filename to l/c */
  727.  
  728.     if (name && *name) {        /* figure out a name to use     */
  729.         strcpy(outname, name);    /* user supplied one         */
  730.         cnvrt = 0;        /* no conversion should be done     */
  731.     } else {    
  732.         *outname = '\0';    /* get name from transmitter     */
  733.         cnvrt = 1;        /* convert to local is necessary */
  734.     }
  735.  
  736.     strcpy(tmpname, ".sl.rcv.XXXXXX");    /* template for mktemp() */
  737.     mktemp(tmpname);        /* use a unique temp filename     */
  738.     if (debug)
  739.         fprintf(stderr, "tmpname: %s\n", tmpname);
  740.  
  741.     if (*outname && (f = fopen(outname, "r"))) {    /* open output file */
  742.         (void) fclose(f);
  743.         if (!(f = fopen(outname, "r+"))) {
  744.             if (debug)
  745.                 fprintf(stderr, "Cannot write %s\n", tmpname);
  746.             sendabort();
  747.             return NULL;
  748.         } else {
  749.             (void) fclose(f);
  750.         }
  751.     }
  752.     if (!(f = fopen(tmpname, "w"))) {    /* open temporary file */
  753.         if (debug)
  754.             fprintf(stderr, "Cannot create %s\n", tmpname);
  755.         sendabort();
  756.         return NULL;
  757.     }
  758.  
  759.     blknum = *outname ? 1 : 0;    /* first block we must get */
  760.     tries = -10;            /* kludge for first time around */
  761.     chktec = 1;            /* try for CRC error checking */
  762.     toterr = 0;            /* no errors yet */
  763.     endblk = 0;            /* we don't know the size yet */
  764.     ackless = 0;            /* we don't know about this yet */
  765.     memset((char *)&zero, 0, sizeof(zero));    /* or much of anything else */
  766.  
  767. /*
  768.     if (com_scan() == EOF)        /+ kludge for adaptive Modem7 +/
  769.         goto nextblock;
  770. */
  771. nakblock:                /* we got a bad block */
  772.     if (blknum>1)
  773.         toterr++;
  774.     if (++tries>10)
  775.         goto abort;
  776.     if (tries == 0)            /* if CRC isn't going */
  777.         chktec = 0;        /* then give checksum a try */
  778.  
  779.     sendack(S_NAK, blknum);        /* send the NAK */
  780.     if (ackless && toterr > 20) {    /* if ackless mode isn't working */
  781.         ackless = 0;        /* then shut it off */
  782.         if (debug)
  783.             fputs("- Overdrive disengaged\n", stderr);
  784.     }
  785.     goto nextblock;
  786.  
  787. ackblock:                /* we got a good block */
  788. nextblock:                /* start of "get a block" */
  789.     while ((c = com_getc(30))  != EOF) {
  790.         if (c == CAN)
  791.             if ((c = com_getc(30)) == CAN) {
  792.                 sendabort();
  793.                 return NULL;
  794.             } else
  795.                 break;
  796.         if (c == EOT) {    
  797.             if (!endblk || endblk == blknum)
  798.                 goto endrcv;
  799.         } else if (c == SOH) {    
  800.             if ((inblk = com_getc(5)) == EOF)
  801.                 goto nakblock;
  802.             if (com_getc(5) == (inblk^0xff)) { 
  803.                 if (debug)
  804.                     fprintf(stderr, "received #%d\n", inblk);
  805.                 goto blockstart;    /* we found a start */
  806.             }
  807.         }
  808.     }
  809.     goto nakblock;
  810.  
  811. blockstart:                /* start of block detected */
  812.     c = blknum&0xff;
  813.     if (inblk == 0 && blknum <= 1) {    /* if this is the header */
  814.         if (!getblock((char *)&zero)) {    
  815.             sendack(S_ACK, inblk);    /* ack the header */
  816.             if (!*name)    /* given name takes precedence */
  817.                 strcpy(outname, zero.fnam);
  818.             if (left = zero.flen)    /* length to transfer */
  819.                 endblk = (int)((left+127L)/128L)+1;
  820.             if (ackless != zero.noacks && debug)
  821.                 fprintf(stderr, "+ Overdrive %sengaged\n", 
  822.                     zero.noacks?"":"dis");
  823.             ackless = zero.noacks;
  824.  
  825.             blknum = 1;    /* now we want first data block */
  826.             goto ackblock;
  827.         } else {
  828.             goto nakblock;        /* bad header block */
  829.         }
  830.     } else if (inblk == c) {        /* if this is the one we want */
  831.         if (!getblock(buf)) {        /* else if we get it okay */
  832.             if (!ackless)        /* if we're sending ACKs */
  833.                 sendack(S_ACK, inblk);    /* then ACK the data */
  834.     /*
  835.      * if file size is not known or more than 128 bytes
  836.      * to go, write one block (128 bytes) to the file
  837.      */
  838.             if (!endblk || left >= 128L) {
  839.                 if (fwrite(buf, 1, 128, f) != 128) {
  840.                     if (debug)
  841.                         fputs("- WRITE ERROR\n", stderr);
  842.                     goto abort;
  843.                 }
  844.                 left -= 128L;    /* 128 less to do... */
  845.     /*
  846.      * we know the size of the file and there are less than
  847.      * 128 bytes to write out; only do the necessary amount
  848.      */
  849.             } else if (left > 0) {
  850.                 if (fwrite(buf, 1, (unsigned)left, f) != (int)left) {
  851.                     if (debug)
  852.                         fputs("- WRITE ERROR\n", stderr);
  853.                     goto abort;
  854.                 }
  855.                 left = 0;    /* and then there were none */
  856.             }
  857.             tries = 0;        /* reset try count */
  858.             blknum++;        /* we want the next block */
  859.             goto ackblock;
  860.         } else {
  861.             goto nakblock;        /* ask for a resend */
  862.         }
  863.     } else if (inblk < c || inblk > (c + 100)) {    /* if we have it, */
  864.         (void) getblock(buf);        /* ignore it */
  865.         sendack(S_ACK, inblk);        /* but ack it */
  866.         goto ackblock;
  867.     } else
  868.         goto nextblock;            /* else if running ahead */
  869.  
  870. endrcv:
  871. #ifdef    NAKEOT
  872.     sendack(S_NAK, blknum);            /* NAK the EOT, make sure */
  873.     if (com_getc(20) != EOT)        /* we're all done */
  874.         goto nakblock;
  875. #endif /* NAKEOT */
  876.     sendack(S_ACK, blknum);            /* ACK it and clean up */
  877.     if (debug)
  878.         fputs("received EOT\n", stderr);
  879.     if (blknum>1) {                /* if we really got anything */
  880.         if (debug && toterr>2)
  881.             fprintf(stderr, "%d errors/%d blocks\n", toterr, blknum-1);
  882.         (void) fclose(f);
  883.         (void) unlink(outname);        /* rename temp to proper name */
  884.  
  885.         for (onp = outname;cnvrt && *onp;onp++)
  886.                         /* find out if there's lower */
  887.             if (islower(*onp))    /* case letters in filename  */
  888.                 cnvrt = 0;    /*  there are, don't convert */
  889.         if (cnvrt)            /* if there aren't, make all */
  890.             for (onp = outname;*onp;onp++)    /* into lowercase */
  891.                 *onp = tolower(*onp);
  892.         if (link(tmpname, outname) == 0 || fcopy(tmpname, outname) == 0)
  893.             (void) unlink(tmpname);
  894.         else if (debug)
  895.             fputs("can't rename or copy file\n", stderr);
  896.         if (zero.fstamp)        /* set stamp, if known */
  897.             setstamp(outname, zero.fstamp);
  898.         if (debug)
  899.             fprintf(stderr, "received file: %s\n", outname);
  900.         return outname;            /* signal what file we got */
  901.     } else {                /* else no real file */
  902.         (void) fclose(f);
  903.         (void) unlink(tmpname);        /* discard empty file */
  904.         if (debug)
  905.             fputs("received end\n", stderr);
  906.         xferdone = 1;            /* signal end of transfer */
  907.         return NULL;
  908.     }
  909.  
  910. abort:
  911.     if (debug) {
  912.         fputs("RECEIVE ABORTED\n", stderr);
  913.         if (toterr)
  914.             fprintf(stderr, "%d errors/%d blocks\n", toterr, blknum-1);
  915.     }
  916.     (void) fclose(f);
  917.     xferdone = 1;                /* signal end of transfer */
  918.     sendabort();
  919.     return NULL;
  920. }
  921.  
  922. int fcopy(file1, file2)        /* copy 'file1' to 'file2'    */
  923. char    *file1;            /* source filename        */
  924. char    *file2;            /* destination filename        */
  925. {
  926.     FILE        *f, *t;    /* files from and to        */
  927.     long        size;    /* bytes to copy        */
  928.     char        *buf;    /* buffer pointer        */
  929.     char        *malloc();    /* buffer allocator    */
  930.     unsigned int    bufl;    /* buffer length        */
  931.     unsigned int    cpy;    /* bytes being copied        */
  932.     struct stat    fst;    /* data about file        */
  933.  
  934.     if (stat(file1, &fst))    /* get file information    */
  935.         return EOF;
  936.     size = (long)fst.st_size;
  937.  
  938.     if ((char *)(f = fopen(file1, "r")) == NULL)
  939.         return EOF;
  940.     if ((char *)(t = fopen(file2, "w")) == NULL) {
  941.         (void) fclose(f);
  942.         return EOF;
  943.     }
  944.  
  945.     bufl = 32760;
  946.     if (bufl > size)
  947.         bufl = size;    /* don't waste space */
  948.  
  949.     while (bufl >= 128 && (buf = malloc(bufl)) == NULL)
  950.         bufl >>= 1;    /* keep trying until it's hopeless    */
  951.     if (buf == NULL) {    /* if we can't get a buffer, clean up    */
  952.         (void) fclose(f);
  953.         (void) fclose(t);
  954.         (void) unlink(file2);
  955.         return EOF;    /* return an error indication        */
  956.     }
  957.  
  958.     while (size > 0) {
  959.         cpy = fread(buf, sizeof(char),
  960.             bufl < size ? bufl : (unsigned short) size, f);
  961.         if (fwrite(buf, sizeof(char), cpy, t) != cpy)
  962.             break;
  963.         size -= cpy;
  964.     }
  965.  
  966.     free(buf);
  967.     (void) fclose(f);
  968.     (void) fclose(t);
  969.     return (size > 0);
  970. }
  971.  
  972. static void setstamp(f_name, f_time)    /* set a file's date/time stamp    */
  973. char    *f_name;            /* file to set stamp on        */
  974. long    f_time;                /* desired date/time        */
  975. {
  976.     void    tzset();        /* library time zone function    */
  977.     time_t    times[2], time();
  978.  
  979.     times[0] = time((time_t *) 0);
  980.     tzset();
  981.     times[1] = f_time + TENYEAR + timezone;    /* convert time        */
  982.     if (daylight)                /* if daylight savings,    */
  983.         times[1] += 3600L;        /* add an hour        */
  984.     utime(f_name, times);
  985. }
  986.  
  987. static int sendack(acknak, blknum)    /* send an ACK or a NAK */
  988. register int    acknak;            /* 1 = ACK, 0 = NAK */
  989. register int    blknum;            /* block number */
  990. {
  991.     if (debug)
  992.         fprintf(stderr, "%s %d\n", acknak?"ACK":"NAK", blknum);
  993.     if (acknak)            /* send the right signal */
  994.         outb[0] = ACK;
  995.     else if (chktec)
  996.         outb[0] = 'C';
  997.     else
  998.         outb[0] = NAK;
  999.  
  1000.     outb[1] = blknum;        /* block number */
  1001.     outb[2] = blknum^0xff;        /* block number check */
  1002.     write(1, outb, 3);
  1003. }
  1004.  
  1005. static int getblock(buf)        /* read a block of data */
  1006. char    *buf;                /* data buffer */
  1007. {
  1008.     register unsigned short    ourcrc = 0;    /* remote CRC check value */
  1009.     unsigned short    hiscrc;            /* remote CRC check value */
  1010.     register int    c;            /* one byte of data */
  1011.     int    n;                /* index */
  1012.     int    timeout = ackless ? 200 : 5;    /* short block timeout */
  1013.  
  1014.     for(n = 0; n<128; n++) {    
  1015.         if ((c = com_getc(timeout)) == EOF) {
  1016.             if (debug)
  1017.                 fputs("block received -- short\n", stderr);
  1018.             return 1;
  1019.         }
  1020.         if (chktec)
  1021.             ourcrc = crc_update(ourcrc, (unsigned char) c);
  1022.         else
  1023.             ourcrc += c;
  1024.         *buf++ = (unsigned char) c;
  1025.     }
  1026.  
  1027.     if (chktec) {    
  1028.         ourcrc = crc_finish(ourcrc);
  1029.         c = com_getc(timeout);
  1030.         hiscrc = (c << 8) | com_getc(timeout);
  1031.     } else {    
  1032.         ourcrc &= 0xff;
  1033.         hiscrc = com_getc(timeout) & 0xff;
  1034.     }
  1035.  
  1036.     if (debug) {
  1037.         if (ourcrc == hiscrc)
  1038.             fputs("block received -- good\n", stderr);
  1039.         else {
  1040.             fprintf(stderr, "block received -- bad %s\n", chktec?"CRC":"checksum");
  1041.             fprintf(stderr, "his = 0x%x  ours = 0x%x\n", hiscrc, ourcrc);
  1042.         }
  1043.     }
  1044.     if (ourcrc == hiscrc)
  1045.         return 0;        /* block is good */
  1046.     else
  1047.         return 1;        /* error in checksum or CRC */
  1048. }
  1049.  
  1050. void sendabort()
  1051. {
  1052. #ifdef    SYSV
  1053.     (void) ioctl(0, TCFLSH, 1);    /* purge output */
  1054. #endif /* SYSV */
  1055. #ifdef    BSD
  1056.     (void) ioctl(0, TIOCFLUSH, 0);    /* purge all i/o */
  1057. #endif /* BSD */
  1058.     strcpy(outb, "\030\030\030\030\030\030\030\030\b\b\b\b\b\b\b\b");
  1059.     strcat(outb, "\030\030\030\030\b\b\b\b");    /* set up cancel seq */
  1060.     write(1, outb, (unsigned)strlen(outb));    /* write it out */
  1061. }
  1062.  
  1063. #ifdef    NO_MEM
  1064. /*
  1065.  *    This routine replicates the function found in the
  1066.  *    System V memory.h module for systems without a proper
  1067.  *    or with no implementation of this function available.
  1068.  */
  1069. char *memset(m, n, c)
  1070. char    *m;
  1071. int    n;
  1072. char    c;
  1073. {
  1074.     int    i;
  1075.     for (i = 0;i < n;i++)
  1076.         *(m+i) = c;
  1077.     return m;
  1078. }
  1079. #endif /* NO_MEM */
  1080.