Newsgroups: comp.sys.ti Path: utzoo!utgpu!jarvis.csri.toronto.edu!csri.toronto.edu!pkern From: pkern@csri.toronto.edu (pkern) Subject: a vt100 emulator in Turbo C (3 of 3) Message-ID: <8806071611.AA12898@bloor.csri.toronto.edu> Organization: University of Toronto, CSRI Date: Tue, 7 Jun 88 10:51:13 EDT #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # types.h # kermit.c # xmodem.c # This archive created: Tue Jun 7 11:28:14 1988 # By: pkern () export PATH; PATH=/bin:$PATH echo shar: extracting "'types.h'" '(139 characters)' if test -f 'types.h' then echo shar: will not over-write existing file "'types.h'" else sed 's/^X//' << \SHAR_EOF > 'types.h' X/* X * types.h: useful compatibility info X */ Xtypedef unsigned long u_long; Xtypedef u_long ino_t; Xtypedef short dev_t; Xtypedef long time_t; SHAR_EOF fi # end of overwriting check echo shar: extracting "'kermit.c'" '(38899 characters)' if test -f 'kermit.c' then echo shar: will not over-write existing file "'kermit.c'" else sed 's/^X//' << \SHAR_EOF > 'kermit.c' X/* X * K e r m i t File Transfer Utility X * X * UNIX Kermit, Columbia University, 1981, 1982, 1983 X * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell X * X * Also: Jim Guyton, Rand Corporation X * Walter Underwood, Ford Aerospace X * X * usage: kermit c [lbe line baud escapechar] to connect X * kermit s [d..iflb line baud] file ... to send files X * kermit r [d..iflb line baud] to receive files X * X * where c=connect, s=send, r=receive, X * d=debug, i=image mode, f=no filename conversion, l=tty line, X * b=baud rate, e=escape char. X * X * For remote Kermit, format is either: X * kermit r to receive files X * or kermit s file ... to send files X * X */ X X/* X * Modification History: X * X * Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS: X * - Changed MYEOL character from \n to \r. X * - Change char to int in bufill so getc would return -1 on X * EOF instead of 255 (-1 truncated to 8 bits) X * - Added read() in rpack to eat the EOL character X * - Added fflush() call in printmsg to force the output X * NOTE: The last three changes are not conditionally compiled X * since they should work equally well on any system. X * X * Changed Berkeley 4.x conditional compilation flag from X * UNIX4X to UCB4X. X * Added support for error packets and cleaned up the printing X * routines. X */ X X#include /* Standard UNIX definitions */ X X/* Conditional compilation for different machines/operating systems */ X/* One and only one of the following lines should be 1 */ X X#define UCB4X 0 /* Berkeley 4.x UNIX */ X#define TOPS_20 0 /* TOPS-20 */ X#define IBM_UTS 0 /* Amdahl UTS on IBM systems */ X#define VAX_VMS 0 /* VAX/VMS (not yet implemented) */ X X/* Conditional compilation for the different Unix variants */ X/* 0 means don't compile it, nonzero means do */ X X#if UCB4X X#define V6_LIBS 0 /* Dont't use retrofit libraries */ X#define NO_FIONREAD 0 /* We have ioctl(FIONREAD,...) for flushinput() */ X#define NO_TANDEM 0 /* We have TANDEM line discipline (xon/xoff) */ X#endif X X#if IBM_UTS X#define V6_LIBS 0 /* Don't use retrofit libraries */ X#define NO_FIONREAD 1 /* No ioctl(FIONREAD,...) for flushinput() */ X#define NO_TANDEM 1 /* No TANDEM line discipline (xon/xoff) */ X#endif X X#if V6_LIBS X#include X#include X#include X#else X#ifdef unix X#include X#include X#else X#include X#endif X#include X#endif X X#if NO_TANDEM X#define TANDEM 0 /* define it to be nothing if it's unsupported */ X#endif X X X/* Symbol Definitions */ X X#define MAXPACKSIZ 94 /* Maximum packet size */ X#define SOH 1 /* Start of header */ X#define CR 13 /* ASCII Carriage Return */ X#define SP 32 /* ASCII space */ X#define DEL 127 /* Delete (rubout) */ X#define ESCCHR '^' /* Default escape character for CONNECT */ X X#define MAXTRY 10 /* Times to retry a packet */ X#define MYQUOTE '#' /* Quote character I will use */ X#define MYPAD 0 /* Number of padding characters I will need */ X#define MYPCHAR 0 /* Padding character I need (NULL) */ X X#if IBM_UTS X#define MYEOL '\r' /* End-Of-Line character for UTS systems */ X#else X#define MYEOL '\n' /* End-Of-Line character I need */ X#endif X X#define MYTIME 10 /* Seconds after which I should be timed out */ X#define MAXTIM 60 /* Maximum timeout interval */ X#define MINTIM 2 /* Minumum timeout interval */ X X#define TRUE -1 /* Boolean constants */ X#define FALSE 0 X X X/* Macro Definitions */ X X/* X * tochar: converts a control character to a printable one by adding a space. X * X * unchar: undoes tochar. X * X * ctl: converts between control characters and printable characters by X * toggling the control bit (ie. ^A becomes A and A becomes ^A). X */ X#define tochar(ch) ((ch) + ' ') X#define unchar(ch) ((ch) - ' ') X#define ctl(ch) ((ch) ^ 64 ) X X X/* Global Variables */ X X#ifndef unix Xstatic X#endif Xint size, /* Size of present data */ X rpsiz, /* Maximum receive packet size */ X spsiz, /* Maximum send packet size */ X pad, /* How much padding to send */ X timint, /* Timeout for foreign host on sends */ X n, /* Packet number */ X numtry, /* Times this packet retried */ X oldtry, /* Times previous packet retried */ X ttyfd, /* File descriptor of tty for I/O, 0 if remote */ X remote, /* -1 means we're a remote kermit */ X image, /* -1 means 8-bit mode */ X debug, /* indicates level of debugging output (0=none) */ X filnamcnv, /* -1 means do file name case conversions */ X filecount; /* Number of files left to send */ X X#ifndef unix Xstatic X#endif Xchar state, /* Present state of the automaton */ X padchar, /* Padding character to send */ X eol, /* End-Of-Line character to send */ X escchr, /* Connect command escape character */ X quote, /* Quote character in incoming data */ X **filelist, /* List of files to be sent */ X *filnam, /* Current file name */ X recpkt[MAXPACKSIZ], /* Receive packet buffer */ X packet[MAXPACKSIZ]; /* Packet buffer */ X X#ifndef unix Xstatic X#endif XFILE *fp, /* File pointer for current disk file */ X *log; /* File pointer for Logfile */ X X#ifdef unix Xjmp_buf env; /* Environment ptr for timeout longjump */ X#else X X#define exit(a) longjmp(xjmp,a) Xstatic jmp_buf env, xjmp; Xextern unsigned int cntdn; X#endif X X X/* X * m a i n X * X * Main routine - parse command and options, set up the X * tty lines, and dispatch to the appropriate routine. X */ X X#ifdef unix Xmain(argc,argv) X#else Xkermit(argc,argv) X#endif Xint argc; /* Character pointers to and count of */ Xchar **argv; /* command line arguments */ X{ X char *ttyname, /* tty name for LINE argument */ X *cp; /* char pointer */ X int speed, /* speed of assigned tty, */ X cflg, rflg, sflg; /* flags for CONNECT, RECEIVE, SEND */ X X#ifdef unix X struct sgttyb X rawmode, /* Controlling tty raw mode */ X cookedmode, /* Controlling tty cooked mode */ X ttymode; /* mode of tty line in LINE option */ X#else X int o_n, o_fmode, boom(), xboom(); X X counter(1); X o_fmode = _fmode; X _fmode = O_BINARY; X debug = 0; X if (o_n = setjmp(xjmp)) { X counter(0); X if (fp != NULL) { X fclose(fp); X fp = NULL; X } X _fmode = o_fmode; X ctrlbrk(boom); X return(o_n); X } X else X ctrlbrk(xboom); X#endif X X if (argc < 2) usage(); /* Make sure there's a command line */ X X cp = *++argv; argv++; argc -= 2; /* Set up pointers to args */ X X/* Initialize these values and hope the first packet will get across OK */ X X eol = CR; /* EOL for outgoing packets */ X quote = '#'; /* Standard control-quote char "#" */ X pad = 0; /* No padding */ X padchar = NULL; /* Use null if any padding wanted */ X X speed = cflg = sflg = rflg = 0; /* Turn off all parse flags */ X ttyname = 0; /* Default is remote mode */ X X#if UCB4X /* Default to 7-bit masking, CRLF */ X image = FALSE; /* translation and filename case */ X filnamcnv = TRUE; /* conversion for UNIX systems */ X#else X image = TRUE; /* Default to no processing for */ X filnamcnv = FALSE; /* non-UNIX systems */ X#endif X X escchr = ESCCHR; /* Default escape character */ X X while ((*cp) != NULL) /* Parse characters in first arg. */ X switch (*cp++) X { X case 'c': cflg++; break; /* C = Connect command */ X case 's': sflg++; break; /* S = Send command */ X case 'r': rflg++; break; /* R = Receive command */ X X case 'd': /* D = Increment debug mode count */ X debug++; break; X X case 'f': X filnamcnv = FALSE; /* F = don't do case conversion */ X break; /* on filenames */ X X case 'i': /* I = Image (8-bit) mode */ X#ifdef unix X image = TRUE; break; /* (this is default for non-UNIX) */ X#else X image = FALSE; break; X#endif X X#ifdef unix X case 'l': /* L = specify tty line to use */ X if (argc--) ttyname = *argv++; X else usage(); X if (debug) printf("Line to remote host is %s\n",ttyname); X break; X X case 'e': /* E = specify escape char */ X if (argc--) escchr = **argv++; X else usage(); X if (debug) printf("Escape char is \"%c\"\n",escchr); X break; X X case 'b': /* B = specify baud rate */ X#if UCB4X X if (argc--) speed = atoi(*argv++); X else usage(); X if (debug) printf("Line speed to remote host is %d\n",speed); X break; X#else X printmsg("Speed setting implemented for Unix only."); X exit(1); X#endif X#endif X } X X/* Done parsing */ X X if ((cflg+sflg+rflg) != 1) /* Only one command allowed */ X usage(); X X X#ifdef unix X if (ttyname) /* If LINE was specified, we */ X { /* operate in local mode */ X ttyfd = open(ttyname,2); /* Open the tty line */ X if (ttyfd < 0) X { X printmsg("Cannot open %s",ttyname); X exit(1); X } X remote = FALSE; /* Indicate we're in local mode */ X } X else /* No LINE specified so we operate */ X { /* in remote mode (ie. controlling */ X ttyfd = 0; /* tty is the communications line) */ X remote = TRUE; X } X X X/* Put the proper tty into the correct mode */ X X if (remote) /* If remote, use controlling tty */ X { X gtty(0,&cookedmode); /* Save current mode so we can */ X gtty(0,&rawmode); /* restore it later */ X rawmode.sg_flags |= (RAW|TANDEM); X rawmode.sg_flags &= ~(ECHO|CRMOD); X stty(0,&rawmode); /* Put tty in raw mode */ X } X else /* Local, use assigned line */ X { X gtty(ttyfd,&ttymode); X ttymode.sg_flags |= (RAW|TANDEM); X ttymode.sg_flags &= ~(ECHO|CRMOD); X X#if UCB4X /* Speed changing for UNIX only */ X if (speed) /* User specified a speed? */ X { X switch(speed) /* Get internal system code */ X { X case 110: speed = B110; break; X case 150: speed = B150; break; X case 300: speed = B300; break; X case 1200: speed = B1200; break; X case 2400: speed = B2400; break; X case 4800: speed = B4800; break; X case 9600: speed = B9600; break; X X default: X printmsg("Bad line speed."); X exit(1); X } X ttymode.sg_ispeed = speed; X ttymode.sg_ospeed = speed; X } X#endif /* UCB4X */ X X stty(ttyfd,&ttymode); /* Put asg'd tty in raw mode */ X } X#else X remote = FALSE; X#endif X X X/* All set up, now execute the command that was given. */ X X if (debug) X { X printf("Debugging level = %d\n\n",debug); X X if (cflg) printf("Connect command\n\n"); X if (sflg) printf("Send command\n\n"); X if (rflg) printf("Receive command\n\n"); X } X X if (cflg) connect(); /* Connect command */ X X if (sflg) /* Send command */ X { X if (argc--) filnam = *argv++; /* Get file to send */ X else X#ifdef unix X { if (remote) X stty(0,&cookedmode); /* Restore controlling tty's modes */ X usage(); /* and give error */ X } X#else X usage(); X#endif X fp = NULL; /* Indicate no file open yet */ X filelist = argv; /* Set up the rest of the file list */ X filecount = argc; /* Number of files left to send */ X if (sendsw() == FALSE) /* Send the file(s) */ X printmsg("Send failed."); /* Report failure */ X else /* or */ X printmsg("done."); /* success */ X } X X if (rflg) /* Receive command */ X { X if (recsw() == FALSE) /* Receive the file(s) */ X printmsg("Receive failed."); X else /* Report failure */ X printmsg("done."); /* or success */ X } X X#ifdef unix X if (remote) stty(0,&cookedmode); /* Restore controlling tty's modes */ X#endif X} X X X/* X * s e n d s w X * X * Sendsw is the state table switcher for sending files. It loops until X * either it finishes, or an error is encountered. The routines called X * by sendsw are responsible for changing the state. X * X */ X X#ifndef unix Xstatic X#endif Xsendsw() X{ X char sinit(), sfile(), sdata(), seof(), sbreak(); X X state = 'S'; /* Send initiate is the start state */ X n = 0; /* Initialize message number */ X numtry = 0; /* Say no tries yet */ X while(TRUE) /* Do this as long as necessary */ X { X if (debug) printf("sendsw state: %c\n",state); X switch(state) X { X case 'S': state = sinit(); break; /* Send-Init */ X case 'F': state = sfile(); break; /* Send-File */ X case 'D': state = sdata(); break; /* Send-Data */ X case 'Z': state = seof(); break; /* Send-End-of-File */ X case 'B': state = sbreak(); break; /* Send-Break */ X case 'C': return (TRUE); /* Complete */ X case 'A': return (FALSE); /* "Abort" */ X default: return (FALSE); /* Unknown, fail */ X } X } X} X X X/* X * s i n i t X * X * Send Initiate: send this host's parameters and get other side's back. X */ X X#ifndef unix Xstatic X#endif Xchar sinit() X{ X int num, len; /* Packet number, length */ X X if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ X spar(packet); /* Fill up init info packet */ X X flushinput(); /* Flush pending input */ X X spack('S',n,6,packet); /* Send an S packet */ X switch(rpack(&len,&num,recpkt)) /* What was the reply? */ X { X case 'N': return(state); /* NAK, try it again */ X X case 'Y': /* ACK */ X if (n != num) /* If wrong ACK, stay in S state */ X return(state); /* and try again */ X rpar(recpkt); /* Get other side's init info */ X X if (eol == 0) eol = '\n'; /* Check and set defaults */ X if (quote == 0) quote = '#'; X X numtry = 0; /* Reset try counter */ X n = (n+1)%64; /* Bump packet count */ X return('F'); /* OK, switch state to F */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: return(state); /* Receive failure, try again */ X X default: return('A'); /* Anything else, just "abort" */ X } X } X X X/* X * s f i l e X * X * Send File Header. X */ X X#ifndef unix Xstatic X#endif Xchar sfile() X{ X int num, len; /* Packet number, length */ X char filnam1[50], /* Converted file name */ X *newfilnam, /* Pointer to file name to send */ X *cp; /* char pointer */ X X if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ X X if (fp == NULL) /* If not already open, */ X { if (debug) printf(" Opening %s for sending.\n",filnam); X fp = fopen(filnam,"r"); /* open the file to be sent */ X if (fp == NULL) /* If bad file pointer, give up */ X { X error("Cannot open file %s",filnam); X return('A'); X } X } X X strcpy(filnam1, filnam); /* Copy file name */ X newfilnam = cp = filnam1; X while (*cp != '\0') /* Strip off all leading directory */ X if (*cp++ == '/') /* names (ie. up to the last /). */ X newfilnam = cp; X X if (filnamcnv) /* Convert lower case to upper */ X for (cp = newfilnam; *cp != '\0'; cp++) X if (*cp >= 'a' && *cp <= 'z') X *cp ^= 040; X X len = cp - newfilnam; /* Compute length of new filename */ X X printmsg("Sending %s as %s",filnam,newfilnam); X X spack('F',n,len,newfilnam); /* Send an F packet */ X switch(rpack(&len,&num,recpkt)) /* What was the reply? */ X { X case 'N': /* NAK, just stay in this state, */ X num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ X if (n != num) /* which is just like an ACK for */ X return(state); /* this packet so fall thru to... */ X X case 'Y': /* ACK */ X if (n != num) return(state); /* If wrong ACK, stay in F state */ X numtry = 0; /* Reset try counter */ X n = (n+1)%64; /* Bump packet count */ X size = bufill(packet); /* Get first data from file */ X return('D'); /* Switch state to D */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: return(state); /* Receive failure, stay in F state */ X X default: return('A'); /* Something else, just "abort" */ X } X} X X X/* X * s d a t a X * X * Send File Data X */ X X#ifndef unix Xstatic X#endif Xchar sdata() X{ X int num, len; /* Packet number, length */ X X if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ X X spack('D',n,size,packet); /* Send a D packet */ X switch(rpack(&len,&num,recpkt)) /* What was the reply? */ X { X case 'N': /* NAK, just stay in this state, */ X num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ X if (n != num) /* which is just like an ACK for */ X return(state); /* this packet so fall thru to... */ X X case 'Y': /* ACK */ X if (n != num) return(state); /* If wrong ACK, fail */ X numtry = 0; /* Reset try counter */ X n = (n+1)%64; /* Bump packet count */ X if ((size = bufill(packet)) == EOF) /* Get data from file */ X return('Z'); /* If EOF set state to that */ X return('D'); /* Got data, stay in state D */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: return(state); /* Receive failure, stay in D */ X X default: return('A'); /* Anything else, "abort" */ X } X} X X X/* X * s e o f X * X * Send End-Of-File. X */ X X#ifndef unix Xstatic X#endif Xchar seof() X{ X int num, len; /* Packet number, length */ X if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */ X X spack('Z',n,0,packet); /* Send a 'Z' packet */ X switch(rpack(&len,&num,recpkt)) /* What was the reply? */ X { X case 'N': /* NAK, just stay in this state, */ X num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */ X if (n != num) /* which is just like an ACK for */ X return(state); /* this packet so fall thru to... */ X X case 'Y': /* ACK */ X if (n != num) return(state); /* If wrong ACK, hold out */ X numtry = 0; /* Reset try counter */ X n = (n+1)%64; /* and bump packet count */ X if (debug) printf(" Closing input file %s, ",filnam); X fclose(fp); /* Close the input file */ X fp = NULL; /* Set flag indicating no file open */ X X if (debug) printf("looking for next file...\n"); X if (gnxtfl() == FALSE) /* No more files go? */ X return('B'); /* if not, break, EOT, all done */ X if (debug) printf(" New file is %s\n",filnam); X return('F'); /* More files, switch state to F */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: return(state); /* Receive failure, stay in Z */ X X default: return('A'); /* Something else, "abort" */ X } X} X X X/* X * s b r e a k X * X * Send Break (EOT) X */ X X#ifndef unix Xstatic X#endif Xchar sbreak() X{ X int num, len; /* Packet number, length */ X if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */ X X spack('B',n,0,packet); /* Send a B packet */ X switch (rpack(&len,&num,recpkt)) /* What was the reply? */ X { X case 'N': /* NAK, just stay in this state, */ X num = (--num<0 ? 63:num); /* unless NAK for previous packet, */ X if (n != num) /* which is just like an ACK for */ X return(state); /* this packet so fall thru to... */ X X case 'Y': /* ACK */ X if (n != num) return(state); /* If wrong ACK, fail */ X numtry = 0; /* Reset try counter */ X n = (n+1)%64; /* and bump packet count */ X return('C'); /* Switch state to Complete */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: return(state); /* Receive failure, stay in B */ X X default: return ('A'); /* Other, "abort" */ X } X} X X X/* X * r e c s w X * X * This is the state table switcher for receiving files. X */ X X#ifndef unix Xstatic X#endif Xrecsw() X{ X char rinit(), rfile(), rdata(); /* Use these procedures */ X X state = 'R'; /* Receive-Init is the start state */ X n = 0; /* Initialize message number */ X numtry = 0; /* Say no tries yet */ X X while(TRUE) X { X if (debug) printf(" recsw state: %c\n",state); X switch(state) /* Do until done */ X { X case 'R': state = rinit(); break; /* Receive-Init */ X case 'F': state = rfile(); break; /* Receive-File */ X case 'D': state = rdata(); break; /* Receive-Data */ X case 'C': return(TRUE); /* Complete state */ X case 'A': return(FALSE); /* "Abort" state */ X } X } X} X X X/* X * r i n i t X * X * Receive Initialization X */ X X#ifndef unix Xstatic X#endif Xchar rinit() X{ X int len, num; /* Packet length, number */ X X if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */ X X switch(rpack(&len,&num,packet)) /* Get a packet */ X { X case 'S': /* Send-Init */ X rpar(packet); /* Get the other side's init data */ X spar(packet); /* Fill up packet with my init info */ X spack('Y',n,6,packet); /* ACK with my parameters */ X oldtry = numtry; /* Save old try count */ X numtry = 0; /* Start a new counter */ X n = (n+1)%64; /* Bump packet number, mod 64 */ X return('F'); /* Enter File-Receive state */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: /* Didn't get packet */ X spack('N',n,0,0); /* Return a NAK */ X return(state); /* Keep trying */ X X default: return('A'); /* Some other packet type, "abort" */ X } X} X X X/* X * r f i l e X * X * Receive File Header X */ X X#ifndef unix Xstatic X#endif Xchar rfile() X{ X int num, len; /* Packet number, length */ X char filnam1[50]; /* Holds the converted file name */ X X if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */ X X switch(rpack(&len,&num,packet)) /* Get a packet */ X { X case 'S': /* Send-Init, maybe our ACK lost */ X if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */ X if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ X { /* Yes, ACK it again with */ X spar(packet); /* our Send-Init parameters */ X spack('Y',num,6,packet); X numtry = 0; /* Reset try counter */ X return(state); /* Stay in this state */ X } X else return('A'); /* Not previous packet, "abort" */ X X case 'Z': /* End-Of-File */ X if (oldtry++ > MAXTRY) return('A'); X if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ X { /* Yes, ACK it again. */ X spack('Y',num,0,0); X numtry = 0; X return(state); /* Stay in this state */ X } X else return('A'); /* Not previous packet, "abort" */ X X case 'F': /* File Header (just what we want) */ X if (num != n) return('A'); /* The packet number must be right */ X strcpy(filnam1, packet); /* Copy the file name */ X X if (filnamcnv) /* Convert upper case to lower */ X for (filnam=filnam1; *filnam != '\0'; filnam++) X if (*filnam >= 'A' && *filnam <= 'Z') X *filnam |= 040; X X if ((fp=fopen(filnam1,"w"))==NULL) /* Try to open a new file */ X { X error("Cannot create %s",filnam1); /* Give up if can't */ X return('A'); X } X else /* OK, give message */ X printmsg("Receiving %s as %s",packet,filnam1); X X spack('Y',n,0,0); /* Acknowledge the file header */ X oldtry = numtry; /* Reset try counters */ X numtry = 0; /* ... */ X n = (n+1)%64; /* Bump packet number, mod 64 */ X return('D'); /* Switch to Data state */ X X case 'B': /* Break transmission (EOT) */ X if (num != n) return ('A'); /* Need right packet number here */ X spack('Y',n,0,0); /* Say OK */ X return('C'); /* Go to complete state */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: /* Didn't get packet */ X spack('N',n,0,0); /* Return a NAK */ X return(state); /* Keep trying */ X X default: return ('A'); /* Some other packet, "abort" */ X } X} X X X/* X * r d a t a X * X * Receive Data X */ X X#ifndef unix Xstatic X#endif Xchar rdata() X{ X int num, len; /* Packet number, length */ X if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */ X X switch(rpack(&len,&num,packet)) /* Get packet */ X { X case 'D': /* Got Data packet */ X if (num != n) /* Right packet? */ X { /* No */ X if (oldtry++ > MAXTRY) X return('A'); /* If too many tries, abort */ X if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ X { /* Previous packet again? */ X spack('Y',num,6,packet); /* Yes, re-ACK it */ X numtry = 0; /* Reset try counter */ X return(state); /* Don't write out data! */ X } X else return('A'); /* sorry, wrong number */ X } X /* Got data with right packet number */ X bufemp(packet,len); /* Write the data to the file */ X spack('Y',n,0,0); /* Acknowledge the packet */ X oldtry = numtry; /* Reset the try counters */ X numtry = 0; /* ... */ X n = (n+1)%64; /* Bump packet number, mod 64 */ X return('D'); /* Remain in data state */ X X case 'F': /* Got a File Header */ X if (oldtry++ > MAXTRY) X return('A'); /* If too many tries, "abort" */ X if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ X { /* It was the previous one */ X spack('Y',num,0,0); /* ACK it again */ X numtry = 0; /* Reset try counter */ X return(state); /* Stay in Data state */ X } X else return('A'); /* Not previous packet, "abort" */ X X case 'Z': /* End-Of-File */ X if (num != n) return('A'); /* Must have right packet number */ X spack('Y',n,0,0); /* OK, ACK it. */ X fclose(fp); /* Close the file */ X#ifndef unix X fp = NULL; X#endif X n = (n+1)%64; /* Bump packet number */ X return('F'); /* Go back to Receive File state */ X X case 'E': /* Error packet received */ X prerrpkt(recpkt); /* Print it out and */ X return('A'); /* abort */ X X case FALSE: /* Didn't get packet */ X spack('N',n,0,0); /* Return a NAK */ X return(state); /* Keep trying */ X X default: return('A'); /* Some other packet, "abort" */ X } X} X X/* X * c o n n e c t X * X * Establish a virtual terminal connection with the remote host, over an X * assigned tty line. X */ X X#ifndef unix Xstatic X#endif Xconnect() X{ X#ifdef unix X int pid, /* Holds process id of child */ X connected; /* Boolean connect flag */ X char bel = '\07', X c; X X struct sgttyb X rawmode, /* Controlling tty raw mode */ X cookedmode; /* Controlling tty cooked mode */ X X if (remote) /* Nothing to connect to in remote */ X { /* mode, so just return */ X printmsg("No line specified for connection."); X return; X } X X gtty(0,&cookedmode); /* Save current mode so we can */ X gtty(0,&rawmode); /* restore it later */ X rawmode.sg_flags |= (RAW|TANDEM); X rawmode.sg_flags &= ~(ECHO|CRMOD); X stty(0,&rawmode); /* Put tty in raw mode */ X X pid = fork(); /* Start fork to get typeout from remote host */ X X if (pid) /* Parent: send type-in to remote host */ X { X printmsg("connected...\r"); X connected = TRUE; /* Put us in "connect mode" */ X while (connected) X { X read(0,&c,1); /* Get a character */ X if ((c&0177) == escchr) /* Check for escape character */ X { X read(0,&c,1); X if ((c&0177) == escchr) X write(ttyfd,&c,1); X else X switch (c&0177) X { X case 'c': X case 'C': X connected = FALSE; X write(0,"\r\n",2); X break; X X case 'h': X case 'H': X write(0,"\r\nYes, I'm still here...\r\n",26); X break; X X default: X write(0,&bel,1); X break; X } X } X else X { /* If not escape charater, */ X write(ttyfd,&c,1); /* write it out */ X c = NULL; /* Nullify it (why?) */ X } X } X kill(pid,9); /* Done, kill the child */ X wait(0); /* and bury him */ X stty(0,&cookedmode); /* Restore tty mode */ X printmsg("disconnected."); X return; /* Done */ X } X else /* Child does the reading from the remote host */ X { X while(1) /* Do this forever */ X { X read(ttyfd,&c,1); X write(1,&c,1); X } X } X#endif X} X X/* X * KERMIT utilities. X */ X X#ifndef unix Xstatic X#endif Xclkint() /* Timer interrupt handler */ X{ X longjmp(env,TRUE); /* Tell rpack to give up */ X} X X X/* X * s p a c k X * X * Send a Packet X */ X X#ifndef unix Xstatic X#endif Xspack(type,num,len,data) Xchar type, *data; Xint num, len; X{ X int i; /* Character loop counter */ X char chksum, buffer[100]; /* Checksum, packet buffer */ X register char *bufp; /* Buffer pointer */ X X if (debug>1) /* Display outgoing packet */ X { X if (data != NULL) X data[len] = '\0'; /* Null-terminate data to print it */ X printf(" spack type: %c\n",type); X printf(" num: %d\n",num); X printf(" len: %d\n",len); X if (data != NULL) X printf(" data: \"%s\"\n",data); X } X X bufp = buffer; /* Set up buffer pointer */ X for (i=1; i<=pad; i++) X#ifdef unix X write(ttyfd,&padchar,1); /* Issue any padding */ X#else X xwrite(&padchar,1); X#endif X X *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */ X *bufp++ = tochar(len+3); /* Send the character count */ X chksum = tochar(len+3); /* Initialize the checksum */ X *bufp++ = tochar(num); /* Packet number */ X chksum += tochar(num); /* Update checksum */ X *bufp++ = type; /* Packet type */ X chksum += type; /* Update checksum */ X X for (i=0; i> 6)+chksum)&077; /* Compute final checksum */ X *bufp++ = tochar(chksum); /* Put it in the packet */ X *bufp = eol; /* Extra-packet line terminator */ X#ifdef unix X write(ttyfd, buffer,bufp-buffer+1); /* Send the packet */ X#else X xwrite( buffer,bufp-buffer+1); X#endif X} X X/* X * r p a c k X * X * Read a Packet X */ X X#ifndef unix Xstatic X#endif Xrpack(len,num,data) Xint *len, *num; /* Packet length, number */ Xchar *data; /* Packet data */ X{ X int i, done; /* Data character number, loop exit */ X char t, /* Current input character */ X type, /* Packet type */ X cchksum, /* Our (computed) checksum */ X rchksum; /* Checksum received from other host */ X X#ifdef unix X#if UCB4X /* TOPS-20 can't handle timeouts... */ X if (setjmp(env)) return FALSE; /* Timed out, fail */ X signal(SIGALRM,clkint); /* Setup the timeout */ X if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME; X alarm(timint); X#endif /* UCB4X */ X#else X X if (setjmp(env)) return FALSE; X if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME; X cntdn = timint * 10; X X#define read(a,b,c) xread(b,c) X X#endif X X while (t != SOH) /* Wait for packet header */ X { X read(ttyfd,&t,1); X t &= 0177; /* Handle parity */ X } X X done = FALSE; /* Got SOH, init loop */ X while (!done) /* Loop to get a packet */ X { X read(ttyfd,&t,1); /* Get character */ X if (!image) t &= 0177; /* Handle parity */ X if (t == SOH) continue; /* Resynchronize if SOH */ X cchksum = t; /* Start the checksum */ X *len = unchar(t)-3; /* Character count */ X X read(ttyfd,&t,1); /* Get character */ X if (!image) t &= 0177; /* Handle parity */ X if (t == SOH) continue; /* Resynchronize if SOH */ X cchksum = cchksum + t; /* Update checksum */ X *num = unchar(t); /* Packet number */ X X read(ttyfd,&t,1); /* Get character */ X if (!image) t &= 0177; /* Handle parity */ X if (t == SOH) continue; /* Resynchronize if SOH */ X cchksum = cchksum + t; /* Update checksum */ X type = t; /* Packet type */ X X for (i=0; i<*len; i++) /* The data itself, if any */ X { /* Loop for character count */ X read(ttyfd,&t,1); /* Get character */ X if (!image) t &= 0177; /* Handle parity */ X if (t == SOH) continue; /* Resynch if SOH */ X cchksum = cchksum + t; /* Update checksum */ X data[i] = t; /* Put it in the data buffer */ X } X data[*len] = 0; /* Mark the end of the data */ X X read(ttyfd,&t,1); /* Get last character (checksum) */ X rchksum = unchar(t); /* Convert to numeric */ X read(ttyfd,&t,1); /* get EOL character and toss it */ X if (!image) t &= 0177; /* Handle parity */ X if (t == SOH) continue; /* Resynchronize if SOH */ X done = TRUE; /* Got checksum, done */ X } X X#ifdef unix X#if UCB4X X alarm(0); /* Disable the timer interrupt */ X#endif X#else X cntdn = 0; X#undef read X#endif X X if (debug>1) /* Display incoming packet */ X { X if (data != NULL) X data[*len] = '\0'; /* Null-terminate data to print it */ X printf(" rpack type: %c\n",type); X printf(" num: %d\n",*num); X printf(" len: %d\n",*len); X if (data != NULL) X printf(" data: \"%s\"\n",data); X } X /* Fold in bits 7,8 to compute */ X cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */ X X if (cchksum != rchksum) return(FALSE); X X return(type); /* All OK, return packet type */ X} X X X/* X * b u f i l l X * X * Get a bufferful of data from the file that's being sent. X * Only control-quoting is done; 8-bit & repeat count prefixes are X * not handled. X */ X X#ifndef unix Xstatic X#endif Xbufill(buffer) Xchar buffer[]; /* Buffer */ X{ X int i, /* Loop index */ X t; /* Char read from file */ X char t7; /* 7-bit version of above */ X X i = 0; /* Init data buffer pointer */ X while((t = getc(fp)) != EOF) /* Get the next character */ X { X t7 = t & 0177; /* Get low order 7 bits */ X X if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */ X { /* special handling? */ X#ifdef unix X if (t=='\n' && !image) X { /* Do LF->CRLF mapping if !image */ X buffer[i++] = quote; X buffer[i++] = ctl('\r'); X } X#endif X buffer[i++] = quote; /* Quote the character */ X if (t7 != quote) X { X t = ctl(t); /* and uncontrolify */ X t7 = ctl(t7); X } X } X if (image) X buffer[i++] = t; /* Deposit the character itself */ X else X buffer[i++] = t7; X X if (i >= spsiz-8) return(i); /* Check length */ X } X if (i==0) return(EOF); /* Wind up here only on EOF */ X return(i); /* Handle partial buffer */ X} X X X/* X * b u f e m p X * X * Put data from an incoming packet into a file. X */ X X#ifndef unix Xstatic X#endif Xbufemp(buffer,len) Xchar buffer[]; /* Buffer */ Xint len; /* Length */ X{ X int i; /* Counter */ X char t; /* Character holder */ X X for (i=0; i 'xmodem.c' X/* X * XMODEM Version 1.0 - by Brian Kantor, UCSD X * X * XMODEM -- Implements the "CP/M User's Group XMODEM" protocol, X * for packetized file up/downloading. X * X * This version is designed for 4.2BSD ONLY! It won't work X * ANYWHERE else - uses the 'select' system call to replace X * the old alarm handlers. X * X * -- Based on UMODEM 3.5 by Lauren Weinstein, Richard Conn, and others. X * X */ X X#include X#include X#include X#include X X#ifdef unix X#include X#include X#include X#else X#include X#include X#endif X X/* log default define */ X#ifndef LOGDEFAULT X#define LOGDEFAULT 1 X#endif X X/* Delete logfile define. Useful on small systems with limited X * filesystem space and careless users. X */ X#ifndef DELDEFAULT X#define DELDEFAULT 1 X#endif X X#define VERSION 10 /* 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#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 X/* Mode for Created Files */ X#define CREATMODE 0644 /* mode for created files */ X X#ifdef unix Xstruct sgttyb ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ X Xstruct stat statbuf; /* for terminal message on/off control */ Xchar *strcat(); XFILE *LOGFP, *fopen(); Xchar buff[BBUFSIZ]; Xint nbchr; /* number of chars read so far for buffered read */ X Xint wason; X Xint pagelen; Xchar *ttyname(); /* forward declaration for C */ Xchar *tty; Xchar XMITTYPE; Xint CRCMODE, RECVFLAG, SENDFLAG, PMSG, DELFLAG, LOGFLAG, MUNGMODE; Xint FILTER, DEBUG; Xint STATDISP; Xchar filename[256]; X#else Xstatic struct stat statbuf; Xstatic FILE *LOGFP; Xstatic char buff[BBUFSIZ]; Xstatic int nbchr; X Xstatic int wason; X Xstatic int pagelen; Xstatic char *tty; Xstatic char XMITTYPE; Xstatic int CRCMODE, RECVFLAG, SENDFLAG, PMSG, DELFLAG, LOGFLAG; Xstatic int FILTER, DEBUG, MUNGMODE; Xstatic int STATDISP; Xstatic char filename[256]; X X#define exit(a) longjmp(xjmp, a) Xstatic jmp_buf xjmp; X X#endif X X#ifdef unix Xmain(argc, argv) X#else Xxmodem(argc, argv) X#endif Xint argc; Xchar **argv; X{ X char *getenv(); X char *fname = filename; X char *logfile; X int index; X char flag; X#ifndef unix X int boom(), xboom(); X X counter(1); /* setup timeout counter */ X if (index = setjmp(xjmp)) { X counter(0); /* decommission timeout counter */ X ctrlbrk(boom); X return(index); X } X else X ctrlbrk(xboom); X#endif X X logfile = "xmodem.log"; /* Name of LOG File */ X X printf("\nXMODEM Version %d.%d", VERSION/10, VERSION%10); X printf(" -- UNIX-CP/M Remote File Transfer Facility\n"); X X if (argc < 3) X { X help(FALSE); X exit(-1); X } X X index = 0; /* set index for loop */ X PMSG = FALSE; /* turn off flags */ X DEBUG = FALSE; X RECVFLAG = FALSE; /* not receive */ X SENDFLAG = FALSE; /* not send either */ X FILTER = FALSE; /* assume literal mode */ X CRCMODE = FALSE; /* use checksums for now */ X XMITTYPE = 't'; /* assume text */ X X DELFLAG = DELDEFAULT; X LOGFLAG = LOGDEFAULT; X if (LOGFLAG) X LOGFLAG = TRUE; X else X LOGFLAG = FALSE; X X MUNGMODE = FALSE; /* protect files from overwriting */ X X while ((flag = argv[1][index++]) != '\0') X switch (flag) { X case '-' : break; X case 'x' : DEBUG = TRUE; X break; X/* no crc mode yet X case 'c' : CRCMODE = TRUE; X xmdebug("CRC mode selected"); X break; X*/ X case 'd' : DELFLAG = !DELDEFAULT; /* delete log file ? */ X xmdebug("delete log toggled"); X break; X case 'l' : LOGFLAG = !LOGDEFAULT; /* turn off log ? */ X xmdebug("write log toggled"); X break; X case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */ X xmdebug("munge mode selected"); X break; X case 'r' : RECVFLAG = TRUE; /* receive file */ X XMITTYPE = gettype(argv[1][index++]); /* get t/b */ X xmdebug("receive mode selected"); X break; X case 's' : SENDFLAG = TRUE; /* send file */ X XMITTYPE = gettype(argv[1][index++]); X xmdebug("send mode selected"); X break; X case 'f' : FILTER = TRUE; X xmdebug("filter selected"); X break; X default : error("Invalid Flag", FALSE); X } X X if (LOGFLAG) X { X#ifdef unix X if ((fname = getenv("HOME")) == 0) /* Get HOME variable */ X error("Can't get Environment!", FALSE); X fname = strcat(fname, "/"); X fname = strcat(fname, logfile); X#else X fname = logfile; X#endif 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("Can't Open Log File", FALSE); X fprintf(LOGFP,"\n\n++++++++\n"); X fprintf(LOGFP,"\nXMODEM Version %d.%d\n", VERSION/10, VERSION%10); X printf("\nXMODEM: LOG File '%s' is Open\n", fname); X } X X X if (RECVFLAG && SENDFLAG) X error("Both Send and Receive Functions Specified", FALSE); X X if (!RECVFLAG && !SENDFLAG) X error("Either Send or Receive Function must be chosen!",FALSE); X X if (FILTER && (!RECVFLAG || XMITTYPE != 't')) X error("Filter is only valid in text receive mode!",FALSE); X X if (RECVFLAG) X { X if(open(argv[2], 0) != -1) /* possible abort if file exists */ X { X printf("\nXMODEM: Warning -- Target File Exists\n"); X if( MUNGMODE == FALSE ) X error("Fatal - Can't overwrite file\n",FALSE); X printf("XMODEM: Overwriting Target File\n"); X } X rfile(argv[2]); /* receive file */ X } X X if (SENDFLAG) X sfile(argv[2]); /* send file */ X X xmdebug("done"); X if (LOGFLAG) fclose(LOGFP); X exit(0); X} X X/* Print Help Message */ X#ifndef unix Xstatic X#endif Xhelp() X { X xmdebug("help:"); X#ifdef unix X printf("\nUsage: \n\txmodem "); X printf("-[rb!rt!sb!st][options] filename\n"); X printf("\nMajor Commands --"); X printf("\n\trb <-- Receive Binary"); X printf("\n\trt <-- Receive Text"); X printf("\n\tsb <-- Send Binary"); X printf("\n\tst <-- Send Text"); X printf("\nOptions --"); X#if DELDEFAULT == 1 X printf("\n\td <-- Do not delete umodem.log file before starting"); X#else X printf("\n\td <-- Delete umodem.log file before starting"); X#endif X X#if LOGDEFAULT == 1 X printf("\n\tl <-- (ell) Turn OFF LOG File Entries"); X#else X printf("\n\tl <-- (ell) Turn ON LOG File Entries"); X#endif X X/* no crc mode yet X printf("\n\tc <-- Select CRC mode on receive"); X*/ X printf("\n\tf <-- Filter 8-bit chars on receive - use with WordStar files"); X printf("\n"); X#else X cprintf("\r\n\ XUsage: \r\n\txmodem -[rb!rt!sb!st][options] filename\r\n\ X\r\nMajor Commands --\ X\r\n\trb <-- Receive Binary\ X\r\n\trt <-- Receive Text\ X\r\n\tsb <-- Send Binary\ X\r\n\tst <-- Send Text\ X\r\nOptions --\ X\r\n\td <-- %s\ X\r\n\tl <-- (ell) %s%s\ X\r\n\tf <-- Filter 8-bit chars on receive - use with WordStar files\n", X#if DELDEFAULT == 1 X "Do not delete xmodem.log file before starting", X#else X "Delete xmodem.log file before starting", X#endif X#if LOGDEFAULT == 1 X "Turn OFF LOG File Entries", X#else X "Turn ON LOG File Entries", X#endif X/* no crc mode yet X "\r\n\tc <-- Select CRC mode on receive"); X*/ X ""); X X#endif X } X X/* get type of transmission requested (text or binary) */ X#ifndef unix Xstatic X#endif Xgettype(ichar) Xchar ichar; X { X xmdebug("gettype:"); X if (ichar == 't') return(ichar); X if (ichar == 'b') return(ichar); X error("Invalid Send/Receive Parameter - not t or b", FALSE); X return; X } X X/* set tty modes for XMODEM transfers */ X#ifndef unix Xstatic X#endif Xsetmodes() X { X xmdebug("setmodes:"); X#ifdef unix X if (ioctl(0,TIOCGETP,&ttys)<0) /* get tty params [V7] */ X error("Can't get TTY Parameters", TRUE); X X tty = ttyname(0); /* identify current tty */ X X /* transfer current modes to new structure */ X ttysnew.sg_ispeed = ttys.sg_ispeed; /* copy input speed */ X ttysnew.sg_ospeed = ttys.sg_ospeed; /* copy output speed */ X ttysnew.sg_erase = ttys.sg_erase; /* copy erase flags */ X ttysnew.sg_flags = ttys.sg_flags; /* copy flags */ X ttysnew.sg_kill = ttys.sg_kill; /* copy std terminal flags */ X X ttysnew.sg_flags |= RAW; /* set for RAW Mode */ X /* This ORs in the RAW mode value, thereby X setting RAW mode and leaving the other X mode settings unchanged */ X X ttysnew.sg_flags &= ~ECHO; /* set for no echoing */ X /* This ANDs in the complement of the ECHO X setting (for NO echo), thereby leaving all X current parameters unchanged and turning X OFF ECHO only */ X X ttysnew.sg_flags &= ~XTABS; /* set for no tab expansion */ X ttysnew.sg_flags &= ~LCASE; /* set for no upper-to-lower case xlate */ X ttysnew.sg_flags |= ANYP; /* set for ANY Parity */ X ttysnew.sg_flags &= ~NL3; /* turn off ALL 3s - new line */ X ttysnew.sg_flags &= ~TAB2; /* turn off tab 3s */ X ttysnew.sg_flags &= ~CR3; /* turn off CR 3s */ X ttysnew.sg_flags &= ~FF1; /* turn off FF 3s */ X ttysnew.sg_flags &= ~BS1; /* turn off BS 3s */ X ttysnew.sg_flags &= ~TANDEM; /* turn off flow control */ X X /* set new paramters */ X if (ioctl(0,TIOCSETP,&ttysnew) < 0) X error("Can't set new TTY Parameters", TRUE); X X if (stat(tty, &statbuf) < 0) /* get tty status */ X error("Can't get your TTY Status", TRUE); X X if (statbuf.st_mode & 022) /* Need to turn messages off */ X if (chmod(tty, statbuf.st_mode & ~022) < 0) X error("Can't change TTY mode", TRUE); X else X wason = TRUE; X else X wason = FALSE; X#endif X xmdebug("tty modes set"); X } X X/* restore normal tty modes */ X#ifndef unix Xstatic X#endif Xrestoremodes(errcall) Xint errcall; X { X xmdebug("restoremodes:"); X#ifdef unix X if (wason) X if (chmod(tty, statbuf.st_mode | 022) < 0) X error("Can't change TTY mode", FALSE); X if (ioctl(0,TIOCSETP,&ttys) < 0) X { if (!errcall) X error("RESET - Can't restore normal TTY Params", FALSE); X else X { printf("XMODEM: "); X printf("RESET - Can't restore normal TTY Params\n"); X } X } X#endif X xmdebug("tty modes reset"); X return; X } X X/* print error message and exit; if mode == TRUE, restore normal tty modes */ X#ifndef unix Xstatic X#endif Xerror(msg, mode) Xchar *msg; Xint mode; X { X xmdebug("error:"); X if (mode) X restoremodes(TRUE); /* put back normal tty modes */ X printf("\r\nXMODEM: %s\n", msg); X#ifdef unix X if ((LOGFLAG || DEBUG) & (int)LOGFP) X#else X if (LOGFLAG || DEBUG) X#endif X { X fprintf(LOGFP, "XMODEM Fatal Error: %s\n", msg); X fclose(LOGFP); X } X exit(-1); X } X X/** print status (size) of a file **/ X#ifndef unix Xstatic X#endif Xyfile(name) Xchar *name; X { X xmdebug("yfile:"); X printf("\nXMODEM File Status Display for %s\n", name); X X#ifdef unix X if (open(name,0) < 0) X#else X if (open(name,O_RDONLY|O_BINARY) < 0) X#endif X { X printf("File %s does not exist\n", name); X return; X } X prfilestat(name); /* print status */ X printf("\n"); 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 */ X#ifndef unix Xstatic X#endif 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 xmdebug("getbyte:"); X if (nbchr == 0) /* Buffer exausted; read some more */ X { X if ((nbchr = read(fildes, buf, BUFSIZ)) < 0) X error("File Read Error", TRUE); 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 **/ X#ifndef unix Xstatic X#endif Xrfile(name) Xchar *name; X { X register int bufctr, checksum; X register int c; X char mode; X int fd, j, firstchar, sectnum, sectcurr, tmode; X int sectcomp, errors, errorflag, recfin; X int errorchar, fatalerror, startstx, inchecksum, endetx, endenq; X long recvsectcnt; X X xmdebug("rfile:"); X mode = XMITTYPE; /* set t/b mode */ X X#ifdef unix X if ((fd = creat(name, CREATMODE)) < 0) X#else X if ((fd = open(name, O_WRONLY|O_BINARY|O_CREAT, CREATMODE)) < 0) X#endif X error("Can't create file for receive", FALSE); X X printf("XMODEM: Ready to RECEIVE File %s\n", name); X puts("Control-X to cancel.\n"); X X if (LOGFLAG) X { X fprintf(LOGFP, "\n----\nXMODEM Receive Function\n"); X fprintf(LOGFP, "File Name: %s\n", name); X } X X setmodes(); /* setup tty modes for xfer */ 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 if (CRCMODE) X { X xmdebug("crc mode request sent"); X sendbyte('C'); /* CRC request for first block */ X } X else X { X xmdebug("NAK sent"); X sendbyte(NAK); /* Start up the sender's first block */ X } 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 xmdebug("first char was timeout"); X if (LOGFLAG) X fprintf(LOGFP, "Timeout on Sector %d\n", sectnum); X errorflag = TRUE; X } X X if ((firstchar & 0x7f) == CAN) X { X xmdebug("CAN received"); X if (LOGFLAG) X fprintf(LOGFP, "Reception canceled at user's request.\n"); X error("Reception canceled at user's request",TRUE); X } X X if (firstchar == SOH) X { X xmdebug("SOH received"); X sectcurr = readbyte(3); 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 (FILTER) /* bit 8 */ X buff[bufctr] &= 0x7f; 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 xmdebug("checksum ok"); X errors = 0; X recvsectcnt++; X sectnum = sectcurr; X if (write(fd, buff, bufctr) < 0) X error("File Write Error", TRUE); X else X sendbyte(ACK); X } X else X { X xmdebug("checksum bad"); X if (LOGFLAG) X fprintf(LOGFP, "Checksum Error on Sector %d\n", X sectnum); X errorflag = TRUE; X } X } X else X { X if (sectcurr == sectnum) X { X xmdebug("dup sector flushed"); X while(readbyte(3) != TIMEOUT) X ; X sendbyte(ACK); X } X else X { X xmdebug("sector out of seq"); X if (LOGFLAG) X { X fprintf(LOGFP, "Phase Error - Received Sector is "); X fprintf(LOGFP, "%d while Expected Sector is %d\n", X sectcurr, ((sectnum+1) & 0xff)); X } X errorflag = TRUE; X fatalerror = TRUE; X sendbyte(CAN); X } X } X } X else X { X if (DEBUG) X fprintf(LOGFP,"DEBUG: bad sector# sectcurr=%02xH, sectcomp=%02xH\n",sectcurr,sectcomp); X if (LOGFLAG) X fprintf(LOGFP, "Header Sector Number Error on Sector %d\n", X sectnum); X errorflag = TRUE; X } X } X X if (errorflag) X { X xmdebug("flushing bad sector"); 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)) X { X xmdebug("EOT received"); X close(fd); X sendbyte(ACK); X restoremodes(FALSE); /* restore normal tty modes */ X#ifdef unix X sleep(5); /* give other side time to return to terminal mode */ X#endif X if (LOGFLAG) X { X fprintf(LOGFP, "\nReceive Complete\n"); X fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt); X } X printf("\n"); X } X else X { X sendbyte(CAN); X xmdebug("error limit exceeded"); X error("\r\nABORTED -- Too Many Errors", TRUE); X } X } X X/** send a file **/ X#ifndef unix Xstatic X#endif Xsfile(name) Xchar *name; X { X register int bufctr, checksum, sectnum; X char blockbuf[134]; X char mode; X int fd, attempts; X int nlflag, sendfin, tmode; X int bbufcnt; X int firstchar; X char c; X int sendresp; /* response char to sent block */ X X xmdebug("sfile:"); X nbchr = 0; /* clear buffered read char count */ X mode = XMITTYPE; /* set t/b mode */ X X#ifdef unix X if ((fd = open(name, 0)) < 0) X#else X if ((fd = open(name, O_RDONLY|O_BINARY)) < 0) X#endif X { X if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n"); X error("Can't open file for send", FALSE); X } X X X printf("XMODEM: File %s Ready to SEND\n", name); X prfilestat(name); /* print file size statistics */ X puts("\nControl-X to cancel.\n"); X X if (LOGFLAG) X { X fprintf(LOGFP, "\n----\nXMODEM Send Function\n"); X fprintf(LOGFP, "File Name: %s\n", name); X } X X if (mode == 't') X tmode = TRUE; X else X tmode = FALSE; X X sendfin = nlflag = FALSE; X attempts = 0; X X setmodes(); /* setup tty modes for xfer */ X X while (((firstchar=readbyte(30)) != NAK) X/* no crc mode yet X && (firstchar != 'C') X*/ X && (firstchar != CAN)) X { X if (++attempts > RETRYMAX) X error("Remote System Not Responding", TRUE); X } X X if ((firstchar & 0x7f) == CAN) X { X#ifndef unix Xcanned: X#endif X xmdebug("can received"); X error("\nSend cancelled at user's request.\n",TRUE); X exit(-1); 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 if (getbyte(fd, &c) == 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#ifdef unix X bufctr++; X#else X buff[bufctr++] = '\0'; X#endif X continue; X } X X#ifdef unix 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#else X buff[bufctr++] = c; X#endif 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#ifdef unix X write(1, blockbuf, 132); /* write the block */ X#else X if (DEBUG) X cprintf(" %d", sectnum); X xwrite(blockbuf, 132); X#endif X X attempts++; X sendresp = readbyte(10); /* get response */ X if ((sendresp != ACK) && LOGFLAG) X { X fprintf(LOGFP, "Non-ACK (%2x) Received on Sector %d\n",sendresp,sectnum); X if (sendresp == TIMEOUT) X fprintf(LOGFP, "This Non-ACK was a TIMEOUT\n"); X if (sendresp == CAN) goto canned; X } X } X while((sendresp != ACK) && (attempts < RETRYMAX)); X X sectnum++; /* increment to next sector number */ X } X while (!sendfin && (attempts < RETRYMAX)); X X if (attempts >= RETRYMAX) X error("Remote System Not Responding", TRUE); 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", TRUE); X X close(fd); X restoremodes(FALSE); X#ifdef unix X sleep(15); /* give other side time to return to terminal mode */ X#endif X X if (LOGFLAG) X fprintf(LOGFP, "\nSend Complete\n"); X printf("\n"); X X } X X/* print file size status information */ X#ifndef unix Xstatic X#endif Xprfilestat(name) Xchar *name; X { X struct stat filestatbuf; /* file status info */ X X xmdebug("prfilestat:"); X stat(name, &filestatbuf); /* get file status bytes */ X printf(" Estimated File Size %ldK, %ld Records, %ld Bytes", X (filestatbuf.st_size/1024)+1, (filestatbuf.st_size/128)+1, X filestatbuf.st_size); X if (LOGFLAG) X fprintf(LOGFP,"Estimated File Size %ldK, %ld Records, %ld Bytes\n", 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 */ X#ifdef unix Xint readbyte(seconds) X#else Xstatic int Xreadbyte(seconds) X#endif Xint seconds; X { X#ifdef unix X char c; X int i, readfd; X struct timeval tmout; X X tmout.tv_sec = seconds; X tmout.tv_usec = 0; X X readfd = 1; X X if ((i=select(1, &readfd, 0, 0, &tmout)) == 0) X { X xmdebug("readbyte timeout"); X return(TIMEOUT); X } X if (DEBUG) X fprintf(LOGFP,"DEBUG: readbyte select returned %d\n",i); X X read(0, &c, 1); X#else X int c; X extern int recv_byt(); X extern unsigned int cntdn; X X cntdn = seconds * 10; /* cntdn == 100 ms decrements */ X X do { c = recv_byt(); } while (c < 0 && cntdn); X X cntdn = 0; X X if (c < 0) return(TIMEOUT); X#endif X X if (DEBUG) X fprintf(LOGFP,"DEBUG: readbyte %02xh\n",c); X X return(c & 0xff); /* return the char */ X } X X/* send a byte to data stream */ X#ifndef unix Xstatic X#endif Xsendbyte(data) Xchar data; X { X#ifdef unix X if (DEBUG) X fprintf(LOGFP,"DEBUG: sendbyte %02xh\n",data); X write(1, &data, 1); /* write the byte */ X ioctl(1,TIOCFLUSH,0); /* flush so it really happens now! */ X#else X extern int xmit_chr(); X X if (DEBUG) X fprintf(LOGFP,"DEBUG: sendbyte %02xh\n",data); X xmit_chr(data); X#endif X return; X } X X/* type out debugging info */ X#ifndef unix Xstatic X#endif Xxmdebug(str) Xchar *str; X { X if (DEBUG) X fprintf(LOGFP,"DEBUG: '%s'\n",str); X } X X#ifndef unix Xstatic Xxwrite(p, n) Xchar *p; Xint n; X{ X if (DEBUG) X fprintf(LOGFP,"DEBUG: xwrite %d bytes\n",n); X while(n--) X xmit_chr(*p++); X} X Xstatic Xxboom() X{ X error("user break"); X} X#endif SHAR_EOF fi # end of overwriting check # End of shell archive exit 0