home *** CD-ROM | disk | FTP | other *** search
- /*
- * A version of Ward Christensen's file transfer protocol for
- * Unix System V or 4.2 bsd.
- *
- * Emmet P. Gray, ..!ihnp4!uiucuxc!fthood!egray, 16 Aug 85
- *
- * Modified by Sanford Zelkovitz 08/18/86
- * Last modification date = 05/20/87
- */
-
- #define SV
- #undef BSD
-
- #include <stdio.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #ifdef SV
- #include <termio.h>
- #endif
- #ifdef BSD
- #include <sgtty.h>
- #endif
-
- #define MAXERRORS 10 /* max number of times to retry */
- #define SECSIZE 1024 /* CP/M sector, transmission block */
- #define CPMEOF 26 /* End Of File (for CP/M) */
- #define SOH 1
- #define STX 2 /* Start Of Header */
- #define EOT 4 /* End Of Transmission */
- #define ACK 6 /* ACKnowledge */
- #define NAK 21 /* Negative AcKnowledge */
- #define CAN 24 /* CANcel */
-
- int synchron;
- int exit_return;
- unsigned char crc1, crc2;
- #ifdef SV
- struct termio ttyhold;
- #endif
- #ifdef BSD
- struct sgttyb ttyhold;
- #endif
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int msgstat;
- char *tty, *ttyname();
- struct stat stbuf;
- exit_return=0;
- if (argc != 3) {
- usage();
- exit(1);
- }
- tty = ttyname(1);
- stat(tty, &stbuf);
- msgstat = (stbuf.st_mode & 0777);
- chmod(tty, 0600); /* mesg n */
- #ifdef SV
- ioctl(0, TCGETA, &ttyhold); /* get current settings */
- #endif
- #ifdef BSD
- ioctl(0, TIOCGETP, &ttyhold);
- #endif
- switch (*argv[1]) {
- case 'r':
- recvfile(argv[2]);
- break;
- case 's':
- sendfile(argv[2]);
- break;
- default:
- usage();
- }
- #ifdef SV
- ioctl(0, TCSETAF, &ttyhold); /* restore settings */
- #endif
- #ifdef BSD
- ioctl(0, TIOCSETP, &ttyhold);
- #endif
- chmod(tty, msgstat); /* restore mesg status */
- exit(exit_return);
- }
-
- /* send a file to the remote */
- sendfile(tfile)
- char *tfile;
- {
- FILE *fp;
- unsigned char chr, checksum, block, sector[SECSIZE];
- int i, mode, nbytes, errcount, size, speed;
- long min, sec;
- static int baud[15] = {0, 50, 75, 110, 134, 150, 200,
- 300, 600, 1200, 1800, 2400, 4800, 9600, 19200};
- struct stat sbuf;
-
- if (!(fp = fopen(tfile, "r"))) {
- fprintf(stderr, "xmodem: Can't open '%s' for read\r\n", tfile);
- exit_return=1;
- return;
- }
- stat(tfile, &sbuf);
- size = (sbuf.st_size / 1024) + 1;
- #ifdef SV
- speed = baud[ttyhold.c_cflag & 017];
- #endif
- #ifdef BSD
- speed = baud[ttyhold.sg_ispeed];
- #endif
- sec = size;
- sec = sec * 1024L * 11L / speed;
- min = sec / 60L;
- sec = sec - min * 60L;
- printf("File open: %d records\r\n", size);
- printf("Send time: %ld min, %ld sec at %d baud\r\n", min, sec, speed);
- printf("To cancel: use CTRL-X numerous times\r\n");
- printf("Waiting ready signal\r\n");
-
- rawmode();
- errcount = 0;
- mode = 0;
- block = 1;
- while (errcount < MAXERRORS) {
- chr = getchar_t();
- if (chr == NAK) /* checksum mode */
- break;
- if (chr == 'C') { /* CRC mode */
- mode = 1;
- break;
- }
- errcount++;
- }
- if (errcount == MAXERRORS) {
- sleep(3);
- fprintf(stderr, "xmodem: Timed out on acknowledge\r\n");
- exit_return=1;
- return;
- }
- while (nbytes = fread(sector, sizeof(sector[0]), SECSIZE, fp)) {
- if (nbytes < SECSIZE) { /* fill short sector */
- for (i=nbytes; i < SECSIZE; i++)
- sector[i] = CPMEOF;
- }
- errcount = 0;
- while (errcount < MAXERRORS) {
- putchar(STX); /* the header */
- putchar(block); /* the block number */
- chr = ~block;
- putchar(chr); /* it's complement */
- checksum = 0;
- crc1 = 0;
- crc2 = 0;
- for (i=0; i < SECSIZE; i++) {
- putchar(sector[i]);
- if (mode)
- update_crc(sector[i]);
- else
- checksum += sector[i];
- }
- if (mode) {
- update_crc(0);
- update_crc(0);
- putchar(crc1);
- putchar(crc2);
- }
- else
- putchar(checksum);
- rec_loop:
- chr = getchar_t();
- if (chr == CAN) {
- sleep(3);
- fprintf(stderr,"\r\nxmodem: Abort request received\r\n");
- exit_return=1;
- return;
- }
- if (chr == ACK)
- break; /* got it! */
- if (chr != NAK ) goto rec_loop; /* noise on line? */
- errcount++;
- }
- if (errcount == MAXERRORS) {
- error();
- exit_return=1;
- return;
- }
- block++;
- }
- errcount = 0;
- exit_return=1;
- while (errcount < MAXERRORS) {
- putchar(EOT);
- if (getchar_t() == ACK)
- {
- exit_return=0;
- break;
- }
- errcount++;
- }
- return;
- }
-
- /* receive a file from the remote */
- recvfile(tfile)
- char *tfile;
- {
- FILE *fp;
- unsigned char hdr, blk, cblk, tmp, cksum;
- unsigned char c1, c2, sum, block, sector[SECSIZE];
- int i, stop = 0, mode, errcount, resync();
- long true_end;
- char ans[40];
-
- if (!access(tfile, 00)) {
- while (1) {
- printf("File already exists \r\n");
- return;
- }
- }
-
- if (!(fp = fopen(tfile, "w"))) {
- fprintf(stderr, "xmodem: Can't open '%s' for write\r\n", tfile);
- return;
- }
- printf("File open - ready to receive\r\n");
- rawmode();
- errcount = 0;
- block = 1;
-
- sleep(10);
- while (errcount < MAXERRORS) {
- if (errcount < (MAXERRORS / 2)) {
- putchar('C'); /* try CRC mode first */
- mode = 1;
- }
- else {
- putchar(NAK); /* then checksum */
- mode = 0;
- }
- if ((hdr = getchar_t()) == SOH) {
- ungetc(SOH, stdin);
- break;
- }
- if ( hdr == STX ) {
- ungetc(STX, stdin);
- break;
- }
- errcount++;
- }
- if (errcount == MAXERRORS) {
- sleep(3);
- fprintf(stderr, "\r\nxmodem: Timed out on acknowledge\r\n");
- return;
- }
- errcount = 0;
-
- while (errcount < MAXERRORS) {
- hdr = getchar_t();
- if (hdr == CAN) {
- sleep(3);
- fprintf(stderr, "\r\nxmodem: Abort request received\r\n");
- return;
- }
- if (hdr == EOT) /* done! */
- break;
- if (hdr != STX && hdr != SOH) { /* read in junk for 6 seconds */
- synchron = 0; /* to re-synchronized block */
- signal(SIGALRM, resync);
- alarm(6);
- while(synchron == 0)
- hdr = getchar();
- goto nak;
- }
- blk = getchar_t();
- cblk = getchar_t();
- crc1 = 0;
- crc2 = 0;
- sum = 0;
- for (i=0; i < SECSIZE; i++) {
- sector[i] = getchar_t();
- if (mode)
- update_crc(sector[i]);
- else
- sum += sector[i];
- }
- if (mode) {
- c1 = getchar_t();
- c2 = getchar_t();
- }
- else
- cksum = getchar_t();
- if (blk != block && blk != (block - 1))
- goto nak;
- tmp = ~blk;
- if (cblk != tmp)
- goto nak;
- if (mode) {
- update_crc(0);
- update_crc(0);
- if (c1 != crc1 || c2 != crc2)
- goto nak;
- }
- else {
- if (cksum != sum)
- goto nak;
- }
- if (block == blk) {
- fflush(fp);
- fwrite(sector, sizeof(sector[0]), SECSIZE, fp);
- }
- block = blk + 1;
- putchar(ACK); /* got it! */
- errcount = 0;
- continue;
-
- nak: putchar(NAK); /* do it over */
- errcount++;
- }
- if (errcount == MAXERRORS) {
- error();
- return;
- }
- putchar(ACK);
- for (i = SECSIZE -1; i >= 0; i--) { /* find true EOF */
- if (sector[i] != CPMEOF) {
- stop = i;
- break;
- }
- }
- /*
- * Some CPM systems don't pad the end of the file with ^Z's so the file may
- * have junk at the end. A conservative approach had to be taken in order
- * for Unix object code (where ^Z's may be valid data) to transfer properly.
- */
- true_end = ftell(fp) - SECSIZE + stop +1;
- fclose(fp);
- truncate(tfile, true_end);
- return;
- }
-
- /* give minimal usage message */
- usage()
- {
- fprintf(stderr, "Usage: xmodem [ s | r ] filename\r\n");
- fprintf(stderr, " options are 's' for send or 'r' for receive\r\n");
- return;
- }
-
- /* exceeded the maximum number of retry's */
- error()
- {
- putchar(CAN);
- putchar(CAN);
- putchar(CAN);
- putchar(CAN);
- sleep(3);
- fprintf(stderr, "\r\nxmodem: Exceeded error limit...aborting\r\n");
- return;
- }
-
- /* update the CRC bytes */
- update_crc(c)
- unsigned char c;
- {
- int i, temp;
- unsigned char carry, c_crc1, c_crc2;
- for (i=0; i < 8; i++) {
- temp = c * 2;
- c = temp; /* rotate left */
- carry = ((temp > 255) ? 1 : 0);
- temp = crc2 * 2;
- crc2 = temp;
- crc2 |= carry; /* rotate with carry */
- c_crc2 = ((temp > 255) ? 1 : 0);
- temp = crc1 * 2;
- crc1 = temp;
- crc1 |= c_crc2;
- c_crc1 = ((temp > 255) ? 1 : 0);
- if (c_crc1) {
- crc2 ^= 0x21;
- crc1 ^= 0x10;
- }
- }
- return;
- }
-
- /* getchar with a 10 sec time out */
- getchar_t()
- {
- int force_it();
- unsigned char c;
- signal(SIGALRM, force_it);
- alarm(10); /* only have 10 sec... */
- c = getchar();
- alarm(0);
- return(c);
- }
-
- /*
- * This code (and the resync() below) is the most machine dependent part
- * of the program. The action of the signal SIGALRM during a read system
- * call is not well defined. Some systems return the stack to the point
- * outside the system call, others inside the call itself. Have fun...
- */
- force_it()
- {
- unsigned char c;
- c = CPMEOF; /* arbitrary default char */
- #ifdef SV
- ungetc(c, stdin);
- #endif
- #ifdef BSD
- ioctl(0, TIOCSTI, &c);
- #endif
- return;
- }
-
- /* truncate file to given length */
- truncate(path, length)
- char *path;
- long length;
- {
- FILE *fp, *tempfp;
- long i;
- char c, string[80], *tempfile, *mktemp();
- if (!(fp = fopen(path, "r"))) {
- fprintf(stderr, "xmodem: Can't open '%s' for read\r\n", path);
- return;
- }
- tempfile = mktemp("/tmp/trunXXXXXX");
- if (!(tempfp = fopen(tempfile, "w"))) {
- fprintf(stderr, "xmodem: Can't open temporary file\r\n");
- return;
- }
- for (i=0; i < length; i++) {
- c = fgetc(fp);
- fputc(c, tempfp);
- }
- fclose(fp);
- fclose(tempfp);
- sprintf(string, "mv %s %s", tempfile, path);
- system(string);
- return;
- }
-
- /* put the stdin/stdout in the "raw" mode */
- rawmode()
- {
- #ifdef SV
- struct termio tbuf;
- ioctl(0, TCGETA, &tbuf);
- tbuf.c_cc[4] = 1; /* VMIN */
- tbuf.c_cc[5] = 0; /* VTIME */
- tbuf.c_iflag = 0;
- tbuf.c_oflag = 0;
- tbuf.c_lflag = 0;
- tbuf.c_cflag &= ~CSIZE;
- tbuf.c_cflag |= CS8;
- tbuf.c_cflag &= ~PARENB;
- ioctl(0, TCSETAF, &tbuf);
- return;
- #endif
- #ifdef BSD
- struct sgttyb sgbuf;
- ioctl(0, TIOCGETP, &sgbuf);
- sgbuf.sg_flags |= RAW;
- sgbuf.sg_flags &= ~ECHO;
- ioctl(0, TIOCSETP, &sgbuf);
- return;
- #endif
- }
-
- /* after 6 seconds of reading junk data... */
- resync()
- {
- char c;
- synchron = 1; /* set the flag */
- c = SOH;
- #ifdef SV
- ungetc(c, stdin);
- #endif
- #ifdef BSD
- ioctl(0, TIOCSTI, &c);
- #endif
- return;
- }
-