/* pe7mai.c */ /* * K e r m i t File Transfer Utility * * UNIX Kermit, Columbia University, 1981, 1982, 1983 * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell * * Also: Jim Guyton, Rand Corporation * Walter Underwood, Ford Aerospace * * usage kermit -s [-f -d -i -u -t -p -l -b] file ... to send files * kermit -r [-d -i -u -t -p -l -b] to receive files * kermit -g [-f -d -i -u -t -p -l -b] file ... to get files * kermit -f [-d -t -p -l] to send finish command * kermit -x [-d -v -i -u -t -p -l] to set server mode * where s=send , r=receive , x=server , * f=finish mode , g=get files mode , * d=debug level , v=verbose mode , * i= image mode , u=no file name conversion * l=tty line , b=baud rate , * t=turnaround mode , p=parity type */ /* * Modification History: * * Version 1.1(0) * December 4, 1986 - Add btobemp to decode other prefixxed packets. * - Split program into parts to allow compiling on * machine with minimum memory configuration. * put in version number (use -v flag) * Chris Lent (ihnp4!philabs!phri!cooper!chris) * March 85 - Change to use the IDRIS operating system * Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS: * - Changed MYEOL character from \n to \r. * - Change char to int in bufill so getc would return -1 on * EOF instead of 255 (-1 truncated to 8 bits) * - Added read() in rpack to eat the EOL character * - Added fflush() call in printmsg to force the output * NOTE: The last three changes are not conditionally compiled * since they should work equally well on any system. * * Changed Berkeley 4.x conditional compilation flag from * UNIX4X to UCB4X. * Added support for error packets and cleaned up the printing * routines. */ #define KVERSION "Perkin-Elmer 7000 Series 1.1(0) 5 Dec 86" #define UTEXT 0 /* Set for 8 bit text strings */ #include /* Standard UNIX definitions */ #include /* Idris system subroutines */ /*#include "sgtty.h" /* Special tty defs */ #include "pe7tty.h" /* Special tty defs */ /* Symbol Definitions */ #define BUFSIZE 80 /* A buffer size for I/O */ #define MAXTRY 8 /* Times to retry a packet */ #define SOH 1 /* Start of header */ #define CR 13 /* ASCII Carriage Return */ #define XON 17 /* ASCII xon character */ #define SP 32 /* ASCII space */ #define DEL 127 /* Delete (rubout) */ #define MAXPACKSIZ 94 /* Maximum packet size */ #define DEFPACKSIZ 80 /* The default packet size */ #define MAXFNAME 50 /* My maximum for filename length */ #define SPOVER 14 /* The closest I may come to the end of the output buffer */ #define MYTIME 10 /* Seconds after which I should be timed out */ #define MYPAD 0 /* Number of padding characters I will need */ #define MYPCHAR 0 /* Padding character I need (NULL) */ #define MYEOL '\r' /* End-Of-Line character I need */ #define MYQUOTE '#' /* Quote character I will use */ #define DEFQBIN 'Y' /* I will do binary quoting */ #define MYQBIN '&' /* Binary quoting character */ #define DEFCHKT '1' /* Default Check sum type */ #define MYCHKT '1' /* Check sum type */ #define DEFREPT '~' /* Repeat character I will use */ #define MYREPT '~' /* Repeat character that I need */ #define TRUE -1 /* Boolean constants */ #define FALSE 0 /* Macro Definitions */ /* * tochar: convert a control character to a printable one by adding a space. * * unchar: undoes tochar. * * ctl: converts between control characters and printable characters by * toggling the control bit (ie. ^A becomes A and A becomes ^A). */ #define tochar(ch) ((ch) + ' ') #define unchar(ch) ((ch) - ' ') #define ctl(ch) ((ch) ^ 64) /* Global Variables */ /* This is the format for the return packet from the other user */ int spsiz {MAXPACKSIZ}, /* Maximum send packet size */ timint {MYTIME}, /* Timeout for foreign host on sends */ pad {0}; /* How much padding to send */ TEXT padchar {0}, /* Padding character to send */ eol {MYEOL}, /* End-Of-Line character to send */ quote {MYQUOTE}, /* Quote character in incoming data */ qbin {0}, /* binary conversion character */ chkt {MYCHKT}, /* Checksum type '1','2', or '3' */ rept {0}; /* Repeat character if repeat supported */ int size[2] {0}, /* Size of present data in packets */ empty[2] {0}, /* Packets ok to fill if zero */ pknum {0}, /* Current packet buffer index */ n {0}, /* Packet number */ nxo {0}, /* Output packet counter for display */ nxi {0}, /* Nak packet counter for display */ nxs {0}, /* Saved nak counter */ repeat_count {0}, /* Repeat counter for repeated characters */ numtry {0}, /* Times this packet retried */ oldtry {0}, /* Times previous packet retried */ iflg {0}, /* Indicates to turn on the image mode */ image {0}, /* -1 means 8-bit mode (driven by iflg) */ speed {0}, /* baud rate of the link */ fflg {0}, /* Indicates to send finish command */ rflg {0}, /* Indicates receive mode */ sflg {0}, /* Indicates send mode */ xflg {0}, /* Indicates server mode */ gflg {0}, /* Indicates get file mode */ vflg {0}, /* Verbose messages in server mode */ tflg {0}, /* Turn around flag */ pflg {0}, /* What kind of parity are we using? */ spflg {0}, /* This is our start-up parity flag */ debug {0}, /* indicates level of debugging output (0=none) */ filnamcnv {0}, /* -1 means do file name case conversions */ filecount {0}; /* Number of files left to send */ TEXT state {0}, /* Present state of the automaton */ rqbin {0}, /* 8 bit quote character saved in rpar */ sqbin {0}, /* 8 bit quote character used in spar */ rrept {0}, /* Repeat character used in spar */ **filelist {0}, /* List of files to be sent */ *filnam {0}, /* Current file name */ *files[10] {0}, /* Pointers for the server file names */ filename_buffer[MAXPACKSIZ] {0}; /* Buffer for the server file name */ TEXT recpkt[MAXPACKSIZ] {0}, /* Receive packet buffer */ *packet[2] {0}, /* Packet pointers */ packet0[MAXPACKSIZ] {0}, /* Send packet zero buffer */ packet1[MAXPACKSIZ] {0}; /* Send packet one buffer */ FILE ttyfd {0}; /* File descriptor of tty for I/O, 0 if remote */ FIO *fd {0}; /* pfio pointer */ FIO pfio {0}; /* Buffer structure for the files */ struct tty ttymode {0}, /* tty raw mode */ savemode {0}; /* tty cooked mode */ struct { int files; /* Number of files */ int fc; /* Number of characters in files */ int pli; /* Number of packets received */ int plo; /* Number of packets sent */ int cli; /* Number of characters received */ int clo; /* Number of characters sent */ ULONG time; /* Total time for transaction */ } total {0}, file {0}; /* * m a i n * * Main routine - parse command and options, set up the * tty lines, and dispatch to the appropriate routine. * */ main(argc,argv) int argc; /* Character pointers to and count of */ char **argv; /* command line arguments */ { TEXT *ttyname, /* tty name for LINE argument */ *cp; /* char pointer */ printf("IDRIS Kermit - %s",KVERSION); if (argc < 2) /* Make sure there's a command line */ Usage("Invalid command line - Not enough arguments."); packet[0] = &packet0; /* Set up the packet pointers */ packet[1] = &packet1; /* Turn off the parse flags */ sflg = FALSE; rflg = FALSE; xflg = FALSE; gflg = FALSE; fflg = FALSE; image = iflg = FALSE; filnamcnv = TRUE; /* conversion for UNIX systems */ vflg = FALSE; /* Turn off verbose flags */ debug = FALSE; /* Turn off the debug mode */ ttyname = 0; /* We did not get a line assignment (pointer) */ speed = FALSE; /* Speed not indicated */ spflg = pflg = FALSE; /* Parity is not indicated */ tflg = FALSE; /* No turn around character */ sqbin = DEFQBIN; /* Set up default qbin character */ rqbin = sqbin; qbin = FALSE; /* Reset the display counters */ nxo = 0; nxi = 0; nxs = -1; getflags(&argc,&argv,\ "s,r,x,g,f,i,u,v,d#,l*,b#,p?,t:F ",\ &sflg, &rflg, &xflg, &gflg, &fflg, &iflg, &filnamcnv, &vflg,\ &debug, &ttyname, &speed, &pflg, &tflg); if (!ttyname) /* If LINE was not specified, use default */ ttyname = "/dev/lnk0"; ttyfd = open(ttyname, UPDATE, 0);/* Open the tty line */ if (ttyfd <= 0) Usage("Cannot open %s.",ttyname); /* Put the proper tty into the correct mode */ egtty(ttyfd,&savemode); /* save for later use */ egtty(ttyfd,&ttymode); /* set for changing the setup */ printf("IDRIS Kermit - %s",KVERSION); vflg = vflg ? vflg : fflg || gflg || rflg || sflg || debug ? TRUE : FALSE; if ((gflg + xflg + rflg + sflg) != 1) /* Only one command allowed */ if (!fflg) Usage("Only one command allowed - g ! r ! s ! x."); if (fflg && (xflg || rflg)) Usage("Finish with server or receive?"); if (!pflg) /* Do we set up the parity? */ { /* Check for raw mode and if true then force no parity */ if (ttymode.t_mode & M_RAW) pflg = FALSE; else { /* What has the caller set into the hardware */ switch (ttymode.t_mode & (M_EVEN | M_ODD)) { case M_ODD: pflg = 'o'; break; case M_EVEN: pflg = 'e'; break; case M_EVEN | M_ODD: pflg = 'm'; break; case 0: pflg = 's'; } } } else { if (scnbuf("neoms",5,pflg) == 5) Usage("Improper parity call out."); if (pflg == 'n') pflg = FALSE; /* If no parity then set it */ } spflg = pflg; /* Set up start-up parity */ /* Put the hardware into raw mode */ ttymode.t_mode = (M_RAW | M_ALL | MR_XON); if (speed) /* Do we set up the speed? */ { switch (speed) { case 110: speed = B110; ttymode.t_mode |= M_2STOP; break; case 150: speed = B150; break; case 300: speed = B300; break; case 1200: speed = B1200; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; default: Usage("Bad line speed."); } ttymode.t_ispeed = speed; ttymode.t_ospeed = speed; } /* Set up the time-out and buffer size */ ttymode.t_min = 128; ttymode.t_time = MYTIME * 10; if (tflg) /* Turn around flag on? */ ttymode.t_cgo = NULL; /* Turn off XON in XON/XOFF */ estty(ttyfd,&ttymode); /* Put asg'd tty in requested mode */ /* All set up, now execute the command that was given. */ if (filnamcnv > 0) /* Do we want to do file name conversions? */ filnamcnv = FALSE; if (iflg) /* Turn on the image mode? */ { image = TRUE; /* Test the parity and make a determination of the qbin mode info */ if (pflg) /* Pflg contains the parity bit if set */ sqbin = MYQBIN; /* Set 8-bit quoting */ else sqbin = DEFQBIN; /* We can do 8 bit transfers */ } if (debug) { printf("Main 1: S=%d, R=%d, X=%d, G=%d,", sflg, rflg, xflg, gflg); printf(" F=%d, I=%d, U=%d, V=%d,", fflg, iflg, filnamcnv, vflg); printf(" D=%d\n", debug); printf(" L=%s, B=%d, P=%c,", ttyname, speed, pflg); printf(" T=%d\n", tflg); printf("Main 2: Bits for ttyfd %x\n",ttymode.t_mode); } if (gflg || sflg) { /* Anything to get or send? */ if (argc--) filnam = *argv++; /* Get file to send */ else { savemode.t_time = 0; estty(ttyfd,&savemode); /* restore the old mode */ Usage("File name required."); /* and give error */ } fd = NULL; /* Indicate no file open yet */ filelist = argv; /* Set up the rest of the file list */ filecount = argc; /* Number of files left to get or send */ } dostat(1); if (gflg) /* Get command */ { if (debug) printf("Main 3: Get command\n"); if (getsw() == FALSE) Usage("Get failed."); else printmsg("Get done."); } if (sflg) /* Send command */ { if (debug) printf("Main 4: Send command\n"); if (sendsw() == FALSE) /* Send the file(s) */ Usage("Send failed."); /* Report failure */ else /* or */ printmsg("Send done."); /* success */ } /* end of sflg */ if (rflg) /* Receive command */ { if (debug) printf("Main 5: Receive command\n"); if (debug < 3) { if (recsw() == FALSE) /* Receive the file(s) */ Usage("Receive failed."); else /* Report failure */ printmsg("Receive done."); /* or success */ } /* End of test for no receive of data (debug < 3) */ } /* End of (rflg) test */ if (xflg) while (server() == TRUE); if (fflg) { if (debug) printf("Main 6: Finish command\n"); if (finishsw() == FALSE) Usage("Finish failed."); else printmsg("Finish done."); } if (vflg) if(total.files > 1) { printf("%d Files\n", total.files); printf("%d Bytes %d Seconds %d/%d Packets", total.fc, total.time, total.plo, total.pli); printf(" %d/%d Characters\n", total.clo, total.cli); } /* Restore the tty (reset the timeout to infinite) */ savemode.t_time = 0; estty(ttyfd,&savemode); exit(YES); } /* * s e n d s w * * Sendsw is the state table switcher for sending files. It loops until * either it finishes, or an error is encountered. The routines called * by sendsw are responsible for changing the state. * */ sendsw() { TEXT sinit(), sfile(), sdata(), seof(), sbreak(); n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ state = 'S'; /* Send initiate is the start state */ while(TRUE) /* Do this as long as necessary */ { if (debug) printf("Sendsw 1: State: %c\n",state); switch(state) { case 'S': state = sinit(); break; /* Send-Init */ case 'T': state = sfile(); break; /* Send-File */ case 'E': state = sdata(); break; /* Send-Data */ case 'Z': state = seof(); break; /* Send-End-of-File */ case 'B': state = sbreak(); break; /* Send-Break */ case 'C': return (TRUE); /* Complete */ case 'A': /* "Abort" */ default: return (FALSE); /* Unknown, fail */ } } } /* * r e c s w * * This is the state table switcher for receiving files. * */ recsw() { TEXT rinit(), rfile(), rdata(); /* Use these procedures */ n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ state = 'R'; /* Receive-Init is the start state */ while(TRUE) { if (debug) printf("Recsw 1: state: %c\n",state); switch(state) /* Do until done */ { case 'R': state = rinit(); break; /* Receive-Init */ case 'F': state = rfile(); break; /* Receive-File */ case 'D': state = rdata(); break; /* Receive-Data */ case 'C': return (TRUE); /* Complete state */ case 'A': default: return (FALSE); /* "Abort" state */ } } } /* * g e t s w * * Get a file from the other end * */ getsw() { TEXT ginit(), iinit(), rfile(), rdata(); /* Use these procedures */ TEXT filnam1[MAXFNAME], *newfilnam, *cp; int num, len; cpystr(filnam1, filnam, NULL); /* Copy file name */ newfilnam = cp = filnam1; while (*cp != '\0') if (*cp++ == '/') newfilnam = cp; if (filnamcnv) /* Convert lower case to upper */ for (cp = newfilnam; *cp != '\0'; cp++) *cp = toupper(*cp); len = cp - newfilnam; printmsg("Requesting %s as %s",filnam, newfilnam); n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ state = 'S'; while (TRUE) { if (debug) printf("Getsw 1: State: %c\n",state); switch (state) { case 'S': state = iinit(); break; /* Try this */ /* Then this */ case 'R': state = ginit(len,newfilnam); break; case 'F': state = rfile(); break; /* Fetch file name */ case 'D': state = rdata(); break; /* Fetch the data */ case 'C': return (TRUE); /* End it all goodly */ case 'A': default: return (FALSE); /* End it all badly */ } } } /* * s e r v e r * * This is the state table switcher for the server mode * */ server() { TEXT xinit(), rfile(), rdata(); /* Use these procedures */ TEXT sinit(), xfile(), sdata(), seof(), sbreak(); /* and these */ image=iflg; pflg=spflg; n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ state = 'X'; /* Begin is the start state */ while(TRUE) { if (debug) printf("Server 1: state: %c\n",state); switch(state) /* Do until done */ { case 'X': state = xinit(); break; /* Fetch what to do */ case 'S': state = sinit(); break; /* Send init packets */ case 'T': state = xfile(); break; /* maybe open file */ case 'E': state = sdata(); break; /* Send the data */ case 'Z': state = seof(); break; case 'B': state = sbreak(); break; case 'F': state = rfile(); break; /* Receive-File */ case 'D': state = rdata(); break; /* Receive-Data */ case 'C': return (TRUE); /* Complete state */ case 'G': return (FALSE); /* Finish command */ case 'A': default: return (debug ? FALSE : TRUE); } } } /* * f i n i s h s w * * finishsw is the state table switcher for sending general commands * It loops until * either it finishes, or an error is encountered. The routines called * by finishsw are responsible for changing the state. * */ finishsw() { TEXT sfinish(); n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ state = 'F'; /* Send finish is the start state */ while(TRUE) /* Do this as long as necessary */ { if (debug) printf("Finishsw 1: State: %c\n",state); switch(state) { case 'F': state = sfinish(); break; /* Send-Finish command */ case 'C': return (TRUE); /* Complete */ case 'A': /* "Abort" */ default: return (FALSE); /* Unknown, fail */ } } } /* * i i n i t * * Initiate: send this host's parameters and get other side's back. * */ TEXT iinit() { int num, len, slen; /* Packet number, length */ if (numtry++ > 2) return ('R'); /* If too many tries, give up */ slen = spar(packet0); /* Fill up init info packet */ flushinput(); /* Flush pending input */ eol = MYEOL; /* Preset to my eol character */ spack('I',n,slen,packet0); /* Send an I packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'Y': /* ACK */ if (n != num) /* If wrong ACK, stay in S state */ { /* Wrong packet number */ case 'N': /* NAK, Try it again */ case FALSE: nxi++; return (state); } if (!rpar(recpkt,&len)) /* Get the other side's init data */ return ('A'); /* error with the packet parameters */ nxtpkt(); n = 0; /* Reset packet number to zero */ return ('R'); /* OK, switch state to R */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ default: return ('A'); /* Anything else, just "abort" */ } } /* * s i n i t * * Send Initiate: send this host's parameters and get other side's back. */ TEXT sinit() { int num, len, slen; /* Packet number, length */ if (numtry++ > MAXTRY) return ('A'); /* If too many tries, give up */ slen = spar(packet0); /* Fill up init info packet */ flushinput(); /* Flush pending input */ eol = MYEOL; /* Preset to my eol character */ spack('S',n,slen,packet0); /* Send an S packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'Y': /* ACK */ if (n != num) /* If wrong ACK, stay in S state */ { /* Wrong packet number */ case 'N': /* NAK, Try it again */ case FALSE: nxi++; return (state); } if (!rpar(recpkt,&len)) /* Get the other side's init data */ return ('A'); /* error with the packet parameters */ nxtpkt(); return ('T'); /* OK, switch state to T */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ default: return ('A'); /* Anything else, just "abort" */ } } /* * r i n i t * * Receive Initialization */ TEXT rinit() { int len, num, slen; /* Packet length, number */ if (numtry++ > MAXTRY) return ('A'); /* If too many tries, "abort" */ switch(rpack(&len,&num,recpkt)) /* Get a packet */ { case 'S': /* Send-Init packet */ if (!rpar(recpkt,&len)) /* Get the other side's init data */ return ('A'); /* error with the packet parameters */ slen = spar(packet0); /* Fill up packet with my init info */ spack('Y',n,slen,packet0); /* ACK with my parameters */ oldtry = numtry; /* Save old try count */ nxtpkt(); return ('F'); /* Enter File-Receive state */ case FALSE: /* Didn't get packet */ nxi++; spack('N',n,0,0); /* Return a NAK */ return (state); /* Keep trying */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ default: return ('A'); /* Some other packet type, "abort" */ } } /* pe7mai.c End-of-file */