home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff330.lzh / Vt100 / Src.lzh / Src / kermit.c < prev    next >
C/C++ Source or Header  |  1990-03-01  |  27KB  |  1,075 lines

  1. static char rcsid[] = "$RCSfile: kermit.c,v $ $Revision: 1.3 $";
  2.  
  3. /*************************************************************
  4.  * vt100 terminal emulator - KERMIT protocol support
  5.  *            :ts=8
  6.  *
  7.  * $Log:    kermit.c,v $
  8.  * Revision 1.3  89/11/02  10:28:28  acs
  9.  * doksend()/dokreceive(): Memory for spackbuf and msgpkt not freed if
  10.  * filename == "$".
  11.  * 
  12.  * Revision 1.2  89/10/25  21:19:54  acs
  13.  * 1) saybye(): allocate/free msgpkt and spackbuf if necessary.
  14.  * 2) Add RCS id and change log.
  15.  * 
  16.  *    v2.9 ACS - Kermit shouldn't NAK packet 0 but timeout waiting for
  17.  *           send-init then NAK if necessary; terminate each packet with
  18.   *           EOL only; don't sendstring("\r") but sendchar() it.
  19.  *    v2.8a 880230 ACS - saybye() won't do anything if not in kermit
  20.  *              mode.
  21.  *    v2.7 870825 ACS - Fixed the "multiple-send" problem in
  22.  *              doksend() et al; show status using the *InfoMsg*()
  23.  *              routines in window.c; fixed erroneous calls to
  24.  *              spack() and rpack(); better error handling.
  25.  *    v2.6 870227 DBW - bug fixes for all the stuff in v2.5
  26.  *    v2.5 870214 DBW - more additions (see readme file)
  27.  *    v2.4 861214 DBW - lots of fixes/additions (see readme file)
  28.  *    v2.3 861101 DBW - minor bug fixes
  29.  *    v2.2 861012 DBW - more of the same
  30.  *    v2.1 860915 DBW - new features (see README)
  31.  *         860901 ACS - Added eight bit quoting
  32.  *         860830 Steve Drew Wild card support, err recovry,bugs.
  33.  *         860823 DBW - Integrated and rewrote lots of code
  34.  *         860811 Steve Drew multi filexfer, bugs, status line ect..
  35.  *    v2.0 860809 DBW - Major rewrite
  36.  *    v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes
  37.  *    v1.0 860712 DBW - First version released
  38.  *
  39.  *************************************************************/
  40.  
  41. #include "vt100.h"
  42.  
  43. #define MAXPACKSIZ 94        /* Maximum msgpkt size */
  44. #define CR       13        /* ASCII Carriage Return */
  45. #define LF       10        /* ASCII line feed */
  46. #define SP       32        /* ASCII space */
  47. #define DEL      127        /* Delete (rubout) */
  48.  
  49. #define MAXTRY      5       /* Times to retry a msgpkt */
  50. #define MYQUOTE  '#'       /* Quote character I will use */
  51. #define MYRPTQ     '~'       /* Repeat quote character */
  52. #define MYEBQ     '&'       /* 8th bit prefix character */
  53. #define MYPAD       0       /* Number of padding charss I will need */
  54. #define MYPCHAR    0       /* Padding character I need (NULL) */
  55. #define MYEOL    '\n'    /* End-Of-Line character I need */
  56. #define IDOLONG    2    /* I do LONG packets! */
  57.  
  58. #define tochar(ch)  ((ch) + ' ')
  59. #define unchar(ch)  ((ch) - ' ')
  60. #define ctl(ch)     ((ch) ^ 64 )
  61.  
  62. /* Global Variables */
  63.  
  64. int sending,    /* Indicates that we're sending, not receiving */
  65.     lastpkt,    /* Last successful packet # sent/received */
  66.     ulp,        /* Using LONG packets */
  67.     size,    /* Size of present data */
  68.     osize,    /* Size of last data entry */
  69.     rpsiz,    /* Maximum receive msgpkt size */
  70.     spsiz,    /* Maximum send msgpkt size */
  71.     timint,    /* Time interval to wait */
  72.     pad,        /* How much padding to send */
  73.     n,        /* Packet number */
  74.     tp,        /* total packets */
  75.     numtry,    /* Times this msgpkt retried */
  76.     retry,    /* total retries */
  77.     oldtry,    /* Times previous msgpkt retried */
  78.     sendabort,    /* flag for aborting send file  */
  79.     rptflg,    /* are we doing repeat quoting */
  80.     ebqflg,    /* are we doing 8th bit quoting */
  81.     notfirst,    /* is this the first file received */
  82.     first,    /* is this the first time in a file */
  83.     rpt,        /* current repeat count */
  84.     next,    /* what is the next character */
  85.     t;        /* current character */
  86. long totbytes;    /* total bytes xfered on this file */
  87.  
  88. char state,    /* Present state of the automaton */
  89.     padchar,    /* Padding character to send */
  90.     eol,        /* End-Of-Line character to send */
  91.     quote,    /* Quote character in incoming data */
  92.     rptq,    /* Quote character for repeats */
  93.     ebq,        /* Quote character for 8th bit quoting */
  94.     ackpkt[MAXPACKSIZ+20],    /* ACK/NAK packet buffer */
  95.     *msgpkt = NULL,        /* Message Packet buffer is AllocMem()d */
  96.     *spackbuf = NULL,    /* Dynamically allocated buffer for spack() */
  97.     filnam[40],    /* remote file name */
  98.     snum[10],
  99.     mainmode[10];
  100.  
  101. FILE *fp;    /* file for send/receive */
  102.  
  103. static void spack(), print_our_err(), print_host_err(),
  104.         dostats(), ClearBuffer();
  105.  
  106. char *
  107. getfname(name)    /* returns ptr to start of file name from spec */
  108. char *name;
  109. {
  110.     int l;
  111.  
  112.     l = strlen(name);
  113.     while(l && name[l] != '/' && name[l] != ':') l--;
  114.     if (name[l] == '/' || name[l] == ':') l++;
  115.     return(name += l);
  116. }
  117.  
  118. doksend(file,more)
  119. char *file;
  120. int more;
  121. {
  122.     int amount, c, wild;
  123.     char *p, **list = NULL;
  124.  
  125.     sending = 1;
  126.     if (!strcmp(file,"$")) { saybye(); return(USERABORT); }
  127.     msgpkt    = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  128.     spackbuf  = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  129.     p = file;
  130.     while(*p && *p != '*' && *p != '?') p++;
  131.     if (*p) {
  132.     wild = TRUE;
  133.     list = expand(file, &amount);
  134.     if (list == NULL) InfoMsg1Line("KERMIT: No wild card match");
  135.     }
  136.     else {
  137.     wild = FALSE;
  138.     amount = 1;
  139.     }
  140.     /*      The "multiple send" problem is brought about by attempting to
  141.     ** send multiple files in a single "batch" (via globbing, e.g. *.foo)
  142.     ** to a remote kermit that is NOT in server mode.  A 'Z' packet
  143.     ** (meaning end-of-file) is sent after each of the files with a 'B'
  144.     ** packet (meaning end-of-batch) coming after the last 'Z' packet.
  145.     ** The old code reset the packet # on each iteration.  We do it
  146.     ** outside of the for loop. */
  147.     n = lastpkt = 0;
  148.     ulp = 0;    /* Assume we won't use LONG packets */
  149.     for (c = 0; c < amount; c++) {
  150.     if (wild == TRUE) p = list[c];
  151.         else  p = file;
  152.     strcpy(filnam,getfname(p));
  153.     ttime = TTIME_KERMIT;
  154.     tp = retry = numtry = 0; totbytes = 0L;
  155.     if ((fp = fopen(p,"r")) == NULL) {
  156.         InfoMsg2Line("KERMIT: Can't open send file:", p);
  157.         continue;
  158.     }
  159.     strcpy(mainmode,"SEND");
  160.     ClearBuffer();
  161.  
  162.     /*  This is another piece of the multiple-send fix.  Sendsw() needs
  163.     ** to know 1) that this is the first file so it can send a send-init
  164.     ** packet and 2) if this is the last file so it can send a B packet
  165.     ** to indicate end-of-batch.  The last piece of the multiple-send fix
  166.     ** is in sendsw() itself. */
  167.     if ( sendsw(c == 0, c >= (amount-1)) ) /* Successful send? */
  168.         ScrollInfoMsg(1);
  169.     fclose(fp);
  170.     }
  171.     free_expand(list);
  172.     FreeMem(spackbuf,    (long)(MAXLONGPKS+20));
  173.     FreeMem(msgpkt,    (long)(MAXLONGPKS+20));
  174.     msgpkt = spackbuf = NULL;
  175.     return(TRUE);
  176. }
  177.  
  178. dokreceive(file,more)
  179. char *file;
  180. int more;
  181. {
  182.     int retval;
  183.  
  184.     ttime = TTIME_KERMIT;
  185.     sending = 0;
  186.     if (!strcmp(file,"$")) { saybye(); return(USERABORT); }
  187.     msgpkt    = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  188.     spackbuf  = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  189.     strcpy(filnam, file);
  190.     if (server) strcpy(mainmode,"GET");
  191.     else        strcpy(mainmode,"RECV");
  192.     tp =  lastpkt = retry = n =  numtry = notfirst = 0; totbytes = 0L;
  193.     ClearBuffer();
  194.     retval  = recsw();
  195.     FreeMem(spackbuf,    (long)(MAXLONGPKS+20));
  196.     FreeMem(msgpkt,    (long)(MAXLONGPKS+20));
  197.     msgpkt = spackbuf = NULL;
  198.     return(retval);
  199. }
  200.  
  201. sendsw(firstfile, lastfile)
  202. int firstfile, lastfile; /* Multiple-send fix */
  203. {
  204.     char sinit(), sfile(), sdata(), seof(), sbreak();
  205.     sendabort = 0;
  206.     /* Multiple-send fix.  If this is the first file of the batch then enter
  207.     ** send-init state otherwise just enter send-file state. */
  208.     if(firstfile)
  209.     state = 'S';
  210.     else
  211.     state = 'F';
  212.     while(TRUE) {
  213.     switch(state) {
  214.     case 'S':   state = sinit();  break;
  215.     case 'F':   state = sfile();  break;
  216.     case 'D':   state = sdata();  break;
  217.     case 'Z':   state = seof();   break;
  218.     case 'B':   if (lastfile || sendabort) {
  219.             /* Multiple-send fix.  If this is the last file then
  220.             ** send a B packet to indicate end-of-batch. */
  221.             state = sbreak();
  222.             break;
  223.              }
  224.              return(TRUE);    /* Otherwise, just return. */
  225.     case 'C':   if (sendabort) return(FALSE);
  226.             else return(TRUE);
  227.     case 'E':   dostats('E',"ERROR");  /* so print the err and abort */
  228.             print_host_err(ackpkt);
  229.             return(FALSE);
  230.     case 'A':   if (timeout == USERABORT) {
  231.             timeout = GOODREAD;
  232.             n = (n+1)%64;
  233.             sendabort = 1;
  234.             dostats('A',"ABORT");
  235.             strcpy(msgpkt, "D");
  236.             state = 'Z';
  237.             break;
  238.             }
  239.             if (timeout == TIMEOUT) dostats('A',"TMOUT");
  240.             else { /* protocol error dectected by us */
  241.             dostats('A',"ERROR");
  242.             print_our_err();
  243.             }
  244.             return(FALSE);
  245.     default:    return(FALSE);
  246.     }
  247.     }
  248. }
  249.  
  250. char sinit()
  251. {
  252.     int num, len;
  253.  
  254.     retry++;
  255.     if (numtry++ > MAXTRY) return('A');
  256.     spar(msgpkt);
  257.  
  258.     spack('S',n,13,msgpkt);
  259.     switch(rpack(&len,&num,ackpkt)) {
  260.     case 'N':    return(state);
  261.     case 'Y':    if (n != num) return(state);
  262.         rpar(ackpkt, len);
  263.         if (eol == 0) eol = '\n';
  264.         if (quote == 0) quote = MYQUOTE;
  265.         numtry = 0;
  266.         retry--;
  267.         n = (n+1)%64;
  268.         return('F');
  269.     case 'E':    return('E');
  270.     case FALSE:    if (timeout == USERABORT) state = 'A';
  271.         return(state);
  272.     default:    return('A');
  273.     }
  274. }
  275.  
  276. char sfile()
  277. {
  278.     int num, len;
  279.  
  280.     retry++;
  281.     if (numtry++ > MAXTRY) return('A');
  282.  
  283.     spack('F',n,strlen(filnam),filnam);
  284.     switch(rpack(&len,&num,ackpkt)) {
  285.     case 'N':
  286.     num = (--num<0 ? 63:num);
  287.     if (n != num) return(state);
  288.     case 'Y':
  289.     if (n != num) return(state);
  290.     numtry = 0;
  291.     retry--;
  292.     n = (n+1)%64;
  293.     first = 1;
  294.     size = getpkt();
  295.     return('D');
  296.     case 'E':
  297.     return('E');
  298.     case FALSE:    if (timeout == USERABORT) state = 'A';
  299.            return(state);
  300.     default:
  301.     return('A');
  302.     }
  303. }
  304.  
  305. char sdata()
  306. {
  307.     int num, len;
  308.  
  309.     retry++;
  310.     if (numtry++ > MAXTRY) return('A');
  311.  
  312.     spack('D',n,size,msgpkt);
  313.     switch(rpack(&len,&num,ackpkt)) {
  314.     case 'N':
  315.     num = (--num<0 ? 63:num);
  316.     if (n != num) return(state);
  317.     case 'Y':
  318.     if (n != num) return(state);
  319.     numtry = 0;
  320.     retry--;
  321.     n = (n+1)%64;
  322.     if ((size = getpkt()) == 0) return('Z');
  323.     return('D');
  324.     case 'E':
  325.     return('E');
  326.     case FALSE: if (timeout == USERABORT) state = 'A';
  327.            return(state);
  328.     default:
  329.     return('A');
  330.     }
  331. }
  332.  
  333. char seof()
  334. {
  335.     int num, len;
  336.     retry++;
  337.     if (numtry++ > MAXTRY) return('A');
  338.  
  339. /*   if (timeout == USERABORT) {*/    /* tell host to discard file */
  340. /*    timeout = GOODREAD;    */
  341. /*      spack('Z',n,1,"D");    */
  342. /*      }            */
  343. /*   else            */
  344.     spack('Z',n,sendabort,msgpkt);
  345.     switch(rpack(&len,&num,ackpkt)) {
  346.     case 'N':
  347.     num = (--num<0 ? 63:num);
  348.     if (n != num) return(state);
  349.     case 'Y':
  350.     if (n != num) return(state);
  351.     numtry = 0;
  352.     dostats('Z',"DONE");
  353.     retry--;
  354.     n = (n+1)%64;
  355.     return('B');
  356.     case 'E':    return('E');
  357.     case FALSE:    return(state);
  358.     default:    return('A');
  359.     }
  360. }
  361.  
  362. char sbreak()
  363. {
  364.     int num, len;
  365.     retry++;
  366.     if (numtry++ > MAXTRY) return('A');
  367.  
  368.     spack('B',n,0,msgpkt);
  369.     switch (rpack(&len,&num,ackpkt)) {
  370.     case 'N':
  371.     num = (--num<0 ? 63:num);
  372.     if (n != num) return(state);
  373.     case 'Y':
  374.     if (n != num) return(state);
  375.     dostats('B', "DONE");
  376.     numtry = 0;
  377.     retry--;
  378.     n = (n+1)%64;
  379.     return('C');
  380.     case 'E':    return('E');
  381.     case FALSE:    return(state);
  382.     default:    return ('A');
  383.     }
  384. }
  385.  
  386. /* timeout equals USERABORT so lets end the file and quit  */
  387. /* when host receives 'Z' packet with "D" in data field he */
  388. /* should discard the file.                   */
  389. /*
  390. sabort()
  391. {
  392.     dostats(' ',"ABORT");
  393.     n = (n+1)%64;
  394.     retry--;
  395.     state = 'Z';
  396.     while (state == 'Z') state = seof();
  397.     while (state == 'B') state = sbreak();
  398.     return(FALSE);
  399. }
  400. */
  401.  
  402.  
  403. recsw()
  404. {
  405.     char rinit(), rfile(), rdata();
  406.     int first_time = 1;
  407.  
  408.     state = 'R';
  409.  
  410.     while(TRUE) {
  411.     switch(state) {
  412.     case 'R':    ulp = 0;    /* Assume we won't use LONG packets */
  413.             state = rinit();
  414.             break;
  415.     case 'Z':
  416.     case 'F':    state = rfile(first_time); first_time = 0;
  417.             break;
  418.     case 'D':    state = rdata();
  419.             break;
  420.     case 'C':    return(TRUE);
  421.     case 'E':
  422.     case 'A':   /* easy way to cleanly abort
  423.             should really send and ACK
  424.             with "X" in data field and
  425.             wait for host to abort but
  426.             not all kermits support
  427.             this feature.        */
  428.             if (timeout == USERABORT){
  429.                 /* send an error packet back   */
  430.                 dostats('A',"ABORT");
  431.                 spack('E',n,12,"User aborted");
  432.                 }
  433.                 else if (timeout == TIMEOUT) {
  434.                 /* we timed out waiting */
  435.                 /* will we need to spack here ?*/
  436.                 dostats('A',"TMOUT");
  437.             }
  438.             /* must be 'E' from host or we detected a protocol 
  439.             ** error */
  440.             else dostats('A',"ERROR");
  441.  
  442.             if (state == 'E') print_host_err(msgpkt);
  443.             else if (timeout == GOODREAD) /* tell host why */
  444.                 print_our_err();
  445.                 /* will this kill all files ?*/
  446.             do  {
  447.                 ttime = 2;
  448.                 readchar();
  449.             }  while (timeout == GOODREAD);
  450.             fclose(fp);
  451.             sendchar('\r');
  452.             return(FALSE);
  453.     default:    return(FALSE);
  454.     }
  455.     }
  456. }
  457.  
  458. char rinit()
  459. {
  460.     int len, num, temp;
  461.     retry++;
  462.     if (numtry++ > MAXTRY) return('A');
  463.  
  464.     if (server)
  465.     spack('R',n,strlen(filnam),filnam);
  466.  
  467.     switch(rpack(&len,&num,msgpkt)) {
  468.     case 'S':
  469.     rpar(msgpkt, len);
  470.     /*   Rpar() will set ulp if we can use long packets.  We can't use
  471.     ** that value right away cause we've gotta ACK with normal pkts. */
  472.     temp = ulp; ulp = 0;
  473.     spar(msgpkt);
  474.     spack('Y',n,13,msgpkt);
  475.     ulp = temp;    /* Restore using long pkts flag */
  476.     oldtry = numtry;
  477.     numtry = 0;
  478.     retry--;
  479.     n = (n+1)%64;
  480.     return('F');
  481.     case 'E':
  482.     return('E');
  483.     case 'N':        /* Other side NAKed us... */
  484.     return(state); /* ...so try again      */
  485.     case FALSE:
  486.     if (timeout == USERABORT) return('A');
  487.     if (timeout == TIMEOUT)   return(state); /* Resend Rec-init on a timeout */
  488.     spack('N',n,0,"");
  489.     return(state);
  490.     default:
  491.     return('A');
  492.     }
  493. }
  494.  
  495. char rfile(first_time)
  496. int first_time;
  497. {
  498.     int num, len, temp;
  499.     USHORT a, a7, b8;
  500.     char *fileptr, *buf;
  501.  
  502.     retry++;
  503.     if (numtry++ > MAXTRY) return('A');
  504.  
  505.     switch(rpack(&len,&num,msgpkt)) {
  506.     case 'S':
  507.     if (oldtry++ > MAXTRY) return('A');
  508.     if (num == ((n==0) ? 63:n-1)) {
  509.         /*   Rpar() will set ulp if we can use long packets.  We can't use
  510.         ** that value right away cause we've gotta ACK with normal pkts. */
  511.         temp = ulp; ulp = 0;
  512.         spar(msgpkt);
  513.         spack('Y',num,13,msgpkt);
  514.         ulp = temp;
  515.         numtry = 0;
  516.         return(state);
  517.     }
  518.     else return('A');
  519.     case 'Z':
  520.     if (oldtry++ > MAXTRY) return('A');
  521.     if (num == ((n==0) ? 63:n-1)) {
  522.         spack('Y',num,0,"");
  523.         ScrollInfoMsg(1);
  524.         numtry = 0;
  525.         return(state);
  526.     }
  527.     else return('A');
  528.     case 'F':
  529.     if (num != n) return('A');
  530.     if(!first_time) {    /* Scroll the Z packet line up */
  531.         dostats('Z', "DONE");
  532.         ScrollInfoMsg(1);
  533.     }
  534.     buf = msgpkt;
  535.     fileptr = filnam;
  536.     while ((a = *buf++) != '\0') { /* Terminator added by rpack() */
  537.         if (rptflg) {
  538.         if (a == rptq) {
  539.             rpt = unchar(*buf++);
  540.             a = *buf++;
  541.         }
  542.         }
  543.         b8 = 0;
  544.         if (ebqflg) {        /* 8th-bit prefixing? */
  545.         if (a == ebq) {        /* Yes, got an 8th-bit prefix? */
  546.             b8 = 0200;        /* Yes, remember this, */
  547.             a = *buf++;        /* and get the prefixed character. */
  548.         }
  549.         }
  550.         if (a == quote) {
  551.         a  = *buf++;
  552.         a7 = a & 0177;
  553.         if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
  554.         }
  555.         a |= b8;
  556.         if (rpt == 0) rpt = 1;
  557.         if (p_mode == 1 && a == '\r') continue;
  558.         for (; rpt > 0; rpt--) *fileptr++ = a;
  559.         *fileptr = '\0';    /* Terminate the filename */
  560.     }
  561.  
  562.     if (p_convert) {
  563.         char *p;
  564.         p = &filnam[0];
  565.         while (*p) { *p = tolower(*p); p++; }
  566.     }
  567.     if (notfirst) {
  568.         totbytes = 0L;
  569.         dostats('F',"RECV");
  570.     }
  571.     else {    /* is the first file so emit actual file name from host */
  572.         notfirst++;
  573.     }
  574.     if ((fp = fopen(filnam,"w")) == NULL) {
  575.         InfoMsg2Line("KERMIT: Unable to create file:", filnam);
  576.         strcpy(msgpkt,"VT100 - Kermit - cannot create file: ");
  577.         strcat(msgpkt,filnam);
  578.         spack('E',n,strlen(msgpkt),msgpkt); /* let host know */
  579.         dostats('E',"ERROR");
  580.         return ('\0');     /* abort everything */
  581.     }
  582.     spack('Y',n,0,"");
  583.     oldtry = numtry;
  584.     numtry = 0;
  585.     retry--;
  586.     n = (n+1)%64;
  587.     return('D');
  588.  
  589.     /* totaly done server sending no more */
  590.     case 'B':
  591.     if (num != n) return ('A');
  592.     spack('Y',n,0,"");
  593.     dostats('B', "DONE");
  594.     ScrollInfoMsg(1);
  595.     retry--;
  596.     return('C');
  597.     case 'E':
  598.     return('E');
  599.     case FALSE:
  600.     if (timeout == USERABORT) return('A');
  601.     spack('N',n,0,"");
  602.     return(state);
  603.     default:
  604.     return ('A');
  605.     }
  606. }
  607.  
  608. char rdata()
  609. {
  610.     int num, len;
  611.     retry++;
  612.     if (numtry++ > MAXTRY) return('A');
  613.  
  614.     switch(rpack(&len,&num,msgpkt)) {
  615.     case 'D':
  616.     if (num != n) {
  617.         if (oldtry++ > MAXTRY) return('A');
  618.         if (num == ((n==0) ? 63:n-1)) {
  619.            spack('Y',num,6,msgpkt);
  620.            numtry = 0;
  621.            return(state);
  622.         }
  623.         else return('A');
  624.     }
  625.     decode();
  626.     spack('Y',n,0,"");
  627.     oldtry = numtry;
  628.     numtry = 0;
  629.     retry--;
  630.     n = (n+1)%64;
  631.     return('D');
  632.     case 'Z':
  633.     if (num != n) return('A');
  634.     spack('Y',n,0,"");
  635.     n = (n+1)%64;
  636.     dostats('Z',"DONE");
  637.     retry--;
  638.     fclose(fp);
  639.     return('Z');
  640.     case 'F':
  641.     if (oldtry++ > MAXTRY) return('A');
  642.     if (num == ((n==0) ? 63:n-1)) {
  643.         spack('Y',num,0,"");
  644.         numtry = 0;
  645.         return(state);
  646.     }
  647.     case 'E':
  648.     return('E');
  649.     case FALSE:
  650.     if (timeout == USERABORT) return('A');
  651.     spack('N',n,0,"");
  652.     return(state);
  653.     default:
  654.     return('A');
  655.     }
  656. }
  657.  
  658. static void
  659. spack(type,num,len,data)
  660. char type, *data;
  661. int num, len;
  662. {
  663.     int i;
  664.     char chksum, t;
  665.     register char *bufp;
  666.  
  667.     if(sending && (lastpkt != num)) {
  668.     tp++;
  669.     lastpkt = num;
  670.     }
  671.     dostats(type,mainmode);
  672.     bufp = spackbuf;
  673.     ClearBuffer();
  674.  
  675.     for (i=1; i<=pad; i++) sendchar(padchar);
  676.  
  677.     *bufp++ = SOH;
  678.     if(ulp && len > (MAXPACKSIZ-3))    /* Using long packets */
  679.     t = tochar(0);
  680.     else
  681.     t = tochar(len+3);
  682.     *bufp++ = t; chksum  = t;
  683.     t = tochar(num);
  684.     *bufp++ = t; chksum += t;
  685.     *bufp++ = type; chksum += type;
  686.     if(ulp && len > (MAXPACKSIZ-3)) {    /* Using long packets */
  687.     unsigned int pl = len + 1;
  688.  
  689.     t = tochar(pl / 95);
  690.     *bufp++ = t; chksum += t;
  691.     t = tochar(pl % 95);
  692.     *bufp++ = t; chksum += t;
  693.     t = tochar((((chksum&0300) >> 6)+chksum)&077);
  694.     *bufp++ = t; chksum += t;
  695.     }
  696.     for (i=0; i<len; i++) {
  697.     *bufp++ = data[i]; chksum += data[i];
  698.     }
  699.     chksum = (((chksum&0300) >> 6)+chksum)&077;
  700.     *bufp++ = tochar(chksum);
  701.     if (eol)
  702.     *bufp++ = eol; /* Use sender's requested end-of-line */
  703.     else
  704.     *bufp++ = '\r';
  705.     *bufp   = '\0';
  706.     sendstring(spackbuf);
  707. }
  708.  
  709. rpack(len,num,data)
  710. int *len, *num;
  711. char *data;
  712. {
  713.     int i, done;
  714.     char type, cchksum, rchksum;
  715.     char t = '\0';
  716.  
  717.     do {
  718.     t = readchar();
  719.     if (timeout != GOODREAD) return(FALSE);
  720.     } while (t != SOH);
  721.  
  722.     done = FALSE;
  723.     while (!done) {
  724.     t = readchar();
  725.     if (timeout != GOODREAD) return(FALSE);
  726.     if (t == SOH) continue;
  727.     cchksum = t;
  728.     *len = unchar(t)-3;
  729.     t = readchar();
  730.     if (timeout != GOODREAD) return(FALSE);
  731.     if (t == SOH) continue;
  732.     cchksum += t;
  733.     *num = unchar(t);
  734.     t = readchar();
  735.     if (timeout != GOODREAD) return(FALSE);
  736.     if (t == SOH) continue;
  737.     cchksum += t;
  738.     type = t;
  739.     if((*len == -3) && ulp) {    /* Using long packets */
  740.         t = readchar();
  741.         if (timeout != GOODREAD) return(FALSE);
  742.         if (t == SOH) continue;
  743.         cchksum += t;
  744.         *len = unchar(t)*95;
  745.         t = readchar();
  746.         if (timeout != GOODREAD) return(FALSE);
  747.         if (t == SOH) continue;
  748.         cchksum += t;
  749.         *len += unchar(t);
  750.         (*len)--;
  751.         t = readchar();
  752.         if (timeout != GOODREAD) return(FALSE);
  753.         if (t == SOH) continue;
  754.         if(unchar(t) != ((((cchksum&0300) >> 6)+cchksum)&077)) return(FALSE);;
  755.         cchksum += t;
  756.     }
  757.     for (i=0; i<*len; i++) {
  758.         t = readchar();
  759.         if (timeout != GOODREAD) return(FALSE);
  760.         if (t == SOH) continue;
  761.         cchksum = cchksum + t;
  762.         data[i] = t;
  763.     }
  764.     data[*len] = 0;
  765.     t = readchar();
  766.     if (timeout != GOODREAD) return(FALSE);
  767.     rchksum = unchar(t);
  768.     t = readchar();
  769.     if (timeout != GOODREAD) return(FALSE);
  770.     if (t == SOH) continue;
  771.     done = TRUE;
  772.     }
  773.     if(type != 'B' && type != 'Z')
  774.     dostats(type,mainmode);
  775.     cchksum = (((cchksum&0300) >> 6)+cchksum)&077;
  776.     if (cchksum != rchksum) return(FALSE);
  777.     if(!sending && (*num != lastpkt)) {
  778.     tp++;
  779.     lastpkt = *num;
  780.     }
  781.     return((int)type);
  782. }
  783.  
  784. getpkt() {
  785.     int i,eof;
  786.  
  787.     static char leftover[10] = { '\0', '\0', '\0', '\0', '\0',
  788.                 '\0', '\0', '\0', '\0', '\0' };
  789.  
  790.     if (first == 1) {
  791.     first = 0;
  792.     *leftover = '\0';
  793.     t = getc(fp);
  794.     if (t == EOF) {
  795.         first = 1;
  796.         return(size = 0);
  797.     }
  798.     totbytes++;
  799.     }
  800.     else if (first == -1) {
  801.     first = 1;
  802.     return(size = 0);
  803.     }
  804.     for (size = 0; (msgpkt[size] = leftover[size]) != '\0'; size++) ;
  805.     *leftover = '\0';
  806.     rpt = 0;
  807.     eof = 0;
  808.     while (!eof) {
  809.     next = getc(fp);
  810.     if (next == EOF) {
  811.         first = -1;
  812.         eof   =  1;
  813.     }
  814.     else totbytes++;
  815.     osize = size;
  816.     encode(t);
  817.     t = next;
  818.     if (size == spsiz-3) return(size);
  819.     if (size > spsiz-3) {
  820.         for (i = 0; (leftover[i] = msgpkt[osize+i]) != '\0'; i++)
  821.         ;
  822.         size = osize;
  823.         msgpkt[size] = '\0';
  824.         return(size);
  825.     }
  826.     }
  827.     return(size);
  828. }
  829.  
  830. static void
  831. encode(a)
  832. char a;
  833. {
  834.     int a7,b8;
  835.  
  836.     if (p_mode == 1 && a == '\n') {
  837.     rpt = 0;
  838.     msgpkt[size++] = quote;
  839.     msgpkt[size++] = ctl('\r');
  840.     if (size <= spsiz-3) osize = size;
  841.     msgpkt[size++] = quote;
  842.     msgpkt[size++] = ctl('\n');
  843.     msgpkt[size]   = '\0';
  844.     return;
  845.     }
  846.     if (rptflg) {
  847.     if (a == next && (first == 0)) {
  848.         if (++rpt < 94) return;
  849.         else if (rpt == 94) {
  850.         msgpkt[size++] = rptq;
  851.         msgpkt[size++] = tochar(rpt);
  852.         rpt = 0;
  853.         }
  854.     }
  855.     else if (rpt == 1) {
  856.         rpt = 0;
  857.         encode(a);
  858.         if (size <= spsiz-3) osize = size;
  859.         rpt = 0;
  860.         encode(a);
  861.         return;
  862.     }
  863.     else if (rpt > 1) {
  864.         msgpkt[size++] = rptq;
  865.         msgpkt[size++] = tochar(++rpt);
  866.         rpt = 0;
  867.     }
  868.     }
  869.     a7 = a & 0177;
  870.     b8 = a & 0200;
  871.  
  872.     if (ebqflg && b8) {            /* Do 8th bit prefix if necessary. */
  873.     msgpkt[size++] = ebq;
  874.     a = a7;
  875.     }
  876.  
  877.     if ((a7 < SP) || (a7==DEL)) {
  878.     msgpkt[size++] = quote;
  879.     a = ctl(a);
  880.     }
  881.     if (a7 == quote) msgpkt[size++] = quote;
  882.     if ((rptflg) && (a7 == rptq)) msgpkt[size++] = quote;
  883.  
  884.     if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th bit prefix */
  885.     msgpkt[size++] = quote;    /* if doing 8th-bit prefixes */
  886.  
  887.     msgpkt[size++] = a;
  888.     msgpkt[size] = '\0';
  889. }
  890.  
  891. static void
  892. decode()
  893. {
  894.     USHORT  a, a7, b8;
  895.     char *buf;
  896.  
  897.     buf = msgpkt;
  898.     rpt = 0;
  899.  
  900.     while ((a = *buf++) != '\0') { /* Terminator added by rpack() */
  901.     if (rptflg) {
  902.         if (a == rptq) {
  903.         rpt = unchar(*buf++);
  904.         a = *buf++;
  905.         }
  906.     }
  907.     b8 = 0;
  908.     if (ebqflg) {             /* 8th-bit prefixing? */
  909.         if (a == ebq) {         /* Yes, got an 8th-bit prefix? */
  910.         b8 = 0200;         /* Yes, remember this, */
  911.         a = *buf++;         /* and get the prefixed character. */
  912.         }
  913.         }
  914.     if (a == quote) {
  915.         a  = *buf++;
  916.         a7 = a & 0177;
  917.         if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
  918.     }
  919.     a |= b8;
  920.     if (rpt == 0) rpt = 1;
  921.     if (p_mode == 1 && a == '\r') continue;
  922.     totbytes += rpt;
  923.     for (; rpt > 0; rpt--) putc(a, fp);
  924.     }
  925.     return;
  926. }
  927.  
  928. static void
  929. spar(data)
  930. char data[];
  931. {
  932.     data[0] = tochar(MAXPACKSIZ);
  933.     data[1] = tochar(TTIME_KERMIT);
  934.     data[2] = tochar(MYPAD);
  935.     data[3] = ctl(MYPCHAR);
  936.     data[4] = tochar(MYEOL);
  937.     data[5] = MYQUOTE;
  938.     if ((p_parity > 0) || ebqflg) {       /* 8-bit quoting... */
  939.     data[6] = MYEBQ;      /* If parity or flag on, send &. */
  940.     if ((ebq > 0040 && ebq < 0100) || /* If flag off, then turn it on  */
  941.        (ebq > 0140 && ebq < 0177) || /* if other side has asked us to */
  942.        (ebq == 'Y')) 
  943.         ebqflg = 1;
  944.     }
  945.     else                    /* Normally, */
  946.     data[6] = 'Y';                /* just say we're willing. */
  947.     data[7] = '1';
  948.     data[8] = MYRPTQ;
  949.     data[9] = tochar(IDOLONG);    /* Tell 'em I do LONG packets */
  950.     data[10] = tochar(0);    /* Don't do windows */
  951.     data[11] = tochar(p_kmaxpack / 95);
  952.     data[12] = tochar(p_kmaxpack % 95);
  953.     data[13] = '\0';
  954. }
  955.  
  956. static void
  957. rpar(data, len)
  958. char data[];
  959. int  len;
  960. {
  961.     int ospsiz;
  962.  
  963.     spsiz   = unchar(data[0]);
  964.     ospsiz  = spsiz;
  965.     ttime   = unchar(data[1]);
  966.     pad     = unchar(data[2]);
  967.     padchar = ctl(data[3]);
  968.     eol     = unchar(data[4]);
  969.     quote   = data[5];
  970.     rptflg  = 0;
  971.     ebqflg  = 0;
  972.     if (len >= 6 && data[6] != 0) {
  973.     ebq = data[6];
  974.     if ((ebq > 040 && ebq < 0100) ||
  975.         (ebq > 0140 && ebq < 0177)) ebqflg = 1;
  976.     else if (((p_parity > 0) || ebqflg) && (ebq == 'Y')) {
  977.         ebqflg = 1;
  978.         ebq = '&';
  979.     }
  980.     else ebqflg = 0;
  981.     }
  982.     if (len >= 8 && data[8] != 0) {
  983.     rptq    = data[8];
  984.     rptflg  = ((rptq > 040 && rptq < 0100) ||
  985.            (rptq > 0140 && rptq < 0177));
  986.     }
  987.     if(len >= 9 && data[9] != 0) {
  988.     int capas;
  989.     for(capas=9; data[capas] & 1; capas++) ; /* Skip over continuations */
  990.     if((ulp = (data[9] & IDOLONG)) == IDOLONG) {
  991.         spsiz = 500; /* Default if no packet size specified */
  992.         if(len >= capas+3) {
  993.         spsiz = (unchar((data[capas+2])) * 95) + unchar(data[capas+3]);
  994.         if(spsiz > MAXLONGPKS)
  995.             spsiz = MAXLONGPKS;
  996.         else if(spsiz < 10)    /* Reasonable? */
  997.             spsiz = 500;
  998.         }
  999.     }
  1000.     }
  1001. }
  1002.  
  1003. void
  1004. saybye()
  1005. {
  1006.    int len, num, gotbuf = 0;
  1007.  
  1008.    if(msgpkt == NULL) {    /* No msgpkt buffer, create one */
  1009.     msgpkt    = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  1010.     spackbuf  = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
  1011.         if(msgpkt == NULL || spackbuf == NULL) {
  1012.         InfoMsg1Line("KERMIT: Insufficient free memory, BYE bypassed");
  1013.         return;
  1014.     }
  1015.     gotbuf = 1;
  1016.     }
  1017.  
  1018.    if(numreqs != 0)    /* Requester's up... */
  1019.     Delay(5L);    /* ...so wait for Intuition, just in case. */
  1020.    spack('G',n,1,"F");  /* shut down server no more files */
  1021.    rpack(&len,&num,ackpkt);
  1022.    if(gotbuf) {
  1023.        FreeMem(spackbuf, (long)(MAXLONGPKS+20));
  1024.        FreeMem(msgpkt,   (long)(MAXLONGPKS+20));
  1025.        msgpkt = spackbuf = NULL;
  1026.    }
  1027. }
  1028.  
  1029. static void
  1030. print_our_err()
  1031. {
  1032.     if (retry > MAXTRY || oldtry > MAXTRY) {
  1033.     InfoMsg1Line("KERMIT: Too may retries for packet");
  1034.     strcpy(msgpkt,"VT100 KERMIT: Too many retries for packet");
  1035.     }
  1036.     else {
  1037.     InfoMsg1Line("KERMIT: Protocol Error");
  1038.     strcpy(msgpkt,"VT100 KERMIT: Protocol Error");
  1039.     }
  1040.     spack('E',n,strlen(msgpkt),msgpkt);
  1041. }
  1042.  
  1043. static void
  1044. print_host_err(msg)
  1045. char *msg;
  1046. {
  1047.     InfoMsg2Line("KERMIT: Host Error:", msg);
  1048. }
  1049.  
  1050. static void
  1051. dostats(type,stat)
  1052. char type,*stat;
  1053. {
  1054.     char *statusform = "%5s %-15.15s Pkt: %4d Retr: %2d Bytes: %6ld Type: %c",
  1055.      status[80];
  1056.  
  1057.     if (type == 'Y' || type == 'N' || type == 'G') return;
  1058.  
  1059.     sprintf(status, statusform, stat, filnam, tp, retry-1,
  1060.         (LONG)totbytes, type);
  1061.     InfoMsgNoScroll(status);
  1062. }
  1063.  
  1064. static void
  1065. ClearBuffer()
  1066. {
  1067.     AbortIO((struct IORequest *)Read_Request);
  1068.     Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
  1069.     WaitIO((struct IORequest *)Read_Request);
  1070.     Read_Request->IOSer.io_Command = CMD_CLEAR;
  1071.     DoIO((struct IORequest *)Read_Request);
  1072.     Read_Request->IOSer.io_Command = CMD_READ;
  1073.     SendIO((struct IORequest *)Read_Request);
  1074. }
  1075.