grandi@noao.arizona.edu (Steve Grandi) (03/05/88)
The recent posting of my xmodem program (v13 i093-095) had an incomplete first shar file. Here are the missing pieces. Also, the second shar seems to have a hiccup (an extra CR) in the first two lines which results in a spurious, but harmless, error message. Steve Grandi, National Optical Astronomy Observatories, Tucson AZ, 602-325-9228 UUCP: {arizona,decvax,hao,ihnp4}!noao!grandi or uunet!noao.arizona.edu!grandi Internet: grandi@noao.arizona.edu SPAN/HEPNET: 5356::GRANDI or DRACO::GRANDI *************************************************************************** : This is a shar archive. Extract with sh, not csh. echo x - xmodem.h sed -e 's/^X//' > xmodem.h << '!Funky!Stuff!' X#include <ctype.h> X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/time.h> X#include <sgtty.h> X#include <signal.h> X X/* define macros to print messages in log file */ X#define logit(string) if(LOGFLAG)fprintf(LOGFP,string) X#define logitarg(string,argument) if(LOGFLAG)fprintf(LOGFP,string,argument) X X#define VERSION 34 /* Version Number */ X#define FALSE 0 X#define TRUE 1 X 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 X/* XMODEM Constants */ X#define TIMEOUT -1 X#define ERRORMAX 10 /* maximum errors tolerated while transferring a packet */ X#define WAITFIRST 1 /* seconds between startup characters in read */ X#define STERRORMAX 60 /* maximum "errors" tolerated in read startup */ X#define CRCSWMAX 30 /* maximum time to try CRC mode before switching */ X#define NAKMAX 120 /* maximum times to wait for initial NAK when sending */ X#define RETRYMAX 5 /* maximum retries to be made certain handshaking routines */ X#define KSWMAX 5 /* maximum errors before switching to 128 byte packets */ X#define EOTMAX 10 /* maximum times sender will send an EOT to end transfer */ X#define SLEEPNUM 100 /* target number of characters to collect during sleepy time */ X#define BBUFSIZ 1024 /* buffer size */ X#define NAMSIZ 11 /* length of a CP/M file name string */ X#define CTRLZ 032 /* CP/M EOF for text (usually!) */ X#define CRCCHR 'C' /* CRC request character */ X#define KCHR 'K' /* 1K block request character */ X#define BAD_NAME 'u' /* Bad filename indicator */ X X#define CREATMODE 0644 /* mode for created files */ X X/* GLOBAL VARIABLES */ X Xint ttyspeed; /* tty speed (bits per second) */ Xunsigned char buff[BBUFSIZ]; /* buffer for data */ Xint nbchr; /* number of chars read so far for buffered read */ Xlong filelength; /* length specified in YMODEM header */ Xlong fileread; /* characters actually read so far in file */ Xchar filename[256]; /* place to construct filenames */ XFILE *LOGFP; /* descriptor for LOG file */ X X/* option flags and state variables */ Xchar XMITTYPE; /* text or binary? */ Xint DEBUG; /* keep debugging info in log? */ Xint RECVFLAG; /* receive? */ Xint SENDFLAG; /* send? */ Xint BATCH; /* batch? (Now used as a state variable) */ Xint CRCMODE; /* CRC or checksums? */ Xint DELFLAG; /* don't delete old log file? */ Xint LOGFLAG; /* keep log? */ Xint LONGPACK; /* do not use long packets on transmit? */ Xint MDM7BAT; /* MODEM7 batch protocol */ Xint YMDMBAT; /* YMODEM batch protocol */ Xint TOOBUSY; /* turn off sleeping in packet read routine */ Xint CHECKLENGTH; /* Are we truncating a file to a YMODEM length? */ X X X/* CRC-16 constants. From Usenet contribution by Mark G. Mendel, X Network Systems Corp. (ihnp4!umn-cs!hyper!mark) X*/ X X /* the CRC polynomial. */ X#define P 0x1021 X X /* number of bits in CRC */ X#define W 16 X X /* the number of bits per char */ X#define B 8 !Funky!Stuff! echo x - xmodem.c sed -e 's/^X//' > xmodem.c << '!Funky!Stuff!' X/* X * XMODEM -- Implements the Christensen XMODEM protocol, X * for packetized file up/downloading. X * X * I have tried to keep the 4.2isms (select system call, 4.2BSD/v7 tty X * structures, gettimeofday system call, etc.) in the source file X * getput.c; but I make no guarantees. Also, I have made no attempt to X * keep variable names under 7 characters (although a cursory check X * shows that all variables are unique within 7 first characters). X * See the README file for some notes on SYS V adaptations. X * The program has been successfully run on VAXes (4.3BSD) and SUN-3s X * (2.x/3.x) against MEX-PC and ZCOMM/DSZ. X * X * -- Based on UMODEM 3.5 by Lauren Weinstein, Richard Conn, and others. X * X * XMODEM Version 1.0 - by Brian Kantor, UCSD (3/84) X * X * Version 2.0 (CRC-16 and Modem7 batch file transfer) -- Steve Grandi, NOAO (5/85) X * X * Version 2.1 (1K packets) -- Steve Grandi, NOAO (7/85) X * X * Version 2.2 (general clean-ups and multi-character read speed-ups) -- Steve Grandi, NOAO (9/85) X * X * Version 2.3 (napping while reading packet; split into several source files) -- Steve Grandi, NOAO (1/86) X * X * Version 3.0 (Ymodem batch receive; associated changes) -- Steve Grandi, NOAO (2/86) X * X * Version 3.1 (Ymodem batch send; associated changes) -- Steve Grandi, NOAO (8/86) X * X * Version 3.2 (general cleanups) -- Steve Grandi, NOAO (9/86) X * X * Released to the world (1/87) X * X * Version 3.3 (see update.doc) -- Steve Grandi, NOAO (5/87) X * X * Version 3.4 (see update.doc) -- Steve Grandi, NOAO (10/87) X * X * Released to the world (1/88) X * X * Please send bug fixes, additions and comments to: X * {ihnp4,hao}!noao!grandi grandi@noao.arizona.edu X */ X X#include "xmodem.h" X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char *getenv(); X FILE *fopen(); X char *unix_cpm(); X char *strcpy(); X char *strcat(); X X char *fname = filename; /* convenient place to stash file names */ X char *logfile = "xmodem.log"; /* Name of LOG File */ X X char *stamptime(); /* for timestamp */ X X char *defname = "xmodem.in"; /* default file name if none given */ X X struct stat filestatbuf; /* file status info */ X X int index; X char flag; X long expsect; X X /* initialize option flags */ X X XMITTYPE = 't'; /* assume text transfer */ X DEBUG = FALSE; /* keep debugging info in log */ X RECVFLAG = FALSE; /* not receive */ X SENDFLAG = FALSE; /* not send either */ X BATCH = FALSE; /* nor batch */ X CRCMODE = FALSE; /* use checksums for now */ X DELFLAG = FALSE; /* don't delete old log file */ X LOGFLAG = TRUE; /* keep log */ X LONGPACK = FALSE; /* do not use long packets on transmit */ X MDM7BAT = FALSE; /* no MODEM7 batch mode */ X YMDMBAT = FALSE; /* no YMODEM batch mode */ X TOOBUSY = FALSE; /* not too busy for sleeping in packet read */ X X printf("XMODEM Version %d.%d", VERSION/10, VERSION%10); X printf(" -- UNIX-Microcomputer Remote File Transfer Facility\n"); X X if (argc == 1) X { X help(); X exit(-1); X } X X index = 0; /* set index for flag loop */ X X while ((flag = argv[1][index++]) != '\0') X switch (flag) { X case '-' : break; X case 'X' : X case 'x' : DEBUG = TRUE; /* turn on debugging log */ X break; X case 'C' : X case 'c' : CRCMODE = TRUE; /* enable CRC on receive */ X break; X case 'D' : X case 'd' : DELFLAG = TRUE; /* delete log file */ X break; X case 'L' : X case 'l' : LOGFLAG = FALSE; /* turn off log */ X break; X case 'm' : X case 'M' : MDM7BAT = TRUE; /* turn on MODEM7 batch protocol */ X BATCH = TRUE; X break; X case 'y' : X case 'Y' : YMDMBAT = TRUE; /* turn on YMODEM batch protocol */ X BATCH = TRUE; X break; X case 'k' : X case 'K' : LONGPACK = TRUE; /* use 1K packets on transmit */ X break; X case 't' : X case 'T' : TOOBUSY = TRUE; /* turn off sleeping */ X break; X case 'R' : X case 'r' : RECVFLAG = TRUE; /* receive file */ X XMITTYPE = gettype(argv[1][index++]); /* get t/b */ X break; X case 'S' : X case 's' : SENDFLAG = TRUE; /* send file */ X XMITTYPE = gettype(argv[1][index++]); X break; X default : printf ("Invalid Flag %c ignored\n", flag); X break; X } X X if (DEBUG) X LOGFLAG = TRUE; X X if (LOGFLAG) X { X if ((fname = getenv("HOME")) == 0) /* Get HOME variable */ X error("Fatal - Can't get Environment!", FALSE); X fname = strcat(fname, "/"); X fname = strcat(fname, logfile); X if (!DELFLAG) X LOGFP = fopen(fname, "a"); /* append to LOG file */ X else X LOGFP = fopen(fname, "w"); /* new LOG file */ X if (!LOGFP) X error("Fatal - Can't Open Log File", FALSE); X X fprintf(LOGFP,"\n++++++++ %s", stamptime()); X fprintf(LOGFP,"XMODEM Version %d.%d\n", VERSION/10, VERSION%10); X fprintf(LOGFP,"Command line: %s %s", argv[0], argv[1]); X for (index=2; index<argc; ++index) X fprintf(LOGFP, " %s", argv[index]); X fprintf(LOGFP, "\n"); X } X X getspeed(); /* get tty-speed for time estimates */ X X if (RECVFLAG && SENDFLAG) X error("Fatal - Both Send and Receive Functions Specified", FALSE); X X if (MDM7BAT && YMDMBAT) X error("Fatal - Both YMODEM and MODEM7 Batch Protocols Specified", FALSE); X X if (!RECVFLAG && !SENDFLAG) X error("Fatal - Either Send or Receive Function must be chosen!",FALSE); X X if (SENDFLAG && argc==2) X error("Fatal - No file specified to send",FALSE); X X if (RECVFLAG && argc==2) X { X /* assume we really want CRC-16 in batch, unless we specify MODEM7 mode */ X CRCMODE = MDM7BAT ? FALSE : TRUE; X printf("Ready for BATCH RECEIVE"); X printf(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X printf("Send several Control-X characters to cancel\n"); X logit("Batch Receive Started"); X logitarg(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X strcpy(fname, defname); X } X X if (RECVFLAG && argc>2) X { X if(open(argv[2], 0) != -1) /* check for overwriting */ X { X logit("Warning -- Target File Exists and is Being Overwritten\n"); X printf("Warning -- Target File Exists and is Being Overwritten\n"); X } X printf("Ready to RECEIVE File %s", argv[2]); X printf(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X printf("Send several Control-X characters to cancel\n"); X logitarg("Receiving in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X strcpy(fname,argv[2]); X } X X if (RECVFLAG) X { X setmodes(); /* set tty modes for transfer */ X X while(rfile(fname) != FALSE); /* receive files */ X X restoremodes(FALSE); /* restore normal tty modes */ X X sleep(2); /* give other side time to return to terminal mode */ X exit(0); X } X X if (SENDFLAG && BATCH) X { X if (YMDMBAT) X { X printf("Ready to YMODEM BATCH SEND"); X printf(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X logit("YMODEM Batch Send Started"); X logitarg(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X } X else if (MDM7BAT) X { X printf("Ready to MODEM7 BATCH SEND"); X printf(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X logit("MODEM7 Batch Send Started"); X logitarg(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X } X printf("Send several Control-X characters to cancel\n"); X X setmodes(); X for (index=2; index<argc; index++) { X if (stat(argv[index], &filestatbuf) < 0) { X logitarg("\nFile %s not found\n", argv[index]); X continue; X } X sfile(argv[index]); X } X sfile(""); X restoremodes(FALSE); X X logit("Batch Send Complete\n"); X sleep(2); X exit (0); X } X X if (SENDFLAG && !BATCH) X { X if (stat(argv[2], &filestatbuf) < 0) X error("Can't find requested file", FALSE); X expsect = (filestatbuf.st_size/128)+1; X X printf("File %s Ready to SEND", argv[2]); X printf(" in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X printf("Estimated File Size %ldK, %ld Sectors, %ld Bytes\n", X (filestatbuf.st_size/1024)+1, expsect, X filestatbuf.st_size); X projtime(expsect, stdout); X printf("Send several Control-X characters to cancel\n"); X logitarg("Sending in %s mode\n", (XMITTYPE == 't') ? "text" : "binary"); X X setmodes(); X sfile(argv[2]); X restoremodes(FALSE); X X sleep(2); X exit(0); X } X} !Funky!Stuff! echo x - batch.c sed -e 's/^X//' > batch.c << '!Funky!Stuff!' X/* X * Various routines for batch tranfer X */ X X#include "xmodem.h" X X/* make sure filename sent or received in YMODEM batch is canonical. X * Turn Unix '/' into CP/M ':' and translate to all lower case. X * Remove trailing dot in incoming name. X */ X Xunixify (name) Xchar *name; X { X char *ptr; X for (ptr=name; *ptr; ++ptr) X { X if (*ptr == '/') X *ptr = ':'; X if (isupper (*ptr)) X *ptr |= 040; X } X ptr--; X if (*ptr == '.') X *ptr = '\0'; X } X Xcpmify (name) Xchar *name; X { X char *ptr; X for (ptr=name; *ptr; ++ptr) X { X if (*ptr == ':') X *ptr = '/'; X if (isupper (*ptr)) X *ptr |= 040; X } X } X X X/* convert a CP/M file name received in a MODEM7 batch transfer X * into a unix file name mapping '/' into ':', converting to all X * upper case and adding dot in proper place. X * Use "filename" to hold name. X * Code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xchar *cpm_unix (string) Xunsigned char *string; X{ X register int i; X unsigned char *iptr, temp; X register char *optr; X X if (*string == '\0') X error("Null file name in MODEM7 batch receive", TRUE); X X for (iptr=string; (temp = *iptr) ; ) { X temp &= 0177; /* strips bit 7 */ X if (isupper(temp)) X temp |= 040; /* set bit 5 for lower case */ X if (temp == '/') X temp=':'; /* map / into : */ X *iptr++ = temp; X } X X /* put in main part of name */ X iptr=string; X optr=filename; X for (i=0; i<8; i++) { X if (*iptr != ' ') X *optr++ = *iptr++; X } X X /* add dot if necessary */ X if (string[8] != ' ' || string[9] != ' ' || string[10] != ' ') X *optr++ = '.'; X X /* put in extension */ X iptr = &string[8]; X for (i=0; i<3; i++) { X if (*iptr != ' ') X *optr++ = *iptr++; X } X X *optr++ = '\000'; X return (filename); X} X X/* Send 11 character CP/M filename for MODEM7 batch transmission X * Returns -1 for a protocol error; 0 if successful X * NOTE: we tromp a little on the argument string! X * code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xsend_name(name) Xchar *name; X{ X register int cksum; X register char *ptr; X X xmdebug("send_name"); X X /* append cp/m EOF */ X name[NAMSIZ] = CTRLZ; X name[NAMSIZ+1] = '\000'; X X /* create checksum */ X ptr = name; X cksum = 0; X while (*ptr) X cksum += *ptr++; X cksum &= 0x00FF; X X /* send filename */ X X sendbyte(ACK); X ptr = name; X sendbyte(*ptr++); X X while (*ptr) { X X switch (readbyte(15)) { X X case ACK: break; X X case TIMEOUT: { X logit("Timeout while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X X default: { X logit("Error while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X } X X sendbyte (*ptr++); X } X X /* Check checksum returned by other side against my value */ X if (readbyte(16) != cksum) { X logit("Bad checksum while sending MODEM7 filename\n"); X sendbyte(BAD_NAME); X return (-1); X } X X sendbyte(ACK); X return (0); X} X X/* Convert Unix filename to 11 character CP/M file name (8 char name, X * 3 char extension, dot in between is not included). X * map ':' into '/'; Use filename to hold name. X * code stolen from D. Thompson's (IRTF) xmodem.c X */ X Xchar *unix_cpm(string) Xchar *string; X{ X register char *iptr, *optr, temp; X int i; X X char *rindex(); X char *strcpy(); X X /* blank 11 character name */ X (void) strcpy (filename," "); X X /* strip off any path name */ X if ((iptr = rindex(string,'/'))) X iptr++; X else X iptr=string; X X /* skip leading '.'s */ X while (*iptr == '.') X iptr++; X X /* copy main part of name */ X optr = filename; X i = 8; X while ((i--) && (*iptr) && (*iptr != '.')) X *optr++ = *iptr++; X X /* advance to unix extension, or end of unix name */ X while ((*iptr != '.') && (*iptr)) X iptr++; X X /* skip over the '.' */ X while (*iptr == '.') X iptr++; X X /* copy extension */ X optr = &filename[8]; X i=3; X while ((i--) && (*iptr) && (*iptr != '.')) X *optr++ = *iptr++; X X filename[NAMSIZ] = '\000'; X X /* Fuss with name */ X for (iptr=filename; (temp = *iptr) ;) { X temp &= 0177; /* strip bit 7 (parity bit) */ X if (islower(temp)) X temp &= ~040; /* make upper case */ X if (temp == ':') X temp ='/'; /* map ':' into '/' */ X *iptr++ = temp; X } X X if (DEBUG) X fprintf (LOGFP, "DEBUG: File %s sent as %s\n", string, filename); X X return(filename); X} !Funky!Stuff! exit -- Steve Grandi, National Optical Astronomy Observatories, Tucson AZ, 602-325-9228 UUCP: {arizona,decvax,hao,ihnp4}!noao!grandi or uunet!noao.arizona.edu!grandi Internet: grandi@noao.arizona.edu SPAN/HEPNET: 5356::GRANDI or DRACO::GRANDI