grandi@noao.UUCP (Steve Grandi) (02/19/85)
The following stuff will enable the 4.2BSD tip program to send and receive files using the Christensen xmodem protocol for packetized file up/downloading. I used the xmodem program written by Brian Kantor (distributed on the net in March 1984) and sliced it and diced it to fit into tip. Note that this code runs ONLY on 4.2BSD since it uses the 'select' system call. Four new '~' commands are added to tip: ~{ Receive xmodem text file ~} Send xmodem text file ~( Receive xmodem binary file ~) Send xmodem binary file Each of these commands will ask for a local file name to send/receive. The following shar archive contains two files. diffs.cmdtab.c is a context diff file which should be applied to /usr/src/usr.bin/tip/cmdtab.c (use patch if you have it). add.cmds.c is a file which should be appended in its entirety to the end of /usr/src/usr.bin/tip/cmds.c. Anyone willing to jam the new unix kermit into tip? -- Steve Grandi, National Optical Astronomy Observatories, Tucson, AZ USA UUCP: {allegra,arizona,astrovax,decvax,hao,ihnp4,lbl-csam,seismo} !noao!grandi Arpa: noao!grandi@lbl-csam -------------------------------------------------------------------------------- : This is a shar archive. Extract with sh, not csh. echo x - diffs.cmdtab.c sed -e 's/^X//' > diffs.cmdtab.c << '!Funky!Stuff!' X*** cmdtab.c.ORIG Sat Jun 25 01:19:08 1983 X--- cmdtab.c Thu Sep 6 08:30:29 1984 X*************** X*** 7,12 X extern int shell(), getfl(), sendfile(), chdirectory(); X extern int finish(), help(), pipefile(), consh(), variable(); X extern int cu_take(), cu_put(), dollar(), genbrk(), suspend(); X X esctable_t etable[] = { X { '!', NORM, "shell", shell }, X X--- 7,13 ----- X extern int shell(), getfl(), sendfile(), chdirectory(); X extern int finish(), help(), pipefile(), consh(), variable(); X extern int cu_take(), cu_put(), dollar(), genbrk(), suspend(); X+ extern int rtfile(), rbfile(), stfile(), sbfile(); X X esctable_t etable[] = { X { '!', NORM, "shell", shell }, X*************** X*** 25,29 X { 's', NORM, "set variable", variable }, X { '?', NORM, "get this summary", help }, X { '#', NORM, "send break", genbrk }, X { 0, 0, 0 } X }; X X--- 26,34 ----- X { 's', NORM, "set variable", variable }, X { '?', NORM, "get this summary", help }, X { '#', NORM, "send break", genbrk }, X+ { '{', NORM, "receive xmodem text file", rtfile }, X+ { '}', NORM, "send xmodem text file", stfile }, X+ { '(', NORM, "receive xmodem binary file", rbfile }, X+ { ')', NORM, "send xmodem binary file", sbfile }, X { 0, 0, 0 } X }; !Funky!Stuff! echo x - add.cmds.c sed -e 's/^X//' > add.cmds.c << '!Funky!Stuff!' X/* Place this code at the end of cmds.c */ X X/* XMODEM stuff; sag sept 1984 - Feb 1985 X * Taken from Brian Kantor's (sdccsu3!brian) xmodem version 1.0 X * Note that this code will work on version 4.2 ONLY (uses select) X */ X X#include <sys/stat.h> X#include <sys/time.h> X X/* ASCII Constants */ X#define SOH 001 X#define STX 002 X#define ETX 003 X#define EOT 004 X#define ENQ 005 X#define ACK 006 X#define LF 012 /* Unix LF/NL */ X#define CR 015 X#define NAK 025 X#define SYN 026 X#define CAN 030 X#define ESC 033 X#define CTRLZ 032 /* CP/M EOF for text (usually!) */ X X/* XMODEM Constants */ X#define TIMEOUT -1 X#define ERRORMAX 10 /* maximum errors tolerated */ X#define RETRYMAX 10 /* maximum retries to be made */ X#define BBUFSIZ 128 /* buffer size -- do not change! */ X#define DEBUG 0 /* 1 for debugging output */ X#define GETERR -10 /* error code for getbyte routine */ X Xchar buff[BBUFSIZ]; X Xint nbchr; /* number of chars read so far for buffered read */ X X/* Receive a text file */ Xrtfile(c) Xchar c; X{ X putchar (c); X rfile ('t'); X} X X/* Receive a binary file */ Xrbfile(c) Xchar c; X{ X putchar (c); X rfile ('b'); X} X X/* Send a text file */ Xstfile(c) Xchar c; X{ X putchar (c); X sfile ('t'); X} X X/* Send a binary file */ Xsbfile(c) Xchar c; X{ X putchar (c); X sfile ('b'); X} X X/* print error message and cleanup for exit */ Xerror(msg) Xchar *msg; X { X printf("\r\nXMODEM: %s\n", msg); X ioctl (0, TIOCSETC, &tchars); X write (fildes[1], (char *)&ccc, 1); X signal (SIGINT, SIG_DFL); X } X X/* X * X * Get a byte from the specified file. Buffer the read so we don't X * have to use a system call for each character. X * X */ Xgetbyte(fildes, ch) /* Buffered disk read */ Xint fildes; Xchar *ch; X X { X static char buf[BUFSIZ]; /* Remember buffer */ X static char *bufp = buf; /* Remember where we are in buffer */ X X if (nbchr == 0) /* Buffer exausted; read some more */ X { X if ((nbchr = read(fildes, buf, BUFSIZ)) < 0) X { X error("File Read Error"); X return (GETERR); X } X bufp = buf; /* Set pointer to start of array */ X } X if (--nbchr >= 0) X { X *ch = *bufp++; X return(0); X } X else X return(EOF); X } X X/** receive a file **/ Xrfile(mode) Xchar mode; X { X register int bufctr, checksum; X register int c; X int j, firstchar, sectnum, sectcurr, tmode; X int sectcomp, errors, errorflag, recfin; X int fatalerror, inchecksum; X long recvsectcnt; X X int (*f) (); X char *cp, *expand(); X time_t start; X X if (prompt (" Receive local file name? ", copyname)) X return; X cp = expand (copyname); X if ((sfd = creat (cp, 0666)) < 0) X { X printf ("\r\n%s: Cannot creat\r\n", copyname); X return; X } X X kill (pid, SIGIOT); X read (repdes[0], (char *)&ccc, 1); /* wait until read process stops */ X ioctl (0, TIOCSETC, &defchars); /* set tty modes */ X f = signal (SIGINT, intcopy); /* intercept control-c */ X X start = time(0); X quit = 0; X (void) setjmp(intbuf); /* set control-c catcher */ X X if (quit) X { X error("Control-C; Reception canceled"); X close (sfd); X return; X } X X printf("XMODEM: Ready to RECEIVE File %s", cp); X puts("\r\nControl-C to cancel.\n"); X X recfin = FALSE; X sectnum = errors = 0; X fatalerror = FALSE; /* NO fatal errors */ X recvsectcnt = 0; /* number of received sectors */ X X if (mode == 't') X tmode = TRUE; X else X tmode = FALSE; X X sendbyte(NAK); /* Start up the sender's first block */ X X do X { X errorflag = FALSE; X do X { X firstchar = readbyte(6); X } X while ((firstchar != SOH) X && (firstchar != EOT) X && (firstchar != TIMEOUT) X && ((firstchar & 0x7f) != CAN)); X X if (firstchar == TIMEOUT) X { X printf("\r\nTimeout on Sector %d\n", sectnum+1); X errorflag = TRUE; X } X X if ((firstchar & 0x7f) == CAN) X { X error("Reception canceled at user's request"); X close (sfd); X return; X } X X if (firstchar == SOH) X { X sectcurr = readbyte(3); /* get sector numbers */ X sectcomp = readbyte(3); X if ((sectcurr + sectcomp) == 0xff) X { X if (sectcurr == ((sectnum+1) & 0xff)) X { X checksum = 0; X for (j = bufctr = 0; j < BBUFSIZ; j++) X { X buff[bufctr] = c = readbyte(3); X checksum = ((checksum+c) & 0xff); X if (!tmode) /* binary mode */ X { X bufctr++; X continue; X } X if (c == CR) X continue; /* skip CR's */ X if (c == CTRLZ) /* CP/M EOF char */ X { X recfin = TRUE; /* flag EOF */ X continue; X } X if (!recfin) X bufctr++; X } X inchecksum = readbyte(3); /* get checksum */ X if (checksum == inchecksum) /* good checksum */ X { X errors = 0; X recvsectcnt++; X sectnum = sectcurr; X if (DEBUG) printf ("\n"); X printf ("\rreceived sector %-4d", recvsectcnt); X if (write(sfd, buff, bufctr) < 0) X { X sendbyte(CAN); X error("File Write Error"); X close (sfd); X return; X } X else X sendbyte(ACK); X X } X else X { X printf("\r\nChecksum Error on Sector %d\n", sectnum+1); X errorflag = TRUE; X } X } X else X { X if (sectcurr == sectnum) X { X while(readbyte(3) != TIMEOUT) X ; X sendbyte(ACK); X } X else X { X printf("\r\nPhase Error - Received Sector is "); X printf("%d while Expected Sector is %d\n", X sectcurr, ((sectnum+1) & 0xff)); X errorflag = TRUE; X fatalerror = TRUE; X sendbyte(CAN); X } X } X } X else X { X printf("\r\nHeader Sector Number Error on Sector %d\n", X sectnum+1); X errorflag = TRUE; X } X } X X if (errorflag) X { X errors++; X while (readbyte(3) != TIMEOUT) X ; X sendbyte(NAK); X } X } X while ((firstchar != EOT) && (errors < ERRORMAX) && !fatalerror); X X if ((firstchar == EOT) && (errors < ERRORMAX) && !fatalerror) X { X close(sfd); X sendbyte(ACK); X printf("\n\rReceive Complete"); X printf("\n\r%ld CP/M Records ", recvsectcnt); X prtime(" transferred in ", time(0)-start); X X ioctl (0, TIOCSETC, &tchars); /* reset ttys */ X write (fildes[1], (char *)&ccc, 1); /* wakeup tip */ X signal (SIGINT, SIG_DFL); /* reset control-c catcher */ X } X else X { X sendbyte(CAN); X error("\r\nABORTED -- Too Many Errors"); X close (sfd); X return; X } X } X X/** send a file **/ Xsfile(mode) Xchar mode; X { X register int bufctr, checksum, sectnum; X char blockbuf[134]; X int fd, attempts; X int nlflag, sendfin, tmode; X int bbufcnt; X int firstchar; X int getretrn; X char c; X int sendresp; /* response char to sent block */ X X int (*f) (); X char *cp, *expand(); X time_t start; X X if (prompt (" Send local file name? ", copyname)) X return; X cp = expand (copyname); X if ((fd = open(cp, 0)) < 0) X { X printf("Can't open file for send\n"); X return; X } X X kill (pid, SIGIOT); X read (repdes[0], (char *)&ccc, 1); /* wait until read process stops */ X ioctl (0, TIOCSETC, &defchars); /* setup tty */ X f = signal (SIGINT, intcopy); /* prepare to catch control-c */ X start = time(0); X quit = 0; X (void) setjmp(intbuf); /* setup control-c catcher */ X X nbchr = 0; /* clear buffered read char count */ X X if (quit) X { X sendbyte(CAN); X error("Control-C; Send canceled"); X close (fd); X return; X } X X printf("XMODEM: File %s Ready to SEND", cp); X prfilestat(cp); /* print file size statistics */ X puts("\r\nControl-C to cancel.\n"); X X if (mode == 't') X tmode = TRUE; X else X tmode = FALSE; X X sendfin = nlflag = FALSE; X attempts = 0; X X while (((firstchar=readbyte(30)) != NAK) && (firstchar != CAN)) X { X if (++attempts > RETRYMAX) { X error("Remote System Not Responding"); X close (fd); X return; X } X } X X if ((firstchar & 0x7f) == CAN) X { X error("Send cancelled at user's request."); X close (fd); X return; X } X X sectnum = 1; /* first sector number */ X attempts = 0; X X do X { X for (bufctr=0; bufctr < BBUFSIZ;) X { X if (nlflag) X { X buff[bufctr++] = LF; /* leftover newline */ X nlflag = FALSE; X } X getretrn = getbyte(fd, &c); X if (getretrn == GETERR) X { X sendbyte(CAN); X error ("Read error on local file"); X close (fd); X return; X } X if (getretrn == EOF) X { X sendfin = TRUE; /* this is the last sector */ X if (!bufctr) /* if EOF on sector boundary */ X break; /* avoid sending extra sector */ X if (tmode) X buff[bufctr++] = CTRLZ; /* Control-Z for CP/M EOF */ X else X bufctr++; X continue; X } X X if (tmode && c == LF) /* text mode & Unix newline? */ X { X buff[bufctr++] = CR; /* insert carriage return */ X if (bufctr < BBUFSIZ) X buff[bufctr++] = LF; /* insert LF */ X else X nlflag = TRUE; /* insert on next sector */ X } X else X buff[bufctr++] = c; /* copy the char without change */ X } X X attempts = 0; X X if (!bufctr) /* if EOF on sector boundary */ X break; /* avoid sending empty sector */ X X do X { X bbufcnt = 0; /* start building block to be sent */ X blockbuf[bbufcnt++] = SOH; /* start of packet char */ X blockbuf[bbufcnt++] = sectnum; /* current sector # */ X blockbuf[bbufcnt++] = -sectnum-1; /* and its complement */ X X checksum = 0; /* init checksum */ X for (bufctr=0; bufctr < BBUFSIZ; bufctr++) X { X blockbuf[bbufcnt++] = buff[bufctr]; X checksum = ((checksum+buff[bufctr]) & 0xff); X } X X blockbuf[bbufcnt++] = checksum; X write(FD, blockbuf, 132); /* write the block */ X ioctl(FD,TIOCFLUSH,0); X X attempts++; X sendresp = readbyte(10); /* get response */ X if (sendresp != ACK) X { X printf("\r\nNon-ACK Received on Sector %d\n",sectnum); X if (sendresp == TIMEOUT) X printf("\r\nThis Non-ACK was a TIMEOUT\n"); X } X } X while((sendresp != ACK) && (attempts < RETRYMAX)); X X sectnum++; /* increment to next sector number */ X if (DEBUG) printf ("\n"); X printf ("\rsent sector %-4d", sectnum-1); X } X while (!sendfin && (attempts < RETRYMAX)); X X if (attempts >= RETRYMAX) X { X error("Remote System Not Responding"); X close (fd); X return; X } X X attempts = 0; X sendbyte(EOT); /* send 1st EOT */ X X while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX)) X sendbyte(EOT); X X if (attempts >= RETRYMAX) X error("Remote System Not Responding on Completion"); X X close(fd); X printf("\r\nSend Complete\r\n"); X prtime("Data transferred in ", time(0)-start); X X ioctl (0, TIOCSETC, &tchars); /* restore tty */ X write (fildes[1], (char *)&ccc, 1); /* wakeup tip */ X signal (SIGINT, SIG_DFL); /* reset control-c catcher */ X sleep(5); /* give other side time to return to terminal mode */ X X } X X/* print file size status information */ Xprfilestat(name) Xchar *name; X { X struct stat filestatbuf; /* file status info */ X X stat(name, &filestatbuf); /* get file status bytes */ X printf("\r\nEstimated File Size %ldK, %ld Records, %ld Bytes", X (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, X filestatbuf.st_size); X return; X } X X/* get a byte from data stream -- timeout if "seconds" elapses */ Xint readbyte(seconds) Xint seconds; X { X int i, readfd; X char c; X struct timeval tmout; X X tmout.tv_sec = seconds; X tmout.tv_usec = 0; X X readfd = 1 << FD; X X if ((i=select(FD+1, &readfd, 0, 0, &tmout)) == 0) X { X return(TIMEOUT); X } X X read(FD, &c, 1); X X return(c & 0xff); /* return the char */ X } X X/* send a byte to data stream */ Xsendbyte(data) Xchar data; X { X write(FD, &data, 1); /* write the byte */ X ioctl(FD,TIOCFLUSH,0); /* flush so it really happens now! */ X return; X } !Funky!Stuff! exit -- Steve Grandi, National Optical Astronomy Observatories, Tucson, AZ USA UUCP: {allegra,arizona,astrovax,decvax,hao,ihnp4,lbl-csam,seismo} !noao!grandi Arpa: noao!grandi@lbl-csam