/* File uclk15.c on ucl u44d - UCL Kermit main source file; Chris Kennington. 30th September 1985 */ /* This is a remote-only Kermit for Berkeley Unix 2.8 (11/44), produced at UCL by surgery on the "demonstration" Kermit, plus enhancements. The program should run unmodified in any unix-like environment; for use in any other environment in which an ASCII OS supports a VDU terminal on an RS232 port, there should be no need to modify any routines in this file. All possibly machine-dependent routines have been isolated in file "uclkmd.c"; these should be checked before compilation. 8th-bit prefixing is specified by a "8" flag on the command-line; "7" forces 7-bit, "i" forces 8-bit image. If no such flag, then 7-bit is assumed. Repeat-prefixing is always potentially on. In 7-bit mode only: LF is converted to CR/LF in transmission and CR, LF, CR/LF or LF/CR to LF on receipt; this code is in the machine-dependent routines. Chris Kennington. UCL. 9th May 1985. */ /* The modification history of this program is in file uclkmods.dox */ /* Identification string: */ char ident[] ="UCL Remote-only Kermit, V15B, March 1986.\n"; #include /* Standard UNIX definitions */ #include /* isascii definitions */ /* MAX & MIN values accepted */ #define MAXPACKSIZ 94 /* Maximum packet size */ #define MAXTIM 60 /* Maximum timeout interval */ #define MAXTRY 10 /* Times to retry a packet */ #define MINTIM 2 /* Minumum timeout interval */ /* My values to send to other end */ #define MYEOL '\n' /* End-Of-Line character I need */ #define MYPAD 0 /* Number of padding characters I will need */ #define MYPCHAR 0 /* Padding character I need (NULL) */ #define MYQUOTE '#' /* Quote character I will use */ #define MYTIME 10 /* Seconds after which I should be timed out */ /* Boolean constants */ #define TRUE -1 #define FALSE 0 /* ASCII Chars */ #define LF 10 #define CR 13 /* ASCII Carriage Return */ #define DEL 127 /* Delete (rubout) */ #define ESC 0x1b /* real escape char */ #define SOH 1 /* Start of header */ #define SP 32 /* ASCII space */ /* Macro Definitions */ /* * tochar: converts 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 ) #define forever while(1) /* procedures passed as arguments etc.:- */ extern char cread(); extern char rinit(), rfile(), rdata(); /* receive procedures */ extern char sinit(), sfile(), sdata(), seof(), sbreak(); /* send procs */ extern int outfc(), outfile(); /* procedures in system-dependent file uclkmd.c:- */ extern char ascout(), ascedit(), filerr(), nextin(); extern int flushinput(), cooktty(), rawtty(), timoset(), unbuffer(); /* Global Variables */ int aflg, rflg, sflg, /* flags for AUTO, RECEIVE, SEND */ debug, /* indicates level of debugging output (0=none) */ filnamcnv, /* -1 means do file name case conversions */ filecount, /* Number of files left to send */ image, oimage, /* 0 = 7-bit, 1 = 8-bit mode, 2 = prefixing */ n, /* Packet number */ oldt, /* previous char in encode() */ numtry, /* Times this packet retried */ oldtry, /* Times previous packet retried */ pad, /* How much padding to send */ qu8, rpt, rptflg, sz, size, /* Size of present data */ spsiz, /* Maximum send packet size */ timint = 10; /* Timeout for foreign host on sends */ char c, /* dummy char */ *dname, /* name of debug file UCL */ *dt, /* data-block code/decode */ eol = '\n', /* End-Of-Line character to send */ **filelist, /* List of files to be sent */ *filenames[11], *filnam, /* Current file name */ getfiles[MAXPACKSIZ], iflg = 0, /* 8th-bit-mode set */ packet[MAXPACKSIZ], /* Packet buffer */ padchar, /* Padding character to send */ quote = '#', /* Quote character in incoming data */ recpkt[MAXPACKSIZ], /* Receive packet buffer */ state, /* Present state of the automaton */ timflag, /* timeout flag UCL */ type; /* received-packet type */ char amauto[] = "%sI am in automatic-server mode!"; char badpack[] = "%sYour Kermit sent invalid packet, type \"%c\"!"; char crlf[] = "\r\n"; char *eighths[] = {"7-bit character", "binary-image-mode", "8th-bit prefixed"}; char goodbye[] = "%sReturning to Unix; goodbye."; char *logicval[]= {"OFF", "ON"}; char noname[] = "%sNo valid names in GET request"; char null[] = ""; /* null string */ char onlyg[] = "%sOnly BYE, LOGOUT, FINISH commands implemented"; char prompt[] = "KmUCL: "; FILE *fp, /* current disk file */ *dfp; /* debug file UCL */ /* * 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 */ { char *cp, **argv1; static char first = 0; int argc1; if (first != 0) { printmsg("Program fault - reentered.\n"); exit(1); } else ++first; unbuffer(); /* unbuffer output */ printf(ident); aflg = sflg = rflg = 0; /* Turn off all parse flags */ cp = *++argv; argv1 = argv; /* remember argument pointers */ argv++; argc1 = --argc; --argc; /* pointers to args */ /* Initialize these values and hope the first packet will get across OK */ eol = CR; /* EOL for outgoing packets */ quote = '#'; /* Standard control-quote char "#" */ pad = 0; /* No padding */ padchar = NULL; /* Use null if any padding wanted */ spsiz = MAXPACKSIZ; qu8 = 0; image = -1; rptflg = TRUE; /* try for repeating */ filnamcnv = TRUE; /* conversion for UNIX systems */ dfp = 0; dname = 0; /* clear debug file ptrs */ if (argc >= 0) while ((*cp) != NULL) switch (*cp++) { /* If command-line, parse characters in first arg. */ case 'a': aflg++; break; /* A = Auto command */ case 'f': filnamcnv = FALSE; /* F = don't do case conversion */ break; /* on filenames */ case 'h': debug = 0; /* there won't be a file yet */ help(); /* H = help messages */ case 'i': /* I = Image (8-bit) mode */ image = 1; ++iflg; break; case 'r': rflg++; break; /* R = Receive command */ case 's': sflg++; break; /* S = Send command */ case '7': /* 7-bit transfer */ image = 0; ++iflg; break; case '8': /* 8-bit prefixed */ image = 2; ++iflg; break; case '-': /* - flag (ignored) */ break; default: --cp; printmsg("Invalid char %c in command-line.",*cp); usage(); } if (image == -1) image = 0; /* default is 7-bit */ oimage = image; /* remember setting */ /* Flags parsed, check for debug filename */ cp = *argv; if (*cp == '-') { if (*++cp != 'D') { /* invalid debug filename */ printmsg("Debug filename <%s> must start with \"D\".",*--cp); usage(); } while (*cp == 'D') { /* count the Ds */ ++debug; ++cp; } dname = cp; /* filename starts after Ds */ dfp = fopen(dname,"a"); ++argv; --argc; } if ((c = aflg+sflg+rflg) > 1) { /* Only one command allowed */ printmsg("One only of Auto OR Receive OR Send!"); usage(); } if (iflg > 1) { printmsg("One only of \"i\" or \"7\" !"); usage(); } printmsg("%s transfer requested;",eighths[image]); if (image != 0) printf(" CR <=> LF translation will NOT be done.\n"); if (c == 0) { /* no action-flag */ printf(crlf); printmsg("No action on command-line; Automatic mode assumed;\n For help use \"kermit h\"."); aflg = 1; } /* Put the tty into the correct mode */ rawtty(); /* All set up, now execute the command that was given. */ if (debug) { if (dfp != 0) { printmsg("Debugging level = %d into file \"%s\";",debug,dname); fprintf(dfp,"\n\n******** UCL Kermit Debug File ** next run starts here **********\n"); fprintf(dfp,ident); fprintf(dfp,"\nDebugging level = %d into file \"%s\";\nCommand-line: <",debug,dname); while (argc1-- > 0) fprintf(dfp," %s",*argv1++); fprintf(dfp," >;"); if (aflg) fprintf(dfp,"\nAuto Command\n"); if (sflg) fprintf(dfp,"\nSend Command\n"); if (rflg) fprintf(dfp,"\nReceive Command\n"); } else { printf("No valid debug file, debug switched off.\r\n"); debug = 0; } } if (sflg) /* Send command */ { if (argc--) { printmsg("Send Command; set your Kermit to receive files\n"); filnam = *argv++; /* Get file to send */ } else { cooktty(); /* restore tty to normal */ printmsg("Send, but no filename given;\n"); usage(); /* and give error */ } fp = NULL; /* Indicate no file open yet */ filelist = argv; /* Set up the rest of the file list */ filecount = argc; /* Number of files left to send */ printmsg("I'll give you 10 seconds to get ready ..."); timoset(10); while (timflag != 0) c = cread(); /* hang about */ if (sendsw() == FALSE) /* Send the file(s) */ printmsg("Send failed."); /* Report failure */ else /* or */ printmsg("done."); /* success */ } else if (rflg) { /* Receive command */ printmsg("Receive Command; set your Kermit to Send files\n"); printmsg("I'll give you 5 seconds to get ready ..."); timoset(5); while (timflag != 0) c = cread(); /* hang about */ timint = 20; if (recsw() == FALSE) /* Receive the file(s) */ printmsg("Receive failed."); else /* Report failure */ printmsg("done."); /* or success */ } else while (aflg) { /* automatic (sub-server) mode */ printmsg("Auto Command; set your Kermit to Send or Get files;"); printf("\tto cancel, use \"LOGOUT\", \"BYE\" or \"FINISH\" command,\r\n"); printf("\t(or send ESCAPE-C at any time).\r\n"); if (autosw() == TRUE) printmsg("Auto Send/Get complete"); else printmsg("Auto-mode cancelled"); } closeall(); cooktty(); exit(0); } /* End main() */ autosw() /* state switcher for automatic mode */ { int len, num; char c, *wild; forever { timint = 40; /* slow NAKs at first */ if (debug) fprintf(dfp,"\nAutosw() waiting: 8th-bit setting now %d, char is %c; reptflag %s.\n",image,qu8,logicval[-rptflg]); n = numtry = 0; switch ( (type = rpack(&len,&num,recpkt)) ) { /* decipher request from micro */ case 'S': /* send-init */ /* receive file(s) from local Kermit */ state = 'F'; rpar(recpkt,len); len = spar(packet); spack('Y',n,len,packet); /* ack parameters */ n = (n+1)%64; while (state != 'C') { /* until EoF */ if (debug == 1) fprintf(dfp," autosw state %c\n",state); switch(state) { case 'F': state = rfile(); break; case 'D': state = rdata(); break; case 'A': state = 'C'; break; case 'C': break; } } /* end switch, while */ image = oimage; break; /* end of reception */ case 'R': /* get command */ /* send file(s) to micro Kermit */ if (debug) fprintf(dfp,"\n getting files <%s>, ", recpkt); movemem(recpkt,getfiles,len); /* file name(s) */ getfiles[len] = 0; wild = getfiles; while ( (c = (*wild++)&0x7f) != 0 ) if ( (c == '*') || (c == '?') ) { error("Cannot accept wildcard <%c> in \"GET\"",c); break; /* no wildcard processing */ } filecount = decol8(getfiles,filenames,10); filelist = filenames; if (debug > 1) { fprintf(dfp," %d files, ",filecount); while (*filelist != 0) fprintf(dfp,"<%s> ",*filelist++); filelist = filenames; } if (filecount--) { /* if any valid names */ filnam = *filelist++; fp = NULL; if (sendsw() == FALSE) /* Send the file(s) */ printmsg("Send failed."); /* Report failure */ else printmsg("Files sent; still in auto-mode."); } else /* if no names */ error(noname,prompt); image = oimage; break; case 'G': /* generic command */ /* only "BYE" and "LOGOUT" are acceptable */ if ( ( (c = *recpkt&0x5f) == 'F' ) || (c == 'L') ) { error(goodbye,prompt); aflg = 0; return(TRUE); } else error(onlyg,prompt); break; case 'I': /* info-exchange packet */ rpar(recpkt,len); len = spar(packet); spack('Y',n,len,packet); /* ack the parameters */ break; case 'E': /* error packet */ prerrpkt(recpkt); aflg = 0; error(goodbye,prompt); return(FALSE); case 'C': /* OS command */ error(onlyg,prompt); break; case 'N': /* NAK - out of phase */ error(amauto,prompt); break; case FALSE: /* timeout - micro not sending */ printf(crlf); printmsg("Please enter \"SEND\", \"GET\" or \"BYE\" command to micro Kermit,"); printf("\t(or send ESCAPE-C to cancel UCL Kermit) ...\n"); spack('N',0,0,null); break; default: /* bad packet */ error(badpack,prompt,type); return(FALSE); } } /* end switch, forever */ } /* End autosw() */ bufill(buffer) /* Get a bufferful of data from the file that's being sent */ char *buffer; { int t; static int softeof= FALSE; if (softeof == TRUE) { softeof = FALSE; return(EOF); } rpt = sz = 0; /* Inits for encode() */ dt = buffer; oldt = -2; /* impossible last char */ while((t = getc(fp)) != EOF) { /* next character */ t = ascedit(t); /* edited if necessary */ encode(t); /* to buffer */ if (sz >= spsiz-6) /* Check length */ return(sz); } /* reach here on (hard) EOF or error */ if (sz==0) return(EOF); else { softeof = TRUE; /* EOF next time */ return(sz); } } /* end of bufill() */ closeall() /* close both files */ { if (fp != NULL) fclose(fp); if (dfp != NULL) { fprintf(dfp,"\n\n************************* End of Run ******************************\n\n"); fclose(dfp); } } /* end of closeall() */ char cread() /* get next char from line */ /* UCL Input Routine: handle escapes as well, return SOH & printables, eat all other controls. */ { char c, t; int ex; ex = 0; while (ex == 0) { t = nextin(); /* get next char */ if (timflag == 0) return(0); /* timeout occured */ c = t&0x7f; if (image != 1) t = c; if ( (c == CR) || (c == LF) || (c == SOH) || (c > 0x1f) ) return(t); /* left only with invalid controls */ if (c == ESC) { /* process escape */ printf("\nKmUCL-ESC"); Repeat: /* for ignoring "-" */ read (0, &t, 1); putchar(t); c = t&0x5f; switch(c) { case SOH: return(t); case 'C': cooktty(); printf("\nUCL Kermit exitting by ESC-C from local station\n"); closeall(); exit(1); case 'H': printf("\n\rUCL Kermit is alive and well ....\n\r"); break; case 0x1f: /* ? */ case 0x0f: /* / */ cooktty(); help1(); rawtty(); break; case '-': goto Repeat; default: printf("???"); break; } } } return(0); } /* End of cread() */ decode(buf,len) /* packet decoding procedure */ /* Called with string to be decoded; Returns 0 or error-code. */ char *buf; int len; { char a, a7, b8, *end, rep; int flg8=0, error=0, r; if (image == 2) flg8 = -1; end = buf + len; while (buf < end) { a = *buf++; if ( rptflg && (a == '~') ) { /* got a repeat prefix? */ rep = unchar(*buf++); /* Yes, get the repeat count, */ a = *buf++; /* and get the prefixed character. */ } else rep = 1; b8 = 0; /* Check high order "8th" bit */ if ( flg8 && (a == qu8) ) { b8 = 0200; a = *buf++; /* and get the prefixed character. */ } if (a == quote) { /* If control prefix, */ a = *buf++; /* get its operand. */ a7 = a & 0177; /* Only look at low 7 bits. */ if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Uncontrollify */ a = ctl(a); /* if in control range. */ } a |= b8; /* OR in the 8th bit */ while (rep-- > 0) { if (image == 0) /* if 7-bit */ r = ascout(a); else /* prefixing / image */ r = putc(a,fp); if (r == EOF) /* if error */ error |= filerr(); } } return(error); } /* end of decode() */ decol8(line,arr,num) /* decollate command-line */ /* Splits up line into sections delimited by controls or blanks, zeros all such chars & places start-addresses into array, up to maximum of num entries; zeros rest of entries and returns count of valid entries. */ char *line, *arr[], num; { char c, count, *start; int i, j; j = count = 0; start = line; for (i=0; i<80, j 0) { arr[j++] = start; count = 0; } if (c == 0) break; /* out of for */ } else if (count++ == 0) /* printable */ start = &line[i]; /* start next parm */ } /* end else, for */ line[i] = 0; /* terminate last parm */ i = j; /* number of parms */ while (j < num) arr[j++] = 0; /* clear garbage */ return(i); } /* End of decol8() */ encode(a) /* encode single character into packet for transmission */ int a; /* char to be encoded */ { int a7; /* Low order 7 bits */ int b8; /* 8th bit of character */ int flg8 = 0; static int oldsz; if (image == 2) flg8 = -1; if (rptflg) { /* repeat-count processing */ if (a == oldt) { /* char is same */ /* This algorithm is simple but relatively inefficient; it stores the repeat flag, count and character each time around so that when the run is broken the buffer is valid; also it treats a pair as a run, which requires 3 bytes not 2 unless the pair is control- or 8bit-prefixed; but it does not require lookahead logic from the data-read. */ sz = oldsz; /* wind back pointer */ dt[sz++] = '~'; /* store prefix */ dt[sz++] = tochar(++rpt); /* & count */ if (rpt > 93) /* force new start */ oldt = -2; /* impossible value */ } else { /* not run, or end */ rpt = 1; oldt = a; /* save char */ oldsz = sz; } } a7 = a & 0177; /* Isolate ASCII part */ b8 = a & 0200; /* and 8th (parity) bit. */ if (flg8 && b8) { /* Do 8th bit prefix if necessary. */ dt[sz++] = qu8; a = a7; } if ((a7 < SP) || (a7==DEL)) { /* Do control prefix if necessary */ dt[sz++] = MYQUOTE; a = ctl(a); } if (a7 == MYQUOTE) /* Prefix the control prefix */ dt[sz++] = MYQUOTE; else if (rptflg && (a7 == '~')) /* If it's the repeat prefix, */ dt[sz++] = MYQUOTE; /* quote it if doing repeat counts. */ else if (flg8 && (a7 == qu8)) /* Prefix the 8th bit prefix */ dt[sz++] = MYQUOTE; /* if doing 8th-bit prefixes */ dt[sz++] = a; /* Finally, insert the character */ dt[sz] = '\0'; /* itself, and mark the end. */ return; } /* end of encode() */ /*VARARGS1*/ error(fmt, a1, a2, a3, a4, a5) /* Remote; send an error packet with the message. * and if debug log in debug file */ char *fmt; { char msg[100]; int len; sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */ len = strlen(msg); spack('E',n,len,msg); /* Send the error packet */ if (debug) fprintf(dfp,msg); return; } /* End of error() */ gnxtfl() /* Get next file in a file group */ { if (debug) fprintf(dfp,"\n gnxtfl: filelist = \"%s\"",*filelist); filnam = *(filelist++); if (filecount-- == 0) return FALSE; /* If no more, fail */ else return TRUE; /* else succeed */ } /* End gnxtfl() */ help() /* UCL help routine */ { printf("\nSummary of UCL Remote Kermit:\n"); help1(); printf("\t\t\t\t hit CR for more "); timoset(30); while (timflag != 0) if (cread() != 0) break; printf("\nBasic syntax of the \"kermit\" command is:\n"); usage(); /* exits */ } /* End of help() */ help1() { printf("\nThis is a remote receive-or-send-or-auto Kermit; normally called\n"); printf(" with a system command-line, failing which it enters automatic mode.\n"); printf("Image-mode transfers may be selected by \"i\"-flag, 8-bit-prefixed transfers\n"); printf(" by \"8\"-flag, otherwise 7-bit data only will be transferred;\n"); printf(" mapping between LFs and CR/LF pairs is only done during 7-bit transfers.\n"); printf("Repeat-prefixing is always available; there is NO Connect facility.\n"); printf("Automatic mode supports the Server-Kermit commands \"SEND\", \"GET\"\n"); printf(" and \"LOGOUT/BYE/QUIT\".\n"); printf("If your transfer breaks down, go into connect-mode and enter\n"); printf(" ESCAPE-C, which will cause UCL Kermit to quit gracefully;\n"); printf(" alternatively enter ESCAPE-H, which will elicit a reassurance.\n"); printf("If debug is requested by \"-D\" flag(s) on second parameter, then\n"); printf(" debug information is written into a file whose name is \n"); printf(" the rest of the second parameter (after last \"D\"); up to three\n"); printf(" \"D\" flags may be entered (but only one \"-\"):\n"); printf("Debug = 1 gives basic trace of automaton states + error-messages,\n"); printf(" = 2 logs packets as sent/received,\n"); printf(" = 3 also logs all chars as read from line, in hex.\n"); return; } /* End of help1() */ movemem(from,to,count) /* shift block of data */ /* moves chars starting at bottom of block */ char *from, *to; int count; { while (count-- > 0) *to++ = *from++; return; } /* End of movemem() */ prerrpkt(msg) /* Print contents of error packet received from remote host. */ char *msg; { if (debug) { fprintf(dfp,"\nKmUCL Aborting with following message from local Kermit:"); msg[50] = 0; fprintf(dfp,"\n \"%s\"",msg); } return; } /* End prerrpkt() */ /*VARARGS1*/ printmsg(fmt, a1, a2, a3, a4, a5) /* Print message on standard output ; * message will only be received if remote is in connect-mode. */ char *fmt; { printf("KmUCL: "); printf(fmt,a1,a2,a3,a4,a5); printf("\n\r"); return; } /* End of printmsg() */ char rdata() /* Receive Data */ { int num, len; /* Packet number, length */ if (debug > 2) printf(" ** rdata() "); if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */ switch(rpack(&len,&num,packet)) /* Get packet */ { case 'D': /* Got Data packet */ if (num != n) /* Right packet? */ { /* No */ if (oldtry++ > MAXTRY) return('A'); /* If too many tries, abort */ if (num == ((n==0) ? 63:n-1)) { /* duplicate? */ spack('Y',num,0,null); /* Yes, re-ACK it */ numtry = 0; /* Reset try counter */ return(state); /* Don't write out data! */ } else return('A'); /* sorry, wrong number */ } /* Got data with right packet number */ if ( (num = decode(packet,len)) != 0 ) { error("Trouble writing file, OS code %xx",num); return('A'); } spack('Y',n,0,null); /* Acknowledge the packet */ n = (n+1)%64; /* Bump packet number, mod 64 */ oldtry = numtry; /* Reset the try counters */ numtry = 0; /* ... */ return('D'); /* Remain in data state */ case 'F': /* Got a File Header */ if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */ if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ { /* It was the previous one */ spack('Y',num,0,null); /* ACK it again */ numtry = 0; /* Reset try counter */ return(state); /* Stay in Data state */ } else return('A'); /* Not previous packet, "abort" */ case 'Z': /* End-Of-File */ if (num != n) return('A'); /* Must have right packet number */ spack('Y',n,0,null); /* OK, ACK it. */ fclose(fp); /* Close the file */ fp = NULL; if (debug) { if ( (len != 0) && (*packet == 'D') ) fprintf(dfp,"\nFile <%s> truncated by local Kermit\n",filnam); else fprintf(dfp,"\nFile <%s> received OK\m",filnam); } if ( (len != 0) && (*packet == 'D') && (image != 1) ) fprintf(fp,"\n\n*** Local Kermit Truncated this File <%s> ***\n", filnam); n = (n+1)%64; /* Bump packet number */ return('F'); /* Go back to Receive File state */ case 'E': /* Error packet received */ prerrpkt(packet); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,null); /* Return a NAK */ return(state); /* Keep trying */ default: error(badpack,prompt,type); return('A'); /* Some other packet, "abort" */ } } /* End of rdata() */ recsw() /* state table switcher for receiving files. */ { if (debug) fprintf(dfp,"Ready to receive file\n"); if (debug > 2) printf(" ** recsw() "); state = 'R'; /* Receive-Init is the start state */ n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ forever { if (debug == 1) fprintf(dfp," recsw 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': return(FALSE); /* "Abort" state */ } } } /* End recsw() */ char rfile() /* Receive File Header */ { int num, len; /* Packet number, length */ char filnam1[50]; /* Holds the converted file name */ if (debug > 2) printf(" ** rfile() "); if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */ switch(rpack(&len,&num,packet)) /* Get a packet */ { case 'S': /* Send-Init, maybe our ACK lost */ if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */ if (num == ((n==0) ? 63:n-1)) { /* Previous packet, mod 64? */ len = spar(packet); /* our Send-Init parameters */ spack('Y',num,len,packet); numtry = 0; /* Reset try counter */ return(state); /* Stay in this state */ } else return('A'); /* Not previous packet, "abort" */ case 'Z': /* End-Of-File */ if (oldtry++ > MAXTRY) return('A'); if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ { /* Yes, ACK it again. */ spack('Y',num,0,null); numtry = 0; return(state); /* Stay in this state */ } else return('A'); /* Not previous packet, "abort" */ case 'F': /* File Header (just what we want) */ if (num != n) return('A'); /* The packet number must be right */ packet[49] = 0; /* ensure string closed */ strcpy(filnam1, packet); /* Copy the file name */ if (filnamcnv) { /* Convert upper case to lower */ for (filnam=filnam1; *filnam != '\0'; filnam++) if (*filnam >= 'A' && *filnam <= 'Z') *filnam |= 040; filnam = filnam1; } if (debug) fprintf(dfp,"\nOpening <%s> for receiving.",filnam1); if ((fp=fopen(filnam1,"w")) == NULL) { error("%sCannot create <%s>",prompt,filnam1); return('A'); } else /* OK, give message */ if (debug) fprintf(dfp,"\nReceiving %s as %s",packet,filnam1); spack('Y',n,0,null); /* Acknowledge the file header */ oldtry = numtry; /* Reset try counters */ numtry = 0; /* ... */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('D'); /* Switch to Data state */ case 'B': /* Break transmission (EOT) */ if (num != n) return ('A'); /* Need right packet number here */ spack('Y',n,0,null); /* Say OK */ if (debug) fprintf(dfp,"All files received\n"); return('C'); /* Go to complete state */ case 'E': /* Error packet received */ prerrpkt(packet); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,null); /* Return a NAK */ return(state); /* Keep trying */ default: error(badpack,prompt,type); return ('A'); /* Some other packet, "abort" */ } } /* End rfile() */ char rinit() /* Receive Initialization */ { int len, num; /* Packet length, number */ if (debug > 2) printf(" ** rinit() "); if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */ switch(rpack(&len,&num,packet)) /* Get a packet */ { case 'S': /* Send-Init */ rpar(packet,len); len = spar(packet); spack('Y',n,len,packet); /* ACK with my parameters */ oldtry = numtry; /* Save old try count */ numtry = 0; /* Start a new counter */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('F'); /* Enter File-Receive state */ case 'E': /* Error packet received */ prerrpkt(packet); /* Print it out and */ return('A'); /* abort */ case 'I': /* Init-parameters */ rpar(packet,len); len = spar(packet); spack('Y',n,len,packet); /* ack with our parameters */ n = (n+1)%64; /* Bump packet number, mod 64 */ return(state); /* & continue */ case FALSE: /* Didn't get packet */ spack('N',n,0,null); /* Return a NAK */ return(state); /* Keep trying */ default: error(badpack,prompt,type); return('A'); /* Some other packet type, "abort" */ } } /* End rinit() */ rpack(len,num,data) /* Read a Packet */ int *len, *num; /* Packet length, number */ char *data; /* Packet data */ { int i, done; /* Data character number, loop exit */ char t, /* Current input character */ cchksum, /* Our (computed) checksum */ rchksum; /* Checksum received from other host */ if (debug > 2) printf(" ** rpack() "); timoset(timint); /* set timeout */ /* The way timeouts are handled is that the flag stays clear, indicating that a timeout has occurred, until it is set to a value again by the next call to timoset(). This means that the effect can run up thro' the procedures without using longjmp(). */ t = 0; *len = 0; /* in case times out */ while (t != SOH) { /* Wait for packet header */ t = cread() & (char)0x7f; if (timflag == 0) return(FALSE); } done = FALSE; /* Got SOH, init loop */ while (!done) /* Loop to get a packet */ { t = cread(); /* Get character */ if (timflag == 0) return(FALSE); if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = t; /* Start the checksum */ *len = unchar(t)-3; /* Character count */ t = cread(); /* Get character */ if (timflag == 0) return(FALSE); if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = cchksum + t; /* Update checksum */ *num = unchar(t); /* Packet number */ t = cread(); /* Get character */ if (timflag == 0) return(FALSE); if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = cchksum + t; /* Update checksum */ type = t; /* Packet type */ for (i=0; i<*len; i++) { /* The data itself, if any */ t = cread(); /* Get character */ if (timflag == 0) return(FALSE); /* as tho' bad checksum */ if (t == SOH) break; /* Resynch if SOH */ cchksum = cchksum + t; /* Update checksum */ data[i] = t; /* Put it in the data buffer */ } if (t == SOH) continue; data[*len] = 0; /* Mark the end of the data */ t = cread(); /* Get last character (checksum) */ if (timflag == 0) return(FALSE); rchksum = unchar(t); /* Convert to numeric */ if (t == SOH) /* Resynchronize if SOH */ continue; done = TRUE; /* Got checksum, done */ } timocan(); if (debug) fprintf(dfp,"Packet %d received; ",*num); if (debug>1) { /* Display incoming packet */ fprintf(dfp," type: %c;",type); fprintf(dfp," num: %d;",*num); fprintf(dfp," len: %d.",*len); if (*len != 0) { fprintf(dfp,"\n data: <"); for (i=0; i<*len; ++i) putc(data[i],dfp); putc('>',dfp); } } /* Fold in bits 7,8 to compute */ cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */ if (cchksum != rchksum) { if (debug) fprintf(dfp,"\n bad checksum: rec'd %xx computed %xx; ", rchksum, cchksum); return(FALSE); } flushinput(); return(type); /* All OK, return packet type */ } /* End of rpack() */ rpar(data,len) /* instal received parameters safely */ char *data, len; /* Set up incoming parameters to either what has been received or, if nil, to defaults. No return-code. */ { char p; /* initialize to defaults in case incoming block is short */ spsiz = 80; /* packet-length 80 chars */ timint = MYTIME; /* timeout as defined */ eol = CR; /* terminator normally CR */ quote = '#'; /* standard control-quote char */ rptflg = FALSE; /* nor repeat-quoting */ while (len-- > 0) { p = data[len]; switch (len) { /* for each parameter */ case 0: /* MAXL */ spsiz = unchar(p); break; case 1: /* TIME */ timint = (unchar(p) < 5) ? 5 : unchar(p); break; case 2: /* NPAD */ pad = unchar(p); break; case 3: /* PADC */ padchar = ctl(p); break; case 4: /* EOL */ eol = unchar(p); break; case 5: /* QCTL */ quote = p; break; case 6: /* QBIN */ if (image == 2) { if (p == 'Y') qu8 = '&'; else if (isalnum(p) == 0) /* provided punctuation */ qu8 = p; else image = 0; break; } break; case 8: /* REPT */ if (p == '~') rptflg = TRUE; break; default: /* CHKT, CAPAS etc. */ break; } } /* end while & outer switch */ if ( (qu8 == 0) && (image == 2) ) /* invlaid setting */ image = 0; if (debug) fprintf(dfp,"\nParameters in: 8th-bit setting now %d, char is %c; reptflag %s.",image,qu8,logicval[-rptflg]); return; } /* End of rpar() */ char sbreak() /* send EoT (break) */ { int num, len; /* Packet number, length */ if (debug > 2) printf(" ** sbreak() "); if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */ spack('B',n,0,packet); /* Send a B packet */ switch (rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless NAK for previous packet, */ if (n != num) /* which is just like an ACK for */ return(state); /* this packet so fall thru to... */ case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, fail */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* and bump packet count */ return('C'); /* Switch state to Complete */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return('C'); /* Receive failure, count as OK */ /* If timed out or etc. when completing, likely other end has gone away */ default: error(badpack,prompt,type); return ('A'); /* Other, "abort" */ } } /* End sbreak() */ char sdata() /* send data packet */ { int num, len; /* Packet number, length */ if (debug > 2) printf(" ** sdata() "); if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ spack('D',n,size,packet); /* Send a D packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ if (n != num) /* which is just like an ACK for */ return(state); /* this packet so fall thru to... */ case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, persist */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ if (len != 0) switch (*recpkt) { /* ACK has data */ case 'Z': /* cancel all */ case 'z': filecount = 0; /* no more to go */ case 'X': /* cancel file */ case 'x': return('Z'); default: /* invalid */ recpkt[20] = 0; /* truncate */ error("%sLocal Kermit sent ACK with data: <%s> - goodbye!",prompt,recpkt); return('A'); } if ((size = bufill(packet)) != EOF) /* data from file */ return('D'); /* Got data, stay in state D */ /* EOF can mean either really end-of-file or an error */ if ( (num = filerr()) != 0 ) { /* actual error */ error("Problem while reading file, OS code %xx",num); return('A'); } return('Z'); /* EOF */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in D */ default: error(badpack,prompt,type); return('A'); /* Anything else, "abort" */ } } /* End sdata() */ sendsw() /* 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. */ { if (debug) { fprintf(dfp,"\nSendsw() sending file; "); if (debug > 2) printf(" ** sendsw() "); } state = 'S'; /* Send initiate is the start state */ n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ forever { /* Do this as long as necessary */ if (debug == 1) fprintf(dfp," sendsw state: %c\n",state); switch(state) { case 'S': state = sinit(); break; /* Send-Init */ case 'F': state = sfile(); break; /* Send-File */ case 'D': 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': return (FALSE); /* "Abort" */ default: return (FALSE); /* Unknown, fail */ } } } /* End sendsw() */ char seof() /* Send EoF */ { int num, len; /* Packet number, length */ if (debug > 2) printf(" ** seof() "); if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */ spack('Z',n,0,packet); /* Send a 'Z' packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */ if (n != num) /* which is just like an ACK for */ return(state); /* this packet so fall thru to... */ case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, hold out */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* and bump packet count */ if (debug) fprintf(dfp,"\nClosing input file <%s>, ",filnam); fclose(fp); /* Close the input file */ fp = NULL; /* Set flag indicating no file open */ if (debug) fprintf(dfp,"looking for next file..."); if (gnxtfl() == FALSE) { /* No more files go? */ if (debug) fprintf(dfp,"\nNo more files to send."); return('B'); /* if not, break, EOT, all done */ } if (debug) fprintf(dfp,"New file is %s\n",filnam); return('F'); /* More files, switch state to F */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in Z */ default: error(badpack,prompt,type); return('A'); /* Something else, "abort" */ } } /* End seof() */ char sfile() /* send file header */ { int num, len; /* Packet number, length */ char filnam1[50], /* Converted file name */ *newfilnam, /* Pointer to file name to send */ *cp; /* char pointer */ if (debug > 2) printf(" ** sfile() "); if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ if (fp == NULL) { /* If not already open, */ if (debug) fprintf(dfp,"\nOpening %s for sending.",filnam); fp = fopen(filnam,"r"); /* open the file to be sent */ if (fp == NULL) /* If bad file pointer, give up */ { error("%sCannot open file <%s>",prompt,filnam); return('A'); } } strcpy(filnam1, filnam); /* Copy file name */ newfilnam = cp = filnam1; while (*cp != '\0') /* Strip off all leading directory */ if (*cp++ == '/') /* names (ie. up to the last /). */ newfilnam = cp; if (filnamcnv) /* Convert lower case to upper */ for (cp = newfilnam; *cp != '\0'; cp++) if (*cp >= 'a' && *cp <= 'z') *cp ^= 040; len = cp - newfilnam; /* Compute length of new filename */ if (debug) fprintf(dfp,"\nSending %s as %s",filnam,newfilnam); spack('F',n,len,newfilnam); /* Send an F packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, stay in F state */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ if ((size = bufill(packet)) != EOF) /* data from file */ return('D'); /* Got data, stay in state D */ /* EOF can mean either really end-of-file or an error */ if ( (num = filerr()) != 0 ) { /* actual error */ error("Problem while reading file, OS code %xx",num); return('A'); } return('Z'); /* EOF (empty file) */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in F state */ default: error(badpack,prompt,type); return('A'); /* Something else, just "abort" */ } } /* End sfile() */ char sinit() /* send initiate (exchange parameters) */ { int num, len; /* Packet number, length */ if (debug > 2) printf(" ** sinit() "); flushinput(); /* Flush pending input */ if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */ len = spar(packet); /* Fill up init info packet */ spack('S',n,len,packet); /* Send an S packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ if (n != num) /* which is just like an ACK for */ return(state); /* this packet so fall thru to... */ case 'Y': /* ACK */ if (n != num) /* If wrong ACK, stay in S state */ return(state); /* and try again */ rpar(recpkt,len); /* Get other side's init info */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ return('F'); /* OK, switch state to F */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case 'I': /* Init-parameters */ rpar(packet,len); len = spar(packet); spack('Y',n,len,packet); /* ack with our parameters */ n = (n+1)%64; /* Bump packet number, mod 64 */ return(state); /* & continue */ case FALSE: return(state); /* Receive failure, try again */ default: error(badpack,prompt,type); return('A'); /* Anything else, just "abort" */ } } /* End sinit() */ spack(stype,num,len,data) /* Send a Packet */ char stype, *data; int num, len; { int i; /* Character loop counter */ char chksum, buffer[100]; /* Checksum, packet buffer */ register char *bufp; /* Buffer pointer */ if (debug > 1) { /* Display outgoing packet */ fprintf(dfp,"\nSending packet; type: %c;",stype); fprintf(dfp," num: %d;",num); fprintf(dfp," len: %d;",len); if (len != 0) { fprintf(dfp,"\n data: <"); for (i=0; i',dfp); } } bufp = buffer; /* Set up buffer pointer */ for (i=1; i<=pad; i++) write(0,&padchar,1); /* Issue any padding */ *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */ *bufp++ = tochar(len+3); /* Send the character count */ chksum = tochar(len+3); /* Initialize the checksum */ *bufp++ = tochar(num); /* Packet number */ chksum += tochar(num); /* Update checksum */ *bufp++ = stype; /* Packet type */ chksum += stype; /* Update checksum */ for (i=0; i> 6)+chksum)&077; /* Compute final checksum */ *bufp++ = tochar(chksum); /* Put it in the packet */ *bufp++ = eol; /* Extra-packet line terminator */ *bufp = CR; /* CR for network */ write(0, buffer,bufp-buffer+1); /* Send the packet */ if (debug) { fprintf(dfp," Packet %d sent;\n",num); if (debug > 2) printf("\n\r"); } return; } /* End of spack() */ spar(data) /* fill up packet with own parameters */ char *data; /* returns length of parameter block (6, 7 or 9) */ { char len; data[0] = tochar(spsiz); data[1] = tochar(MYTIME); data[2] = tochar(MYPAD); data[3] = ctl(0); data[4] = tochar(MYEOL); data[5] = MYQUOTE; len = 6; if (image == 2) { /* 8th-bit prefixing */ if (qu8 == 0) data[6] = 'Y'; else data[6] = qu8; /* feed back 8-quote */ len = 7; } else data[6] = 'N'; if (rptflg) { /* unless repeating turned off */ data[7] = '1'; /* 1-byte checksum */ data[8] = '~'; /* only ~ for repeating*/ len = 9; } if (debug) fprintf(dfp,"\nParameters out: 8th-bit setting now %d, char is %c; reptflag %s.",image,qu8,logicval[-rptflg]); return(len); } /* End of spar() */ timocan() /* cancel timeout */ { timoset(0); timflag = 0xff; return; } /* End of timocan() */ timoex() /* action timeout */ { timflag = 0; /* clear flag */ if (debug) { printmsg("Timeout ... "); fprintf(dfp,"Timeout ..."); } return; } /* End of timoex() */ usage() /* Print summary of usage info and quit */ { cooktty(); printf("Usage: kermit s[f78i] [-Ddebug-file] file(s) ... (send mode)\n"); printf("or: kermit r[f78i] [-Ddebug-file] (receive mode)\n"); printf("or: kermit a[f78i] [-Ddebug-file] (auto mode)\n"); printf(" where f = filenames not converted to upper case,\n"); printf(" 7 = force 7-bit data transfer,\n"); printf(" 8 = attempt 8th-bit-prefixed transfer,\n"); printf(" i = force 8-bit-image transfer,\n"); printf(" and debug-file-name is preceded by \"-\" and up to 3 \"D\"-flags.\n"); printf("or: kermit h (help display).\n"); printf(crlf); closeall(); exit(0); } /* End of usage() */ /************** END of FILE uclk??.c **************************/