/************************************************************************ * * * VICTOR VERSION 1.0 * * * *************************************************************************/ /************************************************************************ * * * KERMIT * * * * This implementation is based on the sample implementation * * supplied in chapter 2 of the Kermit Protocol Manual. This * * version of Kermit was code (copied) and augmented (my own * * ideas) by: * * W. Hertha * * Victor Technologies (Canada) Ltd. * * 55 Savage Drive * * Cambridge, Ontario, Canada * * N1R 5T1 * * * * Thanks are in order to Dr. Joe Angel of the University of * * Saskatchewan for taking care of all the details of getting * * the code and documentation, and providing a DEC TOPS 20 * * system to "talk" to and his efforts in the debugging of * * this product. * * * ************************************************************************* * * * This implementation is missing (or varies in)the following * * - The SET command is implemented using softkeys * * - Only ASCII files may be transmitted * * - Server functions are not implemented * * - There is limited massaging of the file name supplied by the host * * thus hosts with weird naming conventions may cause problems on a * * receive * * * *************************************************************************/ /************************************************************************ * * * Coding Conventions * * * * This is a list of conventions * * [1] All #define names are capitalised * * [2] All global variables have the first character capitalised * * [3] Local variables, function names are in lower case * * [4] Other than a module prolog, in-line commenting is limited * * * *************************************************************************/ /************************************************************************ * * * System dependancies * * * * An attempt has been made to reduce system dependancies. The areas * * which are system dependant are as follows: * * 1. Serial port interfaceing * * 2. CRT display interfaceing * * 3. File system (MS-DOS file naming conventions) * * * * Serial Port Interface * * In order to reduce the effects, all interfaceing to the serial port * * is done through a series of routines grouped together towards the * * end of the listing. * * * * CRT display interface * * Here also most of the interfaces to the display is performed by * * low level routine. In fact many are actually just defines. Defines * * where used so that systems that do not support VT-52 escape sequences * * may replace these with functions. It is worth while to note the * * following features of the display (further information may be found * * in the Operators Reference Guide or the Supplemental Technical * * Reference material). * * 1. Screen addressing is 1 relative, that is HOME is (1,1) * * * *************************************************************************/ #include "stdio.h" #define MAXPACK 94 /* Maximum Packet Size */ #define SOH '\001' /* Start of header */ #define SP ' ' /* Space */ #define CR '\015' /* Carriage Return */ #define LF '\012' /* Line feed */ #define BEL '\007' /* Bell */ #define BS '\010' /* Back Space */ #define DEL '\177' /* Delete (Rubout) */ #define REQCAN '?' #define MAXTRY 5 /* # of times to retry a package */ #define MYQUOTE '#' /* Quote Character used Locally */ #define MYPAD 0 /* Number of Padding characters */ #define MYPCHAR 0 /* Local padding character */ #define MYEOL CR /* Local End-Of-Line character */ #define MYTIME 10 /* Local time out time in seconds */ #define PORTA 0 /* Code for port A */ #define PORTB 1 /* Code for Port B */ #define CR2 2 /* Code for control register 2 */ #define CR3 3 #define CR4 4 #define CR5 5 #define RX 1 /* Receive character available */ #define TX 4 /* Send buffer empty */ #define DCD 8 /* Data Carrier Detect */ #define SEND 1 /* Sending mode code */ #define RECEIVE 2 /* Receiving mode code */ #define STATUS_LINE 25 /* Line number of status line on CRT */ #define SHOW_BASE 5 /* Starting line of "Show" data */ #define POS_MAXPACK 0+SHOW_BASE #define POS_MYPCHAR 1+SHOW_BASE #define POS_MYPAD 2+SHOW_BASE #define POS_MYTIME 3+SHOW_BASE #define POS_MYEOL 4+SHOW_BASE #define POS_MYQUOTE 5+SHOW_BASE #define POS_WARN 6+SHOW_BASE #define POS_ECHO 7+SHOW_BASE #define POS_BAUD 8+SHOW_BASE #define POS_BREAK 9+SHOW_BASE #define POS_PORT 10+SHOW_BASE #define ENDFIL '\034' /* End of Logical file */ #define ENDSEC '\035' /* End of help message section */ #define ENDPAG '\037' /* End of page */ #define ENDSS '\003' /* End of subsection */ #define ENQUIRE '\005' /* Enquirey during help */ #define ROOT_H 1 /* Root help */ #define SET_H 2 #define ON_H 3 #define ECHO_H 4 #define WARN_H 5 #define BAUD_H 6 #define TRACE_H 7 #define GETCP_H 8 #define tochar(ch) (char) (ch + ' ') #define unchar(ch) (ch - ' ') #define ctl(ch) ((ch ^ 64) & 0x0ff) /************************************************************************ * Get character from keyboard but do not echo */ #define getnec() (bdos(7) & 0x0ff) /************************************************************************ * Get keyboard status */ #define getkbs() (bdos(11) & 0x0ff) /************************************************************************ * Clear keyboard buffer */ #define clrkb() for (;getkbs();getnec()) /************************************************************************ * Get MSDOS Version ID */ #define dosid() ((bdos(30) >> 8) & 0x0ff) /************************************************************************ * Put a string to standard output */ #define putstr(str) puts(str,stdout) /************************************************************************ * Put a string in inverse video to standard output */ #define putistr(str) printf("\033p%s\033q",str) /************************************************************************ * Put out a menu on the 25th line */ #define putmenu(str) printf("\033j\033x1\033Y8 \033K%s\033y1\033k",str) /************************************************************************ * Turn on the cursor */ #define cursr_on() puts("\033y5",stdout) /************************************************************************ * Turn off cursor */ #define cursr_off() puts("\033x5",stdout) /************************************************************************ * Clear to end of line X starting at column Y */ #define clear_eol(x,y) printf("\033Y%c%c\033K",x+31,y+31) /************************************************************************ * Clear the screen */ #define clear_screen() puts("\033E",stdout) /************************************************************************ * Move the cursor to home position */ #define home() puts("\033H",stdout) /************************************************************************ * Position cursor at row X, column Y */ #define pos(x,y) printf("\033Y%c%c",x+31,y+31) /************************************************************************ * Position cursor at row X, column Y and output character ch */ #define plotc(x,y,ch) printf("\033Y%c%c%c",x+31,y+31,ch) /************************************************************************ * Position cursor at row X, column Y and output inverse video character ch */ #define plotic(x,y,ch) printf("\033Y%c%c\033p%c\033q",x+31,y+31,ch) /************************************************************************ * Position cursor at row X, column Y and output decimal (integer) number */ #define plotd(x,y,n) printf("\033Y%c%c%d",x+31,y+31,n) /************************************************************************ * Position cursor at row X, column Y and output Unsigned long */ #define plotU(x,y,n) printf("\033Y%c%c%U",x+31,y+31,n) /************************************************************************ * Position cursor at row X, column Y and output string s */ #define plotstr(x,y,str) printf("\033Y%c%c%s",x+31,y+31,str) /************************************************************************ * Position cursor at row X, column Y and output inverse video string s */ #define plotistr(x,y,str) printf("\033Y%c%c\033p%s\033q",x+31,y+31,str) /************************************************************************ * Position cursor at row X, column Y and output inverse video string s */ #define plotustr(x,y,str) printf("\033Y%c%c\0330%s\0331",x+31,y+31,str) /************************************************************************ * Check status of port */ #define sstatus(s,p) (peek(p+2,0x0e004) & s) /* */ /************************************************************************ * * * Defintion of Global Variables * * * *************************************************************************/ typedef struct { int si; /* Position in (send)trace buffer */ int smnum[256]; /* (Send) message number */ char smtype[256]; /* (Send) message type */ int ri; /* Position in (receive) trace buffer */ int rmnum[256]; /* (Receive) message number */ char rmtype[256]; /* (Receive) message type */ } TRACE_BUF; TRACE_BUF Trace; int Size, /* Size of Present data */ N, /* Message Number */ Rpsiz, /* Maximum Receive packet size */ Spsiz, /* Maximum Send Packet Size */ Pad, /* How much padding to send */ Timint, /* Time out for remote host */ Numtry, /* Number of retries for a package */ Oldtry; /* Times Previous package retried */ char State, /* Present state of the automaton */ Padchar, /* Padding character to send */ Eol, /* End-of-line character to send */ Quote, /* Quote character in in-coming data */ Filnam[50], /* The file name */ Recpkt[MAXPACK], /* Receive packet buffer */ Packet[MAXPACK]; /* Packet buffer */ int Mypad = MYPAD, Mytime = MYTIME, Maxpack = MAXPACK; char Break = '\03', /* Break code for DEC */ Mydel = DEL, Mypchar = MYPCHAR, Myquote = MYQUOTE, Myeol = MYEOL; int Msdos20 = FALSE, /* Operating system */ Display = FALSE, /* Display data as it is transmitted */ Echo = FALSE, /* Local Echo ON */ Warn = TRUE, /* File warning */ Port = PORTA, /* Serial Port to perform I/O */ Speed = B1200; /* Baud Rate */ unsigned char Rate[] ={4,1, /* 300 baud */ 0x082,0, /* 600 baud */ 0x041,0, /* 1200 baud */ 0x020,0, /* 2400 baud */ 0x010,0, /* 4800 baud */ 0x008,0 }, /* 9600 baud */ cr2[2] = {0x10, 0x10}, cr3[2] = {0xe1, 0xe1}, cr4[2] = {0x44, 0x44}, cr5[2] = {0xea, 0xea}; FILE *Fptr; /* File pointer */ unsigned long int Nch, /* Number of characters transmitted */ Nmess; /* Number of messages(Packetes) transmitted */ jmp_buf escape; /* Define environment of Longjump */ int Msem; /* Message semaphore */ /* */ /************************************************************************ * * * MESSAGES * * * *************************************************************************/ char *Pa[] = { "Packet Size", "Padding Character", "Number of Padding characters", "Time out (seconds)", "End of Line character", "Quote Character", "File Warning", "Local Echo", "Baud Rate", "Break Character", "Port"}; char *Ba[] = { "300 Baud ", "600 Baud ", "1200 Baud", "2400 Baud", "4800 Baud", "9600 Baud"}; char Startms[] ="\033w\033H\033E\033i1"; char Endms[] ="\033H\033z"; char Sys_id[] ="KERMIT VICTOR 9000 "; char Root_m[] ="\033p1 EXIT \033q \033p2 SEND \033q\ \033p3 RECEIVE\033q \033p4 SET \033q \033p5 SHOW \033q\ \033p6 TRACE \033q \033p7 HELP \033q \033p8 EXEC \033q"; char Set_m[] ="\033p1 BAUD \033q \033p2 ECHO \033q\ \033p3 WARNING\033q \033p4 PARAMS.\033q \033p5 \033q\ \033p6 RETURN \033q \033p7 HELP \033q"; char Onoff_m[] ="\033p1 YES \033q \033p2 NO \033q\ \033p \033q \033p \033q \033p \033q\ \033p6 RETURN \033q \033p7 HELP \033q"; char Baud_m[] ="\033p1 300 \033q \033p2 600 \033q\ \033p3 1200 \033q \033p4 2400 \033q \033p5 4800 \033q\ \033p6 9600 \033q \033p7 HELP \033q"; char Show_m[] ="\033p Hit to continue\033q"; char Help_m[] ="\033p Hit to continue\033q"; char Trace_m[] ="\033p1 PAGE \033q \033p2 NEXT \033q\ \033p3 PREV. \033q \033p4 \033q \033p5 \033q\ \033p6 RETURN \033q \033p7 HELP \033q"; char Parm_m[] =" "; char Getcp_m[] ="\033p1 ENTER \033q \033p2NUMERIC \033q\ \033p3 \033q \033p4 \033q \033p5 \033q\ \033p6 \033q \033p7 HELP \033q"; char Getnp_m[] ="\033p Hit to enter \033q"; /* */ /************************************************************************ * * * MAIN LINE OF KERMIT * * * *************************************************************************/ main(){ int i,j; putstr(Startms); for (i=80,j=strlen(Sys_id)+81; i > 30; --i, --j){ plotstr(5,i,Sys_id); clear_eol(5,j); } portopen(PORTA,Speed); Msdos20 = (dosid() >= 2)?TRUE: FALSE; termem(); putstr(Endms); } portopen(port,baud) int port; { Port = port; portinit(port); sbaud(baud,port); if (sstatus(DCD,port)) putstr("\n\n\nConnection to host established\n"); else{ putchar(BEL); putstr("\n\n\nConnection to host NOT established\n"); } } /* */ /************************************************************************ * * * THE PROTOCOL * * * *************************************************************************/ /************************************************************************ * * * Module: send * * Purpose: To set up environment to perform a file send * * Interface: send() * * Parameters: None * * Globals: Filnam (char[]): File name(s) to send * * * *************************************************************************/ send() { while (TRUE){ Nmess = 0; Nch = 0; putmenu("Name of file to Send: "); Size = getln(Filnam); minit("SEND FILE",Filnam); if (sendsw() == TRUE){ clrkb(); putmenu("File transfer complete Do you wish to Send more files (y/n): "); switch (getnec()){ case 'N': case 'n': return; } }else{ /* An error occurred */ clrkb(); putmenu("ERROR DURING FILE TRANSFER (A)bort or (C)ontinue"); switch(getnec()){ case 'A': case 'a': return; } } } } /************************************************************************ * * * Module: sendsw * * Purpose: To maintain the file sending sub-automoton * * Interface: sendsw() * * Parameters: None * * Globals: State (char): State of machine * * Numtry (int): Number of retries * * * *************************************************************************/ sendsw() { char sinit(), sfile(), sdata(), seof(), sbreak(); State = 'S'; /* Initialise to start state */ N = 0; /* Initialise message number */ Numtry = 0; /* Initialise Number of trys */ setjmp (escape); /* Return here if time out */ while (TRUE){ switch (State){ case 'D': State = sdata(); break; case 'F': State = sfile(); break; case 'Z': State = seof(); break; case 'S': State = sinit(); break; case 'B': State = sbreak(); break; case 'C': return(TRUE); case 'A': return(FALSE); default: return(FALSE); } } } /************************************************************************ * * * Module: sinit() * * Purpose: To initialise connection with the host. * * Interface: sinit() * * Parameters: * * Input: None * * Output: Through function name (char) * * Globals: * * Eol (char): End of line character for host * * Quote (char): Quote character for host * * Numtry (int): Number of attempts/retries to contact * * the host. * * Packet (char[]): Packet to be sent * * Recpkt (char[]): Packet received from host * * N (int): Message number * * Fptr (->FILE): Pointer to file * * * *************************************************************************/ char sinit() { int num, len; FILE *fopen(); char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries exceeded - INIT")); spar(Packet); /* Setup packet for host */ spack('S',N,6,Packet); /* Send it to the host */ switch (rpack(&len,&num,Recpkt)){ case 'N': if (N != num--) return(State); case 'Y': if (N != num) return(State); rpar(Recpkt); /* Get the initialisation info */ if (Eol == 0) Eol = '\n'; if (Quote == 0) Quote = '#'; Numtry = 0; Nch = 0; Nmess = 0; N = (N + 1) % 64; return( ((Fptr=fopen(Filnam,"rb")) == 0)? abort("Unable to open file"): 'F' /* Ok- File opened */ ); case FALSE: return(State); case 'E': return (abort(Recpkt)); default: return(abort("Unexpected response - INIT")); } } /************************************************************************ * * * Module: sfile * * Purpose: To send file name to host * * Interface: sfile() * * Parameters: * * Input: None * * Output: Through function name (char): * * Globals: * * Numtry (int): number of trys * * Filnam (char[]): File name to send * * N (int): Message number * * Recpkt (char[]): Received packet * * Packet (char[]): Packet to send * * Size (int): Size of data sent from host * * * *************************************************************************/ char sfile() { int num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - SEND FILE")); for (len=0; Filnam[len] != '\0'; len++); /* get length of name */ len++; spack('F',N,len,Filnam); switch (rpack(&len,&num,Recpkt)){ case 'N': if (N != num--) return(State); case 'Y': if (N != num) return(State); Numtry = 0; N = (N + 1) % 64; Size = bufill(Packet); return('D'); case FALSE: return(State); case 'E': return(abort(Recpkt)); default: return(abort("Unexpected response - SEND FILE")); } } /************************************************************************ * * * Module: sdata * * Purpose: To send data to the host (generally file contents) * * Interface: sdata() * * Parameters: * * Input: None * * Output: Through function name (char): * * Globals: * * Numtry (int): Number of tries * * Packet (char[]): packet data sent to host * * Recpkt (char[]): Packet sent from host * * N (int): Message number * * Size (int): Size of packet (amount of data) received * * * *************************************************************************/ char sdata() { int num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - SEND DATA")); spack('D',N,Size,Packet); switch(rpack(&len,&num,Recpkt)){ case 'N': if (N != num--) return(State); case 'Y': if (N != num) return(State); Nch += Size; Numtry = 0; N = (N + 1) % 64; return (((Size = bufill(Packet)) == EOF)? 'Z': 'D' ); case FALSE: return(State); case 'E': return(abort(Recpkt)); default: return(abort("Unexpected response - SEND DATA")); } } /************************************************************************ * * * Module: seof * * Purpose: To send an EOF Packet * * Interface: seof() * * Parameters: * * Input: None * * Ouptut: Through function name (char) * * Globals: * * Numtry (int): Number of tries * * Packet (char[]): Packet * * * *************************************************************************/ char seof() { int num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - SEND EOF")); spack('Z',N,0,Packet); switch (rpack(&len,&num,Recpkt)){ case 'N': if (N != num--) return(State); case 'Y': if (N != num) return(State); Numtry = 0; N = (N + 1) % 64; return ((gnxtfl() == EOF)?'B':'F'); case FALSE: return(State); case 'E': return(abort(Recpkt)); default: return(abort("Unexpected response - SEND EOF")); } } /************************************************************************ * * * Module: sbreak * * Purpose: To send a break (end of transmission) package * * Interface: sbreak() * * * *************************************************************************/ char sbreak() { int num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - SEND EOT")); spack('B',N,0,Packet); switch(rpack(&len,&num,Recpkt)){ case 'N': if (N != num--) return(State); case 'Y': if (N != num) return(State); Numtry = 0; N = (N + 1) % 64; return('C'); case FALSE: return(State); case 'E': return(abort(Recpkt)); default: return(abort("Unexpected response - SEND EOT")); } } /************************************************************************ * * * Module: spack * * Purpose: To assemble and send out the supplied Package * * Interface: spack(type,num,len,data) * * Input: type (char): Type of package * * num (int): Message number * * len (int): Size (length) of massage data * * data (char[]): Data to be packaged * * Output: None * * * ************************************************************************/ spack(type,num,len,data) char type,data[]; int num,len; { int i, chksum; char abort(); if (getkbs() && (getnec() == REQCAN)){ State = abort("Message Break"); longjmp(escape); } plotc(9,66,'S'); prpack(SEND,len,num,type,data); for (i=1; i <= Pad; i++) sputc(Padchar,Port); sputc(SOH,Port); chksum = tochar(len+3); sputc(tochar(len+3),Port); chksum += tochar(num); sputc(tochar(num),Port); chksum += type; sputc(type,Port); for (i=0; i < len; i++){ sputc(data[i],Port); chksum += data[i] & 0x0ff; /* Prevent Sign extension */ } chksum = (chksum + ((chksum & 192) /64)) & 63; sputc(tochar(chksum),Port); sputc(Eol,Port); Trace.smtype[Trace.si] = type; Trace.smnum[Trace.si] = num; if (++Trace.si > 255) Trace.si = 0; Nmess++; plotc(9,66,'W'); } /************************************************************************ * * * Module: rpack * * Purpose: To wait for the receipt of a package * * Interface: rpack(len,num,data) * * Input: len (int): Length of message (data portion) * * num (int): Message Number * * data (char[]): Vector which maintaines data received * * * *************************************************************************/ rpack(len,num,data) int *len, *num; char *data; { static int semaphore; int i, fld, chksum, t, type; char abort(); chksum = 0; t = 0; plotc(9,66,'R'); while ((t=sgetc(Port)) != SOH); for (fld=1; fld <= 5; fld++){ if (fld != 5 || i != 0) if ((t=sgetc(Port)) == SOH) fld = 0; if (fld <= 3) chksum += t; switch (fld){ case 0: chksum = 0; break; case 1: *len = unchar(t)-3; break; case 2: *num = unchar(t); break; case 3: type = t; break; case 4: for (i=0; i < *len; i++){ if (i != 0) if ((t=sgetc(Port)) == SOH){ fld = -1; break; } chksum += t; data[i] = t; } break; case 5: chksum = (chksum + ((chksum & 192) / 64)) & 63; break; } } if (getkbs() && (getnec() == REQCAN)){ State = abort("Message Break"); longjmp(escape); } Trace.rmtype[Trace.ri] = type; Trace.rmnum[Trace.ri] = *num; if (++Trace.ri > 255) Trace.ri = 0; Nmess++; prpack(RECEIVE,*len,*num,type,data,chksum); plotc(9,66,'W'); if (chksum != unchar(t)){ clear_eol(5,1); putchar(BEL); plotistr(5,5,"Bad Checksum"); semaphore = TRUE; return(FALSE); } if (semaphore){ semaphore = FALSE; clear_eol(5,1); } return(type); } /************************************************************************ * * * Module: receive * * Purpose: To set up environment to receive a file * * Interface: receive() * * Parameters: None * * Globals: Filnam (char[]): Name of file to receive * * * *************************************************************************/ receive() { while (TRUE){ Nmess = 0; Nch = 0; putmenu(" Name of file to Receive: "); Size = getln(Filnam); minit("RECEIVE FILE",Filnam); if (recsw() == TRUE){ clrkb(); putmenu(" File transfer complete Do you wish to Receive more files (y/n): "); switch (getnec()){ case 'N': case 'n': return; } }else{ /* An error occurred */ clrkb(); putmenu("ERROR DURING FILE TRANSFER (A)bort or (C)ontinue"); switch(getnec()){ case 'A': case 'a': return; } } } } /************************************************************************ * * * Module: recsw * * Purpose: To maintain the receive file automoton * * Interface: recsw() * * * *************************************************************************/ recsw() { char rinit(), rfile(), rdata(); State = 'R'; N = 0; Numtry = 0; if (setjmp (escape)) /* Return here on SIO time out */ spack('N',N,0,0); /* Send NAK */ while (TRUE){ switch(State){ case 'D': State = rdata(); break; case 'F': State = rfile(); break; case 'R': State = rinit(); break; case 'C': return(TRUE); case 'A': return(FALSE); default: return(FALSE); } } } /************************************************************************ * * * Module: rinit * * Purpose: To initialise the conection to the host * * Interface: rinit() * * * *************************************************************************/ char rinit() { int len, num; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - RECEIVE INIT")); switch (rpack(&len,&num,Packet) ){ case 'S': N = num; rpar(Packet); spar(Packet); spack('Y',N,6,Packet); Oldtry = Numtry; Numtry = 0; Nch = 0; Nmess = 0; N = (N + 1) % 64; return('F'); case 'N': spack('Y',num,0,0); return(State); case FALSE: spack('N',num,0,0); return(State); case 'E': return(abort(Packet)); default: return(abort("Unexpected response - RECEIVE INIT")); } } /************************************************************************ * * * Module: rfile * * Purpose; To receive a file from the host * * Interface: rfile() * * * *************************************************************************/ char rfile() { int num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - RECEIVE FILE")); switch ( rpack(&len,&num,Packet) ){ case 'S': if (Oldtry++ > MAXTRY) return(abort("Maximum number of attempts - RECEIVE FILE")); if (num == N-1){ spar(Packet); spack('Y',num,6,Packet); Numtry = 0; return(State); }else return(abort("Message number mismatch - RECEIVE FILE")); case 'Z': if (Oldtry++ > MAXTRY) return(abort("Maximum number of attempts - RECEIVE FILE")); if (num == N-1){ spack('Y',num,0,0); Numtry = 0; return(State); }else return(abort("Message number mismatch - RECEIVE FILE")); case 'F': if (num != N) return(abort("Message number mismatch - RECEIVE FILE")); if (getfil(Filnam) == FALSE) return(abort("Unable to open file")); spack('Y',N,0,0); Oldtry = Numtry; Numtry = 0; N = (N + 1) % 64; return('D'); case 'B': if (num != N) return(abort("Message number mismatch - RECEIVE FILE")); spack('Y',N,0,0); return('C'); case 'N': spack('Y',num,0,0); return(State); case FALSE: spack('N',num,0,0); return(State); case 'E': return(abort(Packet)); default: return(abort("Unexpected response - RECEIVE FILE")); } } /************************************************************************ * * * Module: rdata * * Purpose; To receive data from the host * * Interface: rdata() * * * *************************************************************************/ char rdata() { int i, num, len; char abort(); if (Numtry++ > MAXTRY) return(abort("Maximum number of retries - RECEIVE DATA")); switch ( rpack(&len,&num,Packet) ){ case 'D': if (num != N){ if (Oldtry++ > MAXTRY) return(abort("Maximum number of attempts - RECEIVE DATA")); if (num == N-1){ spack('Y',num,0,0); Numtry = 0; return(State); }else return(abort("Message number mismatch - RECEIVE DATA")); } Nch += len; bufemp(Packet,Fptr,len); spack('Y',N,0,0); Oldtry = Numtry; Numtry = 0; N = (N + 1) % 64; return('D'); case 'Z': if (num != N) return(abort("Message number mismatch - RECEIVE DATA")); i = (fclose(Fptr) == -1)? abort("Unable to close file"): 'F'; spack('Y',num,0,0); N = (N + 1) % 64; return(i); case 'F': if (Oldtry++ > MAXTRY) return(abort("Maximum number of attempts - RECEIVE DATA")); if (num != N-1){ spack('Y',N,0,0); Numtry = 0; return(State); }else return(abort("Message number mismatch - RECEIVE DATA")); case 'B': if (num != N) return(abort("Message number mismatch - RECEIVE DATA")); spack('Y',N,0,0); return('C'); case 'N': spack('Y',num,0,0); return(State); case FALSE: spack('N',num,0,0); return(State); case 'E': return(abort(Packet)); default: return(abort("Unexpected response - RECEIVE DATA")); } } /* */ /************************************************************************ * * * TERMINAL EMULATION ROUTINES * * * *************************************************************************/ /************************************************************************ * * * Module: termem * * Purpose: To emulate a terminal * * Interface: termem() * * * *************************************************************************/ termem() { unsigned char ch; putmenu(Root_m); setjmp (escape); while (TRUE){ if (getkbs()){ /* Check for character @ keyboard */ if (((ch=getnec()) >= F1) && (ch <= F8)){ clear_screen(); /* cursr_off();*/ switch (ch){ case F1: return; case F2: send(); break; case F3: receive(); break; case F4: set(); break; case F5: show(); getnec(); break; case F6: dtrace(); break; case F7: help(ROOT_H); break; case F8: putchar('!'); docmd(); } clear_screen(); putmenu(Root_m); home(); /* cursr_on();*/ }else{ sputc(((ch == REQCAN)?Break:ch),Port); if (Echo) putchar(ch); } }else if ((ch=scheck(Port)) > 0){ putchar(ch&0x07f); /* Else echo chararacter from Port */ } } } /************************************************************************ * * * Module: docmd() * * Purpose: To execute a MS-DOS command * * Input: None * * Output: None * * * *************************************************************************/ docmd() { struct regval {int ax,bx,cx,dx,si,di,ds,es;} srv,rrv; char line[256]; if (!Msdos20){ putstr("Requires MSDOS 2.0 or greater ... request ignored\n"); }else putstr("Feature not implemented at this time\n"); putmenu(Help_m); getnec(); } /************************************************************************ * * * Module: dtrace() * * Purpose: To display trace information collected during data * * transmition * * Input: Trace: Trace buffer (Global data) * * Output: None * * * *************************************************************************/ dtrace() { int j, d, k, i; printf("\033Y!7\0330Local\0331\033Y!L\0330Host\0331\033Y7 "); putmenu(Trace_m); for (i=Trace.si-1, j=(i+1) & 0x0ff; Trace.smtype[i] != 'S' && Trace.rmtype[i] != 'S' && i != j; \ i = (i - 1) & 0x0ff); if (i == j){ printf("\n No transactions in Trace Buffer"); }else{ Trace.smtype[j] = 0; /* Mark top of list */ d = (Trace.smtype[i] == 'S')?SEND: RECEIVE; j = 10; i = (Trace.si - 1) & 0x0ff; while (TRUE){ if (d == SEND){ printf("\033Y# \033M\033Y7D<------- %c(%d)",\ Trace.rmtype[i],Trace.rmnum[i]); printf("\033Y# \033M\033Y77%c(%d) ------->",\ Trace.smtype[i],Trace.smnum[i]); }else{ printf("\033Y# \033M\033Y77%c(%d) ------->",\ Trace.smtype[i],Trace.smnum[i]); printf("\033Y# \033M\033Y7D<------- %c(%d)",\ Trace.rmtype[i],Trace.rmnum[i]); } if ((Trace.smtype[i] == 'S') || (Trace.rmtype[i] == 'S')) break; i = (i - 1) & 0x0ff; while (--j < 1){ switch (getnec()){ case F1: j = 10; break; case F2: break; case F3: printf("\033j"); k = (i + 11) & 0x0ff; if (d == SEND){ printf("\033Y# \033L\033Y#7%c(%d) ------->",\ Trace.smtype[k],Trace.smnum[k]); printf("\033Y# \033L\033Y#D<------- %c(%d)",\ Trace.rmtype[k],Trace.rmnum[k]); }else{ printf("\033Y# \033L\033Y#D<------- %c(%d)",\ Trace.rmtype[k],Trace.rmnum[k]); printf("\033Y# \033L\033Y#7%c(%d) ------->",\ Trace.smtype[k],Trace.smnum[k]); } printf("\033k"); if (Trace.smtype[i] != 0) i = (i + 1) & 0x0ff; continue; case F6: return; case F7: help(TRACE_H); break; default: putchar(BEL); continue; } break; } } } getnec(); } prtrace(d,i,l) int d,i,l; { } /************************************************************************ * * * Module: set * * Purpose: To set or reset system variable * * Interface: set() * * * *************************************************************************/ set() { show(); while (TRUE){ putmenu(Set_m); switch (getnec()){ case F1: set_baud(); break; case F2: set_echo(); break; case F3: set_warning(); break; case F4: set_parms(); break; case F6: return; case F7: help(SET_H); show(); break; default: putchar(BEL); } } } set_baud() { plotistr(POS_BAUD,4,Pa[8]); plotistr(POS_BAUD,50,Ba[Speed]); while (TRUE){ putmenu(Baud_m); switch (getnec()){ case F1: Speed = B300; break; case F2: Speed = B600; break; case F3: Speed = B1200; break; case F4: Speed = B2400; break; case F5: Speed = B4800; break; case F6: Speed = B9600; break; case F7: help(BAUD_H); show(); continue; default: putchar(BEL); continue; } break; } sbaud(Speed,Port); clear_eol(POS_BAUD,1); plotstr(POS_BAUD,4,Pa[8]); plotstr(POS_BAUD,50,Ba[Speed]); } set_echo() { plotistr(POS_ECHO,4,Pa[7]); plotistr(POS_ECHO,50,(Echo)?"YES":"NO "); while (TRUE){ putmenu(Onoff_m); switch (getnec()){ case F1: Echo = TRUE; break; case F2: Echo = FALSE; break; case F6: break; case F7: help(ECHO_H); show(); continue; default: putchar(BEL); continue; } break; } clear_eol(POS_ECHO,1); plotstr(POS_ECHO,4,Pa[7]); plotstr(POS_ECHO,50,(Echo)?"YES":"NO "); } set_warning() { plotistr(POS_WARN,4,Pa[6]); plotistr(POS_WARN,50,(Warn)?"YES":"NO "); while (TRUE){ putmenu(Onoff_m); switch (getnec()){ case F1: Warn = TRUE; break; case F2: Warn = FALSE; break; case F6: break; case F7: help(WARN_H); show(); continue; default: putchar(BEL); continue; } break; } clear_eol(POS_WARN,1); plotstr(POS_WARN,4,Pa[6]); plotstr(POS_WARN,50,(Warn)?"YES":"NO "); } set_parms() { /* cursr_on();*/ putmenu(Parm_m); pos(POS_MAXPACK,50); Maxpack = getnp(Maxpack,MAXPACK); putmenu(Parm_m); pos(POS_MYPCHAR,50); Mypchar = getcp(Mypchar); putmenu(Parm_m); pos(POS_MYPAD,50); Mypad = getnp(Mypad,NULL); putmenu(Parm_m); pos(POS_MYTIME,50); Mytime = getnp(Mytime,NULL); putmenu(Parm_m); pos(POS_MYEOL,50); Myeol = getcp(Myeol); putmenu(Parm_m); pos(POS_MYQUOTE,50); Myquote = getcp(Myquote); pos(POS_BREAK,50); Break = getcp(Break); /* cursr_off();*/ } show() { int i; clear_screen(); putmenu(Show_m); for (i=0; i < 11;i++){ plotistr(SHOW_BASE+i,4,Pa[i]); plotstr(SHOW_BASE+i,4,Pa[i]); } plotd(POS_MAXPACK,50,Maxpack); plotstr(POS_MYPCHAR,50,(Mypchar < ' ')?"^":" "); plotc(POS_MYPCHAR,51,(Mypchar < ' ')?Mypchar+'@':Mypchar); plotd(POS_MYPAD,50,Mypad); plotd(POS_MYTIME,50,Mytime); plotstr(POS_MYEOL,50,(Myeol < ' ')?"^":" "); plotc(POS_MYEOL,51,(Myeol < ' ')?Myeol+'@':Myeol); plotstr(POS_MYQUOTE,50,(Myquote < ' ')?"^":" "); plotc(POS_MYQUOTE,51,(Myquote < ' ')?Myquote+'@':Myquote); plotstr(POS_WARN,50,(Warn)?"YES":"NO "); plotstr(POS_ECHO,50,(Echo)?"YES":"NO "); plotstr(POS_BAUD,50,Ba[Speed]); plotstr(POS_BREAK,50,(Break < ' ')?"^":" "); plotc(POS_BREAK,51,(Break < ' ')?Break+'@':Break); plotstr(POS_PORT,50,(Port == PORTA)?"PORT A":"PORT B "); } /************************************************************************ * * * Module: help * * Purpose: To print out help messages * * Interface: help(message) * * Input: message (->char): Help message to be printed * * Output: None * * * *************************************************************************/ help(message) int message; { FILE *fp; int i; int ch; clear_screen(); putmenu(Help_m); if ((fp=fopen("KERMIT.HEP","r")) != 0){ i = 0; while (i != message){ while (getc(fp) != ENDSEC); ++i; } while (TRUE){ switch (ch=getc(fp)){ case ENDPAG: getnec(); clear_screen(); continue; case ENDSEC: case ENDSS: case ENDFIL: case EOF: break; case ENQUIRE: i = getnp(1,NULL); putmenu(Help_m); while (i > 0){ switch (ch=getc(fp)){ case ENDSEC: case ENDFIL: case EOF: ungetc(ch,fp); break; case ENDSS: --i; break; } } clear_screen(); continue; case '\n': if ((ch=getc(fp)) == ';'){ while (getc(fp) != '\n'); ungetc('\n',fp); }else{ putchar('\n'); ungetc(ch,fp); } continue; default: putchar(ch); continue; } break; } fclose(fp); }else{ putstr("Help file not found"); } getnec(); } /* */ /************************************************************************ * * * UTILITES * * * *************************************************************************/ /************************************************************************ * * * Module: abort * * Purpose: To print out the abort message, return an Abort * * code, and send an Error packet to the host. * * Interface: abort(message) * * Input: message (->char): Message to be printed * * Output: Through function name (char): * * 'A': Abort code * * * *************************************************************************/ char abort(message) char *message; { clear_eol(5,1); plotistr(5,(38-(strlen(message)/2)),message); spack('E',N,strlen(message),message); return('A'); } /************************************************************************ * * * Module: basenm * * Purpose: To strip off any device specifiers and/or path names * * supplied with the file name. * * Interface: basenm(fname) * * Input: fname (->char): Name of file * * Output: Through function name (->char): * * pointer to position of "base name" * * * *************************************************************************/ char *basenm(fname) char fname[]; { int i,j,k; for (j=strlen( &fname[ (fname[1] == ':')? 2: 0 ] ), k = j; (j >= 0) && (fname[j] != '/') ; --j); for (i=j; i < k; i++){ if (fname[i] == '.'){ if (i+3 < k){ fname[i+4] = '\0'; break; } } } return(&fname[j+1]); } /************************************************************************ * * * Module: bufemp * * Purpose: To write data received to the appropriate file * * Interface: bufemp(buffer,filptr,len) * * * *************************************************************************/ bufemp(buffer,filptr,len) char buffer[]; FILE *filptr; int len; { int i, t; for (i=0; i < len; i++){ if ((t=buffer[i]) == Myquote){ if ((t=buffer[++i]) != Myquote) t = ctl(t); } fputc(t,filptr); } } /************************************************************************ * * * Module: bufill * * Purpose: To get data from a file * * Interface: bufill(buffer) * * * *************************************************************************/ bufill(buffer) char buffer[]; { int i, t; i = 0; while ((t = fgetc(Fptr)) != EOF){ t &= 0x07f; if (t < SP || t == DEL || t == Quote){ buffer[i++] = Quote; if (t != Quote) t = ctl(t); } buffer[i++] = t; if (i >= Spsiz-6) return(i); } if (i == 0) return(EOF); return(i); } /************************************************************************ * * * Module: getcp * * Purpose: To get a character from the keyboard * * Interface: getcp(d) * * Input: d (integer): Default value, if nothing entered * * Output: Through function name: * * Keyed, or default value * * * *************************************************************************/ getcp(d) int d; { int c,j; c = -1; /* cursr_on();*/ putmenu(Getcp_m); while(TRUE){ switch(j=getnec()){ case F1:/* cursr_off();*/ return((c == -1)?d:c); case F2: return(getnp(0,128)); case F7: help(GETCP_H); show(); putmenu(Getcp_m); break; default: if (j < ' ') printf("^%c",j+'@'); else putchar(j); c = j; } } } /************************************************************************ * * * Module: getfil * * Purpose: To open a file for data received from host * * Interface: getfil(filenm) * * * *************************************************************************/ getfil(filenm) char *filenm; { FILE *fopen(); char *fln, *basenm(); if (*filenm == '\0'){ /* Default to name sent by host */ fln = basenm(Packet); pos(5,(38-(14+strlen(fln))/2)); putistr("RECEIVE FILE: "); putistr(fln); }else fln = filenm; if (fopen(fln,"r") != 0){ /* File already exists */ while (Warn){ putmenu("File already exists. Do you want to (C)ontinue, (A)bort, or (R)ename: "); switch (getnec()){ case 'A': case 'a': return(FALSE); case 'C': case 'c': break; case 'R': case 'r': clear_eol(5,1); plotstr(5,1,"Enter new filename: "); getln(filenm); return(getfil(filenm)); default: putchar(BEL); plotistr(5,1,"Invalid entry "); continue; } break; } } return ( ((Fptr=fopen(fln,"wb")) == 0)? FALSE: TRUE); } /************************************************************************ * * * Module: getlc * * Purpose: To get the current Line/Column position of the cursor * * Interface: getlc(line,column) * * Input: line (->char): Pointer to integer variable * * column (->char): Pointer to integer variable * * Output: Line (->char): Current line position (0 releative) * * column (->char): Current column position (0 rel) * * * *************************************************************************/ getlc(line,column) char *line,*column; { printf("\033n"); /*send out control code to CRT */ getnec(); /* By pass garbage ... \033Y */ getnec(); *line = getnec(); /* Get information we want */ *column = getnec(); } /************************************************************************ * * * Module: getln * * Purpose: To get a line from standard input * * Interface: getln(line) * * Input: line (->char[]): Pointer to vector which receives * * keyed data * * Output: Through function name (integer): * * Number of characters entered * * * *************************************************************************/ getln(line) char line[]; { int i; for (i=0; (line[i]=getchar()) != '\n';++i); line[i] = '\0'; return(i); } /************************************************************************ * * * Module: getnp * * Purpose: To get a number (decimal) from the keyboard * * Interface: getnp(default,max) * * Input: d (integer): Default value, if nothing entered * * max (integer): Maximun value. If NULL, then no maximum * * Output: Through function name: * * Keyed, or default value * * * *************************************************************************/ getnp(d,max) int d,max; { int i, j, k; /* cursr_on();*/ i = 0; k = 0; while(TRUE){ putmenu(Getnp_m); while ((j=getnec()) != CR){ if (j >= '0' && j <= '9'){ k++; putchar(j); i = (i * 10) + (j - '0'); }else if (j == BS){ i /= 10; printf("%c %c",BS,BS); }else if (j == '\030'){ /* Handle Control X */ while (k-- > 0) printf("%c %c",BS,BS); i = 0; k = 0; }else putchar(BEL); } if (k == 0){ i = d; break; } if ((max == 0) || (k <= max)) break; putchar(BEL); putmenu("Entry too large"); } /* cursr_off();*/ return(i); } /************************************************************************ * * * Module: gnxtfl * * Purpose: To get the next file in a group, if there is one * * Interface: gnxtfl() * * * *************************************************************************/ gnxtfl() { return(EOF); } /************************************************************************ * * * Module: minit() * * Purpose: To initialise the message screen for data communications* * Input: message: Message type (Sending/receiving) * * name: File name involved * * Output: None * * * *************************************************************************/ minit(message,name) char *message,*name; { int x,y; clear_screen(); putmenu(Startms); pos(5,(38-(strlen(message)+strlen(name))/2)); putistr("RECEIVE FILE: "); putistr(name); plotstr(7,30,"Transmision Statistics"); plotustr(8,1, " Send Receive Accumulated Data "); plotstr(9,1,"Message Type:"); plotstr(10,1,"Message Number:"); plotstr(11,1,"Data Length:"); plotstr(9,40,"Transmision State:"); plotstr(10,40,"Number of Messages:"); plotstr(11,40,"Number of Characters:"); Trace.si = 0; Trace.ri = 0; Msem = TRUE; } /************************************************************************ * * * Module: prpack * * Purpose: To print a packet * * Interface: prpack(len,num,type,data,chksum) * * Input: len (int): Length of data to be sent * * num (int): Message number * * type (char): Type of message * * data (char[]): Data to be sent * * chksum (int): Check sum being sent * * Output: None * * * *************************************************************************/ prpack(state,len,num,type,data) int state,len,num; char type,data[]; { int i,ch; static y; ch = (state == SEND)?18: 27; plotc(9,ch,type); plotd(10,ch,num); plotd(11,ch,len); plotU(10,66,Nmess); plotU(11,66,Nch); if (Display){ if (Msem == TRUE){ Msem = FALSE; y = 32; } for (i=0; i < len; i++){ if ((ch=data[i]) == Myquote){ if ((ch=data[++i]) != Myquote) if ((ch=ctl(ch&0x0ff)) == LF){ y = 32; plotstr(11,0,"\033M\033Y6 "); ch = CR; } } plotc(23,y++,ch); } } } /************************************************************************ * * * Module: rpar * * Purpose: To receive initialisation parameters from host * * Input: data: Parameters sent by host. * * Output: Spsiz, Timint, Pad, Padchar, Eol, Quote are set * * * *************************************************************************/ rpar(data) char data[]; { Spsiz = unchar(data[0]); Timint = unchar(data[1]); Pad = unchar(data[2]); Padchar = ctl(data[3]); Eol = unchar(data[4]); Quote = data[5]; } /************************************************************************ * * * Module: spar * * Purpose: To send initialisation parameters to the host * * Input: Spsiz, Timint, Pad, Padchar, Eol, Quote are set * * Output: data: Parameters to send to the host * * * *************************************************************************/ spar(data) char data[]; { data[0] = tochar(Maxpack); data[1] = tochar(Mytime); data[2] = tochar(Mypad); data[3] = ctl(Mypchar); data[4] = tochar(Myeol); data[5] = Myquote; } /* */ /************************************************************************ * * * SERIAL I/O UTILITIES * * * *************************************************************************/ /************************************************************************ * * * Module: sbaud * * Purpose: To set the baud rate * * Interface: sbaud(baud,port) * * Input: baud (int): Baud rate: 0 = 300 baud * * 1 = 600 baud * * 2 = 1200 Baud * * 3 = 2400 baud * * 4 = 4800 baud * * 5 = 9600 baud * * port (int): Serial Port:0 = Port A * * 1 = Port B * * Output: None * * * *************************************************************************/ sbaud(baud,port) int baud, port; { pokeb(3,0x0e002, ((port == PORTA)? 0x036: 0x076)); baud *= 2; pokeb(port,0x0e002,Rate[baud]); pokeb(port,0x0e002,Rate[baud+1]); } /************************************************************************ * * * Module: scheck * * Purpose: To check if a character is available at the port * * and return it if there is one * * Interface: scheck(port) * * Parameters: * * Input: port (int): Port id: 0 = Port A * * 1 = Port B * * Output: Through function name (int): * * Character found,or FALSE (i.e. 0) * * * *************************************************************************/ scheck(port) int port; { return( (sstatus(RX,port))? /* Check status */ (peek(port,0x0e004) & 0x0ff): /* Return character */ 0 ); } /************************************************************************ * * * Module: sgetc * * Purpose: To get a character from the serial port * * Interface: sgetc(port) * * Parameters: * * Input: port (int): Port id: 0 = Port A * * 1 = Port B * * Output: Through function name (char): * * Character read at port * * * *************************************************************************/ sgetc(port) int port; { unsigned timer, ch; while (TRUE){ for (timer=0;--timer > 0 && (sstatus(RX,port) == FALSE);); if (timer == 0){ if (getkbs()){ /* TRUE if character present */ if ((ch=getnec()) == CR){ longjmp (escape,RECEIVE); }else if (ch == DEL){ State = abort("Time out on Input"); longjmp (escape,RECEIVE); } }else putchar(BEL); }else return(peek(port,0x0e004) & 0x07f); } } /************************************************************************ * * * Module: sputc * * Purpose: To put a character out to the serial port * * Interface: sputc(ch,port) * * Parameters: * * Input: ch (char): Character to output * * port (int): Port id: 0 = Port A * * 1 = Port B * * Output: None * * * *************************************************************************/ sputc(ch,port) char ch; int port; { unsigned timer, c; while (TRUE){ for (timer=0;--timer > 0 && sstatus(TX,port) == FALSE;); if (timer == 0){ /* If timed out */ if (getkbs()){ if ((c=getnec()) == CR) longjmp (escape,SEND); else if (c == DEL){ State = abort("Time out on Output"); longjmp (escape,SEND); } }else putchar(BEL); }else{ pokeb(port,0x0e004,ch & 0x07f); break; } } } /************************************************************************ * * * Module: portinit * * Purpose: To initialise the specified port * * Interface: portinit(port) * * Input: port (int): Id of port to initialise: 0 = Port A * * 1 = Port B * * Output: None * * * *************************************************************************/ portinit(port) int port; { int control; control = port + 2; /* Move to "Control" addresses */ pokeb(control,0x0e004,0x018); /* Channel Reset */ pokeb(control,0x0e004,CR2); /* Address control register 2 */ pokeb(control,0x0e004,cr2[port]); pokeb(control,0x0e004,CR4); /* Address control register 4 */ pokeb(control,0x0e004,cr4[port]); pokeb(control,0x0e004,CR3); /* Address control register 3 */ pokeb(control,0x0e004,cr3[port]); pokeb(control,0x0e004,CR5); /* Address control register 5 */ pokeb(control,0x0e004,cr5[port]); pokeb(control,0x0e004,0x011); /* Disable interupts */ pokeb(control,0x0e004,0); }