home *** CD-ROM | disk | FTP | other *** search
- /*****************************************************************************
- * SEAlink - Sliding window file transfer protocol
- *
- * @(#) sealink.c 2.9 89/03/02
- * UNIX SVR2 and BSD versions by Scott Reynolds
- * uucp: clmqt!scott
- *
- * additional SysV modifications by Sanford Zelkovitz, without whose
- * help this couldn't have been accomplished
- *
- * Based on:
- * MS-DOS Version 1.20, created on 08/05/87 at 17:51:40
- * (C)COPYRIGHT 1986, 87 by System Enhancement Associates ALL RIGHTS RESERVED
- * By: Thom Henderson
- *
- * Mr. Henderson had no hand in this UNIX port; please don't bother
- * him with questions about this program!
- *
- * Description:
- *
- * The intent of SEAlink is to provide a file transfer protocol that
- * does not suffer from propagation delays, such as are introduced
- * by satellite relays or packet switched networks.
- ****************************************************************************/
-
- /*
- * The following flags are for compiling on different systems.
- */
- #define SYSV /* Compile for SYS V i/o calls */
- /* #define BSD /* Compile for BSD i/o calls */
-
- /*
- * Define NO_MEM if your system doesn't have a
- * working memset() function
- */
- /* #define NO_MEM /* memset() doesn't work */
-
- /*
- * Define NO_NAP if there is no nap() function.
- *
- * If you use nap() be aware that it causes a MUCH greater load
- * on the processor.
- *
- * With NO_NAP defined the XModem compatibility is reduced; if
- * this is not a concern, define this for greater efficiency.
- */
- #define NO_NAP /* nap() doesn't work */
-
- /*
- * Define CRCTABLE to use the fast table lookup CRC calculation;
- * a slower calculation-based method can be compiled to reduce the
- * amount of process memory required by commenting this out.
- */
- #define CRCTABLE /* use CRC lookup table */
-
- /*
- * The section of code that is compiled when NAKEOT is defined is in the
- * original MS-DOS version 1.16 code. Its purpose is to send a NAK when
- * an EOT is received during rcvfile(), apparently to confirm that this is
- * indeed the end of file. However, in certain (apparently non-standard)
- * versions of the protocol, it is possible that the program will report an
- * error when in fact there isn't one. Comment this out at your discretion.
- */
- #define NAKEOT /* define to NAK EOT's */
-
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <time.h>
- #ifdef SYSV /* use System V I/O control */
- #include <termio.h>
- #endif /* SYSV */
- #ifdef BSD /* use BSD I/O control */
- #include <sgtty.h>
- #endif /* BSD */
- #ifndef NO_MEM
- #include <memory.h>
- #endif /* NO_MEM */
-
- /* Various system constants */
- #define WINDOW 6 /* maximum size of window */
- #define S_NAK 0 /* NAK condition for sendack() */
- #define S_ACK 1 /* ACK condition for sendack() */
- #define NONE 0 /* neither send nor receive */
- #define SEND 1 /* send mode */
- #define RECV 2 /* receive mode */
- #define TENYEAR (time_t) 315532800L /* GMT offset for 1970 <-> 1980 */
-
- /* SEAlink block zero data structure */
- struct zeros
- {
- long flen; /* file length */
- time_t fstamp; /* file date/time stamp */
- char fnam[17]; /* original file name */
- char prog[15]; /* sending program name */
- char noacks; /* true if ACKing not required */
- char fill[87]; /* reserved for future use */
- };
-
- /* ASCII mnemonic values */
- #define ACK 0x06
- #define NAK 0x15
- #define SOH 0x01
- #define EOT 0x04
- #define CAN 0x18
-
- static int outblk; /* number of next block to send */
- static int ackblk; /* number of last block ACKed */
- static int blksnt; /* number of last block sent */
- static int slide; /* true if sliding window */
- static int ackst; /* ACK/NAK state */
- static int numnak; /* number of sequential NAKs */
- static int chktec; /* 1 = CRC, 0 = checksum */
- static int toterr; /* total number of errors */
- static int ackrep; /* true when ACK, NAK reported */
- static int ackseen; /* count of sliding ACKs seen */
- static int xferdone = 0; /* done with transfer (recvr) */
- static int debug = 0; /* debugging flag */
- static int sld_flag = 1; /* sliding windows allowed */
- static int ackless = 1; /* true if ACKs not required */
- static char outb[133]; /* block to use for output */
-
- /* the program name MUST be 14 characters plus a '\0' terminator */
- #ifdef SYSV
- char progname[15] = "SEAlink/SYSV ";
- #endif /* SYSV */
- #ifdef BSD
- char progname[15] = "SEAlink/BSD ";
- #endif /* BSD */
-
- /* the debug filename is a local preference */
- char *dbugfile = "/tmp/sealink.log";
-
-
- #ifdef NO_NAP
- /*
- * Need this to do a (very) rough approximation of nap().
- * Used by alarm() in com_getc()
- */
- jmp_buf tohere;
- #endif
-
- /* CRC computation logic */
- #ifdef CRCTABLE
- unsigned short crc_tab[256] = /* CRC lookup table */
- {
- 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
- 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
- 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
- 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
- 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
- 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
- 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
- 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
- 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
- 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
- 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
- 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
- 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
- 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
- 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
- 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
- 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
- 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
- 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
- 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
- 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
- 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
- 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
- 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
- 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
- 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
- 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
- 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
- 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
- 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
- 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
- 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
- };
-
- /*
- * crc_update performs CRC calculation using crc_tab[].
- * Note: Don't need to "flush" with zeroes with this formula.
- */
- #define crc_update(CRC, C) ((CRC << 8) ^ crc_tab[(CRC >> 8)^C])
- #define crc_finish(CRC) (CRC)
-
- #else /* otherwise, don't use CRC table */
-
- unsigned short crc_update(crc, c) /* calculate a CRC value */
- register unsigned crc;
- register int c;
- {
- int count;
-
- for (count = 8; --count >= 0;) {
- if (crc & 0x8000) {
- crc <<= 1;
- crc += (((c <<= 1) & 0400) != 0);
- crc ^ = 0x1021;
- } else {
- crc <<= 1;
- crc += (((c <<= 1) & 0400) != 0);
- }
- }
- return crc;
- }
-
- /* finish CRC calculation by "flushing" with zeroes */
- #define crc_finish(C) crc_update(crc_update(C, 0), 0)
- #endif
-
- unsigned alarm();
- void sendabort();
- void shipblk();
-
- #ifdef NO_MEM
- char *memset();
- #endif /* NO_MEM */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int c; /* used to get options */
- int mode = NONE; /* SEND, RECV files */
- int noerr; /* no error in transmission */
- char *fn; /* current filename to send/recv */
- int getopt();
- unsigned sleep();
- int xmtfile();
- char *rcvfile();
- extern int opterr; /* used by getopt() */
- extern int optind; /* " */
- extern char *optarg; /* " */
- #ifdef SYSV
- struct termio oldtty, tty;
- #endif /* SYSV */
- #ifdef BSD
- struct sgttyb oldtty, tty;
- #endif /* BSD */
-
- mode = 0;
- fn = NULL;
- opterr = 0;
- while ((c = getopt(argc, argv, "dfors:")) != EOF) {
- switch (c) {
- case 'd':
- debug = 1; /* use debug file */
- break;
- case 'f':
- sld_flag = 0; /* no sliding window */
- break;
- case 'o':
- ackless = 0; /* no overdrive mode */
- break;
- case 's':
- mode = SEND;
- fn = optarg;
- break;
- case 'r':
- mode = RECV;
- break;
- default:
- mode = NONE;
- break;
- }
- }
- switch (mode) {
- case RECV:
- fputs("sealink: ready to receive\n", stderr);
- break;
- case SEND:
- fputs("sealink: ready to send\n", stderr);
- break;
- default:
- if (debug)
- printf("%s 2.9 sealink.c 89/03/02\n\n", progname);
- printf(" SEAlink sliding window file transfer protocol\n");
- printf("v1.20 (C) 1986, 1987 System Enhancement Associates\n");
- printf(" ALL RIGHTS RESERVED written by Thom Henderson\n");
- printf(" UNIX version written by Scott Reynolds\n\n");
- printf("Usage: sealink -[dfo]s filename...\n");
- printf(" sealink -[dfo]r [filename...]\n");
- printf("Options:\n");
- printf(" -d Debug output to temporary file\n");
- printf(" -f Force no sliding window\n");
- printf(" -o Shut down overdrive mode\n");
- printf(" -s filename... Send the file(s) specified\n");
- printf(" -r [filename...] Receive file(s)\n");
- exit(1);
- }
- if (debug) {
- time_t tim, time();
- char *ctime();
-
- printf("Sending debug output to %s\n", dbugfile);
- freopen(dbugfile, "a", stderr); /* open log file */
- setbuf(stderr, NULL);
- (void) time(&tim);
- fputs(ctime(&tim), stderr);
- }
- fflush(stdout); /* flush output before anything else */
-
- #ifdef SYSV
- (void) ioctl(0, TCGETA, &oldtty); /* get terminal parameters */
- if (debug)
- fputs("tty parameters read\n", stderr);
- tty = oldtty; /* copy them, then set new */
- tty.c_iflag = IGNBRK; /* No input filter; ignore break */
- tty.c_oflag = 0; /* Use transparent output */
- tty.c_lflag &= ~(ECHO|ICANON|ISIG); /* disable echo, signals */
- tty.c_cc[VMIN] = 0; /* AT&T Sys V: return immediately */
- tty.c_cc[VTIME] = 0; /* if no characters can be read */
- tty.c_cflag &= ~PARENB; /* Leave baud rate, disable parity */
- tty.c_cflag &= ~CSIZE; /* reset data bits */
- tty.c_cflag |= CS8; /* set 8 bit data */
- (void) ioctl(0, TCSETAW, &tty); /* go after setting terminal */
- #endif /* SYSV */
- #ifdef BSD
- (void) ioctl(0, TIOCGETP, &oldtty); /* get terminal parameters */
- if (debug)
- fputs("tty parameters read\n", stderr);
- tty = oldtty; /* copy them, then set new */
- tty.sg_flags = RAW; /* raw mode (8 bit, no processing */
- (void) ioctl(0, TIOCSETP, &tty); /* go after setting terminal */
- #endif /* BSD */
- if (debug)
- fputs("tty parameters set\n", stderr);
-
- if (mode == SEND) {
- do {
- if (noerr = xmtfile(fn))
- sleep(2); /* wait a few before next */
- if (optind < argc)
- fn = argv[optind++];
- else
- fn = NULL;
- } while (noerr && fn != NULL);
- if (noerr) /* no errors, send end marker */
- (void) xmtfile("");
- } else {
- do {
- if (optind < argc) /* if filename given, use it */
- fn = argv[optind++];
- else /* otherwise get one from remote */
- fn = "";
- if (noerr = (rcvfile(fn) != NULL))
- sleep(2); /* wait a few before next */
- } while (noerr); /* go until done/err */
- noerr = xferdone; /* set no error if done */
- }
-
- #ifdef SYSV
- (void) ioctl(0, TCSBRK, 1); /* Wait for output to drain */
- (void) ioctl(0, TCFLSH, 2); /* Flush input queue */
- (void) ioctl(0, TCSETAW, &oldtty); /* Restore original modes */
- (void) ioctl(0, TCXONC, 1); /* Restart output */
- #endif /* SYSV */
- #ifdef BSD
- (void) ioctl(0, TIOCSETP, &oldtty);
- #endif /* BSD */
- if (debug) {
- fputs("tty parameters restored\n", stderr);
- (void) fclose(stderr);
- }
-
- exit(!noerr); /* and return error status */
- /*NOTREACHED*/
- }
-
- /*
- * chkout() returns non-zero if stdout and stderr are sending to
- * different files/devices, zero if the same
- */
- int chkout()
- {
- struct stat so, se;
-
- (void)fstat(1, &so);
- (void)fstat(2, &se);
- return (so.st_rdev != se.st_rdev);
- }
-
- #ifdef NO_NAP
- /*
- * alarmint() is called when an alarm signal is caught.
- */
- int alarmint()
- {
- longjmp(tohere, EOF); /* return EOF to indicate timeout */
- }
- #endif /* NO_NAP */
-
- /*
- * com_getc(timeout) reads a character from file descriptor 0
- * timeout is in tenths of seconds
- * EOF returned if no character was available to read
- *
- * If timeout is 0, this routine will return immediately regardless of
- * the status of the read. If timeout > 0, there will be a minimum of
- * one to two seconds before returning if nap() does not work.
- */
- int com_getc(timeout)
- register int timeout;
- {
- static char byt[2]; /* buffer to read characters into */
-
- #ifdef BSD
- if (!timeout) { /* if no timeout then no alarms */
- long len; /* number of buffered characters */
-
- (void) ioctl(0, FIONREAD, &len); /* check buffer */
- /* read character if available */
- if (len > 0L && read(0, byt, 1) == 1)
- return (byt[0] & 0377); /* return the character */
- return EOF; /* error or none available */
- }
- #endif /* BSD */
- #ifdef SYSV
- if (!timeout) { /* if no timeout then no alarms */
- if (read(0, byt, 1) == 1) /* if character was read, */
- return (byt[0] & 0377); /* return the character */
- return EOF; /* error or none available */
- }
- #endif /* SYSV */
-
- #ifdef NO_NAP
- /* There's a timeout value, so now we get to use alarm() */
- timeout = ((timeout-1)/10)+1; /* round to seconds */
- if (timeout == 1) /* minimum of 2 seconds for alarm, */
- timeout++; /* since 1 may not be any delay */
- if (setjmp(tohere)) { /* if the alarm went off */
- if (debug) /* timeout message if debugging */
- fputs("Read: timeout\n", stderr);
- return EOF; /* return EOF (longjmp call) */
- }
- signal(SIGALRM, alarmint); /* set up alarm signal catching */
- alarm((unsigned)timeout); /* set alarm time */
- while (read(0, byt, 1) != 1) /* Go until we read a character */
- ; /* (or the alarm goes off!) */
- alarm(0); /* reset alarm */
- signal(SIGALRM, SIG_DFL); /* and turn off signal catching */
- return (byt[0] & 0377); /* return the character */
- #else /* NO_NAP undefined -- nap() works */
- do {
- if (read(0, byt, 1) == 1) /* did we read a char? */
- return (byt[0] & 0377); /* yes, return it */
- (void) nap(100L); /* sleep for a little while */
- } while (--timeout); /* loop until time runs out */
- if (debug) /* timeout message if debugging */
- fputs("Read: timeout\n", stderr);
- return EOF;
- #endif /* NO_NAP */
- }
-
- /* File transmitter logic */
-
- int xmtfile(name) /* transmit a file */
- char *name; /* name of file to send */
- {
- FILE *f, *fopen(); /* file to send */
- int endblk; /* block number of EOT */
- struct stat fst; /* data about file */
- struct zeros zero; /* block zero data */
- char *basename; /* base filename */
-
- if (name && *name) { /* if sending a file */
- if ((char *)(f = fopen(name, "r")) == NULL) {
- fprintf(stderr, "Can't read %s\n", name);
- return 0;
- }
-
- memset((char *)&zero, 0, sizeof(zero)); /* clear data block */
-
- tzset();
- stat(name, &fst); /* get file information */
- zero.flen = (long)fst.st_size;
- zero.fstamp = fst.st_mtime - timezone; /* adjust f/TZ */
- if (daylight) /* if daylight savings, */
- zero.fstamp -= 3600L; /* subtract an hour */
- if (zero.fstamp < 0L)
- zero.fstamp = (time_t) 0;
- if ((basename = strrchr(name, '/')) == NULL) {
- strcpy(zero.fnam, name);
- } else {
- basename++;
- strcpy(zero.fnam, basename);
- }
- if (debug)
- fprintf(stderr, "basename: %s\n", zero.fnam);
- strcpy(zero.prog, progname);
- zero.noacks = ackless;
-
- endblk = (int)((zero.flen+127L)/128L)+1;
- } else {
- endblk = 0; /* fake for no file */
- if (debug)
- fputs("send transfer complete\n", stderr);
- }
-
- outblk = 1; /* set starting state */
- ackblk = (-1);
- blksnt = slide = ackst = numnak = toterr = ackrep = ackseen = 0;
- chktec = 2; /* undetermined */
-
- while (ackblk < endblk) { /* while not all there yet */
- if (outblk <= ackblk + ((slide && sld_flag)? WINDOW : 1)) {
- if (outblk < endblk) {
- if (outblk > 0)
- sendblk(f, outblk);
- else
- shipblk((unsigned char *)&zero, 0);
- if (ackless && slide && sld_flag)
- ackblk = outblk;
- } else if (outblk == endblk) {
- outb[0] = EOT;
- write(1, outb, 1);
- if (debug)
- fputs("sent EOT\n", stderr);
- }
- outblk++;
- }
-
- ackchk();
- if (numnak>10)
- goto abort;
- }
-
- if (endblk)
- (void) fclose(f);
- if (debug && toterr>2)
- fprintf(stderr, "%d errors/%d blocks\n", toterr, blksnt);
- return 1; /* exit with good status */
-
- abort:
- if (endblk)
- (void) fclose(f);
- if (debug) {
- fputs("TRANSMIT ABORTED\n", stderr);
- if (toterr)
- fprintf(stderr, "%d errors/%d blocks\n", toterr, blksnt);
- }
- sendabort();
- return 0; /* exit with bad status */
- }
-
- /*
- * The various ACK/NAK states are:
- * 0: Ground state, ACK or NAK expected.
- * 1: ACK received
- * 2: NAK received
- * 3: ACK, block# received
- * 4: NAK, block# received
- * 5: Returning to ground state
- */
- static ackchk() /* check for ACK or NAK */
- {
- int c; /* one byte of data */
- static int rawblk; /* raw block number */
-
- ackrep = 0; /* nothing reported yet */
-
- while ((c = com_getc(0)) != EOF) {
- if (c == CAN) { /* CANcel received? */
- if ((c = com_getc(20)) == CAN) { /* get two */
- numnak = 11; /* OK, let's abort! */
- ackst = 0;
- if (debug)
- fputs("received cancel\n", stderr);
- }
- return; /* break out of here */
- }
- if (ackst == 3 || ackst == 4) { /* Windowed ACK/NAK */
- slide = 0; /* assume this will fail */
- /* see if we believe the number */
- if (rawblk == (c^0xff)) {
- rawblk = outblk - ((outblk-rawblk)&0xff);
- if (rawblk >= 0 && rawblk <= outblk
- && rawblk > outblk-128) {
- /* we have sliding window! */
- if (ackst == 3) {
- ackblk = ackblk > rawblk ? ackblk : rawblk;
- slide = 1;
- if (ackless && ++ackseen>10) {
- ackless = 0;
- if (debug)
- fputs("- Overdrive disengaged\n", stderr);
- }
- } else {
- outblk = rawblk<0? 0 : rawblk;
- slide = numnak<4;
- }
- if (debug)
- fprintf(stderr, "%s %d == \n", ackst == 3?"ACK":"NAK", rawblk);
- ackrep = 1; /* we reported something */
- }
- }
- ackst = 5; /* return to ground state */
- }
-
- if (ackst == 1 || ackst == 2) {
- rawblk = c;
- ackst += 2;
- }
-
- if (!slide || ackst == 0) {
- if (c == ACK) {
- if (!slide) {
- ackblk++;
- if (debug)
- fprintf(stderr, "ACK %d --\n", ackblk);
- ackrep = 1; /* reported an ACK */
- }
- ackst = 1;
- numnak = 0;
- } else if (c == 'C' || c == NAK) {
- /* if method not determined yet */
- if (chktec>1) /* then do what rcver wants */
- chktec = (c == 'C');
- #ifdef SYSV
- (void) ioctl(0, TCFLSH, 1); /* purge output */
- #endif /* SYSV */
- #ifdef BSD
- /* for now this code is commented out. It causes more complications to have
- * it installed -- my wish list would include for a BSD ioctl() to purge only
- * the output, but at the moment things will just have to stay this way. */
- /* (void) ioctl(0, TIOCFLUSH, 0); /* purge i/o */
- #endif /* BSD */
- if (!slide) {
- outblk = ackblk+1;
- if (debug)
- fprintf(stderr, "NAK %d --\n", ackblk+1);
- ackrep = 1; /* reported a NAK */
- }
- ackst = 2;
- numnak++;
- if (blksnt)
- toterr++;
- }
- }
- if (ackst == 5)
- ackst = 0;
- }
- }
-
- static sendblk(f, blknum) /* send one block */
- FILE *f; /* file to read from */
- int blknum; /* block to send */
- {
- long blkloc; /* address of start of block */
- unsigned char buf[128]; /* one block of data */
-
- if (blknum != blksnt+1) { /* if jumping */
- blkloc = (long)(blknum-1) * 128L;
- fseek(f, blkloc, 0); /* move where to */
- }
- blksnt = blknum;
-
- memset(buf, 26, 128); /* fill buffer with control Zs */
- fread(buf, 1, 128, f); /* read in some data */
- shipblk(buf, blknum); /* pump it out to the receiver */
- }
-
- static void shipblk(blk, blknum) /* physically ship a block */
- unsigned char *blk; /* data to be shipped */
- int blknum; /* number of block */
- {
- register unsigned short crc = 0; /* CRC check value */
- register int n; /* index */
- unsigned char *b = blk; /* data pointer */
-
- outb[0] = SOH; /* block header */
- outb[1] = blknum; /* block number */
- outb[2] = blknum^0xff; /* block number check value */
-
- for(n = 0;n < 128;n++) { /* ship the data */
- if (chktec)
- crc = crc_update(crc, *b);
- else
- crc += *b;
- outb[n+3] = (*b++);
- }
- crc = crc_finish(crc);
-
- if (chktec) { /* send proper check value */
- outb[131] = crc>>8;
- outb[132] = crc&0xff;
- write(1, outb, 133);
- } else {
- outb[131] = crc&0xff;
- write(1, outb, 132);
- }
-
- if (debug)
- fprintf(stderr, "sent block %d\n", blknum);
-
- return;
- }
-
- /* File receiver logic */
-
- char *rcvfile(name) /* receive file */
- char *name; /* name of file */
- {
- int c; /* received character */
- int tries; /* retry counter */
- int blknum; /* desired block number */
- int inblk; /* this block number */
- FILE *f; /* file to receive to */
- char buf[128]; /* data buffer */
- char tmpname[100]; /* name of temporary file */
- static char outname[100]; /* name of final file */
- struct zeros zero; /* file header data storage */
- int endblk; /* block number of EOT if known */
- long left; /* bytes left to output */
- int getblock(); /* block receiver, status */
- int fcopy(); /* copy source file to dest */
- void setstamp(); /* set date/time stamp of file */
- int cnvrt; /* flag -- convert filename? */
- char *onp; /* use to convert filename to l/c */
-
- if (name && *name) { /* figure out a name to use */
- strcpy(outname, name); /* user supplied one */
- cnvrt = 0; /* no conversion should be done */
- } else {
- *outname = '\0'; /* get name from transmitter */
- cnvrt = 1; /* convert to local is necessary */
- }
-
- strcpy(tmpname, ".sl.rcv.XXXXXX"); /* template for mktemp() */
- mktemp(tmpname); /* use a unique temp filename */
- if (debug)
- fprintf(stderr, "tmpname: %s\n", tmpname);
-
- if (*outname && (f = fopen(outname, "r"))) { /* open output file */
- (void) fclose(f);
- if (!(f = fopen(outname, "r+"))) {
- if (debug)
- fprintf(stderr, "Cannot write %s\n", tmpname);
- sendabort();
- return NULL;
- } else {
- (void) fclose(f);
- }
- }
- if (!(f = fopen(tmpname, "w"))) { /* open temporary file */
- if (debug)
- fprintf(stderr, "Cannot create %s\n", tmpname);
- sendabort();
- return NULL;
- }
-
- blknum = *outname ? 1 : 0; /* first block we must get */
- tries = -10; /* kludge for first time around */
- chktec = 1; /* try for CRC error checking */
- toterr = 0; /* no errors yet */
- endblk = 0; /* we don't know the size yet */
- ackless = 0; /* we don't know about this yet */
- memset((char *)&zero, 0, sizeof(zero)); /* or much of anything else */
-
- /*
- if (com_scan() == EOF) /+ kludge for adaptive Modem7 +/
- goto nextblock;
- */
- nakblock: /* we got a bad block */
- if (blknum>1)
- toterr++;
- if (++tries>10)
- goto abort;
- if (tries == 0) /* if CRC isn't going */
- chktec = 0; /* then give checksum a try */
-
- sendack(S_NAK, blknum); /* send the NAK */
- if (ackless && toterr > 20) { /* if ackless mode isn't working */
- ackless = 0; /* then shut it off */
- if (debug)
- fputs("- Overdrive disengaged\n", stderr);
- }
- goto nextblock;
-
- ackblock: /* we got a good block */
- nextblock: /* start of "get a block" */
- while ((c = com_getc(30)) != EOF) {
- if (c == CAN)
- if ((c = com_getc(30)) == CAN) {
- sendabort();
- return NULL;
- } else
- break;
- if (c == EOT) {
- if (!endblk || endblk == blknum)
- goto endrcv;
- } else if (c == SOH) {
- if ((inblk = com_getc(5)) == EOF)
- goto nakblock;
- if (com_getc(5) == (inblk^0xff)) {
- if (debug)
- fprintf(stderr, "received #%d\n", inblk);
- goto blockstart; /* we found a start */
- }
- }
- }
- goto nakblock;
-
- blockstart: /* start of block detected */
- c = blknum&0xff;
- if (inblk == 0 && blknum <= 1) { /* if this is the header */
- if (!getblock((char *)&zero)) {
- sendack(S_ACK, inblk); /* ack the header */
- if (!*name) /* given name takes precedence */
- strcpy(outname, zero.fnam);
- if (left = zero.flen) /* length to transfer */
- endblk = (int)((left+127L)/128L)+1;
- if (ackless != zero.noacks && debug)
- fprintf(stderr, "+ Overdrive %sengaged\n",
- zero.noacks?"":"dis");
- ackless = zero.noacks;
-
- blknum = 1; /* now we want first data block */
- goto ackblock;
- } else {
- goto nakblock; /* bad header block */
- }
- } else if (inblk == c) { /* if this is the one we want */
- if (!getblock(buf)) { /* else if we get it okay */
- if (!ackless) /* if we're sending ACKs */
- sendack(S_ACK, inblk); /* then ACK the data */
- /*
- * if file size is not known or more than 128 bytes
- * to go, write one block (128 bytes) to the file
- */
- if (!endblk || left >= 128L) {
- if (fwrite(buf, 1, 128, f) != 128) {
- if (debug)
- fputs("- WRITE ERROR\n", stderr);
- goto abort;
- }
- left -= 128L; /* 128 less to do... */
- /*
- * we know the size of the file and there are less than
- * 128 bytes to write out; only do the necessary amount
- */
- } else if (left > 0) {
- if (fwrite(buf, 1, (unsigned)left, f) != (int)left) {
- if (debug)
- fputs("- WRITE ERROR\n", stderr);
- goto abort;
- }
- left = 0; /* and then there were none */
- }
- tries = 0; /* reset try count */
- blknum++; /* we want the next block */
- goto ackblock;
- } else {
- goto nakblock; /* ask for a resend */
- }
- } else if (inblk < c || inblk > (c + 100)) { /* if we have it, */
- (void) getblock(buf); /* ignore it */
- sendack(S_ACK, inblk); /* but ack it */
- goto ackblock;
- } else
- goto nextblock; /* else if running ahead */
-
- endrcv:
- #ifdef NAKEOT
- sendack(S_NAK, blknum); /* NAK the EOT, make sure */
- if (com_getc(20) != EOT) /* we're all done */
- goto nakblock;
- #endif /* NAKEOT */
- sendack(S_ACK, blknum); /* ACK it and clean up */
- if (debug)
- fputs("received EOT\n", stderr);
- if (blknum>1) { /* if we really got anything */
- if (debug && toterr>2)
- fprintf(stderr, "%d errors/%d blocks\n", toterr, blknum-1);
- (void) fclose(f);
- (void) unlink(outname); /* rename temp to proper name */
-
- for (onp = outname;cnvrt && *onp;onp++)
- /* find out if there's lower */
- if (islower(*onp)) /* case letters in filename */
- cnvrt = 0; /* there are, don't convert */
- if (cnvrt) /* if there aren't, make all */
- for (onp = outname;*onp;onp++) /* into lowercase */
- *onp = tolower(*onp);
- if (link(tmpname, outname) == 0 || fcopy(tmpname, outname) == 0)
- (void) unlink(tmpname);
- else if (debug)
- fputs("can't rename or copy file\n", stderr);
- if (zero.fstamp) /* set stamp, if known */
- setstamp(outname, zero.fstamp);
- if (debug)
- fprintf(stderr, "received file: %s\n", outname);
- return outname; /* signal what file we got */
- } else { /* else no real file */
- (void) fclose(f);
- (void) unlink(tmpname); /* discard empty file */
- if (debug)
- fputs("received end\n", stderr);
- xferdone = 1; /* signal end of transfer */
- return NULL;
- }
-
- abort:
- if (debug) {
- fputs("RECEIVE ABORTED\n", stderr);
- if (toterr)
- fprintf(stderr, "%d errors/%d blocks\n", toterr, blknum-1);
- }
- (void) fclose(f);
- xferdone = 1; /* signal end of transfer */
- sendabort();
- return NULL;
- }
-
- int fcopy(file1, file2) /* copy 'file1' to 'file2' */
- char *file1; /* source filename */
- char *file2; /* destination filename */
- {
- FILE *f, *t; /* files from and to */
- long size; /* bytes to copy */
- char *buf; /* buffer pointer */
- char *malloc(); /* buffer allocator */
- unsigned int bufl; /* buffer length */
- unsigned int cpy; /* bytes being copied */
- struct stat fst; /* data about file */
-
- if (stat(file1, &fst)) /* get file information */
- return EOF;
- size = (long)fst.st_size;
-
- if ((char *)(f = fopen(file1, "r")) == NULL)
- return EOF;
- if ((char *)(t = fopen(file2, "w")) == NULL) {
- (void) fclose(f);
- return EOF;
- }
-
- bufl = 32760;
- if (bufl > size)
- bufl = size; /* don't waste space */
-
- while (bufl >= 128 && (buf = malloc(bufl)) == NULL)
- bufl >>= 1; /* keep trying until it's hopeless */
- if (buf == NULL) { /* if we can't get a buffer, clean up */
- (void) fclose(f);
- (void) fclose(t);
- (void) unlink(file2);
- return EOF; /* return an error indication */
- }
-
- while (size > 0) {
- cpy = fread(buf, sizeof(char),
- bufl < size ? bufl : (unsigned short) size, f);
- if (fwrite(buf, sizeof(char), cpy, t) != cpy)
- break;
- size -= cpy;
- }
-
- free(buf);
- (void) fclose(f);
- (void) fclose(t);
- return (size > 0);
- }
-
- static void setstamp(f_name, f_time) /* set a file's date/time stamp */
- char *f_name; /* file to set stamp on */
- long f_time; /* desired date/time */
- {
- void tzset(); /* library time zone function */
- time_t times[2], time();
-
- times[0] = time((time_t *) 0);
- tzset();
- times[1] = f_time + TENYEAR + timezone; /* convert time */
- if (daylight) /* if daylight savings, */
- times[1] += 3600L; /* add an hour */
- utime(f_name, times);
- }
-
- static int sendack(acknak, blknum) /* send an ACK or a NAK */
- register int acknak; /* 1 = ACK, 0 = NAK */
- register int blknum; /* block number */
- {
- if (debug)
- fprintf(stderr, "%s %d\n", acknak?"ACK":"NAK", blknum);
- if (acknak) /* send the right signal */
- outb[0] = ACK;
- else if (chktec)
- outb[0] = 'C';
- else
- outb[0] = NAK;
-
- outb[1] = blknum; /* block number */
- outb[2] = blknum^0xff; /* block number check */
- write(1, outb, 3);
- }
-
- static int getblock(buf) /* read a block of data */
- char *buf; /* data buffer */
- {
- register unsigned short ourcrc = 0; /* remote CRC check value */
- unsigned short hiscrc; /* remote CRC check value */
- register int c; /* one byte of data */
- int n; /* index */
- int timeout = ackless ? 200 : 5; /* short block timeout */
-
- for(n = 0; n<128; n++) {
- if ((c = com_getc(timeout)) == EOF) {
- if (debug)
- fputs("block received -- short\n", stderr);
- return 1;
- }
- if (chktec)
- ourcrc = crc_update(ourcrc, (unsigned char) c);
- else
- ourcrc += c;
- *buf++ = (unsigned char) c;
- }
-
- if (chktec) {
- ourcrc = crc_finish(ourcrc);
- c = com_getc(timeout);
- hiscrc = (c << 8) | com_getc(timeout);
- } else {
- ourcrc &= 0xff;
- hiscrc = com_getc(timeout) & 0xff;
- }
-
- if (debug) {
- if (ourcrc == hiscrc)
- fputs("block received -- good\n", stderr);
- else {
- fprintf(stderr, "block received -- bad %s\n", chktec?"CRC":"checksum");
- fprintf(stderr, "his = 0x%x ours = 0x%x\n", hiscrc, ourcrc);
- }
- }
- if (ourcrc == hiscrc)
- return 0; /* block is good */
- else
- return 1; /* error in checksum or CRC */
- }
-
- void sendabort()
- {
- #ifdef SYSV
- (void) ioctl(0, TCFLSH, 1); /* purge output */
- #endif /* SYSV */
- #ifdef BSD
- (void) ioctl(0, TIOCFLUSH, 0); /* purge all i/o */
- #endif /* BSD */
- strcpy(outb, "\030\030\030\030\030\030\030\030\b\b\b\b\b\b\b\b");
- strcat(outb, "\030\030\030\030\b\b\b\b"); /* set up cancel seq */
- write(1, outb, (unsigned)strlen(outb)); /* write it out */
- }
-
- #ifdef NO_MEM
- /*
- * This routine replicates the function found in the
- * System V memory.h module for systems without a proper
- * or with no implementation of this function available.
- */
- char *memset(m, n, c)
- char *m;
- int n;
- char c;
- {
- int i;
- for (i = 0;i < n;i++)
- *(m+i) = c;
- return m;
- }
- #endif /* NO_MEM */
-