home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / croutes.zip / KERMITPC.C < prev    next >
Text File  |  1984-07-29  |  31KB  |  974 lines

  1. /*
  2.  *    K e r m i t  File Transfer Utility
  3.  *
  4.  *    UNIX Kermit, Columbia University, 1981, 1982, 1983
  5.  *    Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz
  6.  *
  7.  *    usage: kermit [csr][dlbe line baud escapechar] [f1 f2 ...]
  8.  *
  9.  *    where c=connect, s=send [files], r=receive, d=debug,
  10.  *    l=tty line, b=baud rate, e=escape char (decimal ascii code).
  11.  *    For "host" mode Kermit, format is either "kermit r" to
  12.  *    receive files, or "kermit s f1 f2 ..." to send f1 .. fn.
  13.  *
  14.  *
  15.  *    Fixed up again for Unix, Jim Guyton 7/13/83 (Rand Corp)
  16.  */
  17.  
  18. #include <stdio.h>            /* Standard UNIX definitions */
  19. #include <sgtty.h>
  20. #include <signal.h>
  21. #include <setjmp.h>
  22.  
  23. /* Conditional Compilation: 0 means don't compile it, nonzero means do */
  24.  
  25. #define UNIX        1    /* Conditional compilation for UNIX */
  26. #define TOPS_20     0    /* Conditional compilation for TOPS-20 */
  27. #define VAX_VMS     0    /* Ditto for VAX/VMS */
  28. #define IBM_UTS     0    /* Ditto for Amdahl UTS on IBM systems */
  29.  
  30.  
  31. /* Symbol Definitions */
  32.  
  33. #define MAXPACK     94    /* Maximum packet size */
  34. #define SOH        1    /* Start of header */
  35. #define SP        32    /* ASCII space */
  36. #define CR        015    /* ASCII Carriage Return */
  37. #define DEL        127    /* Delete (rubout) */
  38. #define CTRLD        4
  39. #define BRKCHR        CTRLD    /* Default escape character for CONNECT */
  40.  
  41. #define MAXTRY        5    /* Times to retry a packet */
  42. #define MYQUOTE     '#'     /* Quote character I will use */
  43. #define MYPAD        0    /* Number of padding characters I will need */
  44. #define MYPCHAR     0    /* Padding character I need */
  45. #define MYEOL        '\n'    /* End-Of-Line character I need */
  46. #define MYTIME        5    /* Seconds after which I should be timed out */
  47. #define MAXTIM        20    /* Maximum timeout interval */
  48. #define MINTIM        2    /* Minumum timeout interval */
  49.  
  50. #define TRUE        -1    /* Boolean constants */
  51. #define FALSE        0
  52.  
  53.  
  54. /* Global Variables */
  55.  
  56. int    size,            /* Size of present data */
  57.     n,            /* Message number */
  58.     rpsiz,            /* Maximum receive packet size */
  59.     spsiz,            /* Maximum send packet size */
  60.     pad,            /* How much padding to send */
  61.     timint,         /* Timeout for foreign host on sends */
  62.     numtry,         /* Times this packet retried */
  63.     oldtry,         /* Times previous packet retried */
  64.     fd,            /* File pointer of file to read/write */
  65.     remfd,            /* File pointer of the host's tty */
  66.     image,            /* -1 means 8-bit mode */
  67.     remspd,         /* Speed of this tty */
  68.     host,            /* -1 means we're a host-mode kermit */
  69.     debug;            /* -1 means debugging */
  70.  
  71. char    state,            /* Present state of the automaton */
  72.     padchar,        /* Padding character to send */
  73.     eol,            /* End-Of-Line character to send */
  74.     escchr,         /* Connect command escape character */
  75.     quote,            /* Quote character in incoming data */
  76.     **filelist,        /* List of files to be sent */
  77.     *filnam,        /* Current file name */
  78.     recpkt[MAXPACK],    /* Receive packet buffer */
  79.     packet[MAXPACK];    /* Packet buffer */
  80.  
  81. struct sgttyb
  82.     rawmode,        /* Host tty "raw" mode */
  83.     cookedmode,        /* Host tty "normal" mode */
  84.     remttymode;        /* Assigned tty line "raw" mode */
  85.  
  86. jmp_buf env;            /* Environment ptr for timeout longjump */
  87.  
  88.  
  89. /*
  90.  *    m a i n
  91.  *
  92.  *    Main routine - parse command and options, set up the
  93.  *    tty lines, and dispatch to the appropriate routine.
  94.  */
  95.  
  96. main(argc,argv)
  97. int argc;                /* Character pointer for */
  98. char **argv;                /*  command line arguments */
  99. {
  100.   char *remtty,*cp;            /* tty for CONNECT, char pointer */
  101.   int speed, cflg, rflg, sflg;        /* speed of assigned tty, */
  102.                     /* flags for CONNECT, RECEIVE, SEND */
  103.   if (argc < 2) usage();        /* Make sure there's a command line. */
  104.  
  105.   cp = *++argv; argv++; argc -= 2;    /* Set up pointers to args */
  106.  
  107. /* Initialize this side's SEND-INIT parameters */
  108.  
  109.   eol = CR;                /* EOL for outgoing packets */
  110.   quote = MYQUOTE;            /* Standard control-quote char "#" */
  111.   pad = 0;                /* No padding */
  112.   padchar = NULL;            /* Use null if any padding wanted */
  113.  
  114.   speed = cflg = sflg = rflg = 0;    /* Turn off all parse flags */
  115.   remtty = 0;                /* Default is host (remote) mode */
  116.   image = FALSE;            /* Default to 7-bit mode */
  117.   escchr = BRKCHR;            /* Default escape character */
  118.  
  119.   while ((*cp) != NULL)         /* Get a character from the cmd line */
  120.     switch (*cp++)            /* Based on what the character is, */
  121.      {                    /*  do one of the folloing */
  122.       case '-': break;                  /* Ignore dash (UNIX style) */
  123.       case 'c': cflg++; break;          /* C = CONNECT command */
  124.       case 's': sflg++; break;          /* S = SEND command */
  125.       case 'r': rflg++; break;          /* R = RECEIVE command */
  126.       case 'e': if (argc--)             /* E = specify escape char */
  127.           escchr = atoi(*argv++); /*  as ascii decimal number */
  128.         else usage();
  129.         if (debug) fprintf(stderr,"escape char is ascii %d\n",escchr);
  130.         break;
  131.       case 'l': if (argc--)             /* L = specify tty line to use */
  132.           remtty = *argv++;
  133.         else usage();
  134.         if (debug) fprintf(stderr,"line %s\n",remtty);
  135.         break;
  136. #if UNIX                /* This part only for UNIX systems */
  137.       case 'b': if (argc--) speed = atoi(*argv++); /* Set baud rate */
  138.           else usage();
  139.         if (debug) fprintf(stderr,"speed %d\n",speed); break;
  140.  
  141.       case 'i': image = TRUE; break;    /* Image (8-bit) mode */
  142. #endif /* UNIX */
  143.  
  144.       case 'd': debug = TRUE; break;    /* Debug mode */
  145.      }
  146.  
  147. /* Done parsing */
  148.  
  149.   if ((cflg+sflg+rflg) != 1) usage();    /* Only one command allowed */
  150.  
  151.   remfd = 0;                /* Start out as a host (remote) */
  152.   host = TRUE;
  153.  
  154.   if (remtty)                /* If another tty was specified, */
  155.    {
  156.     remfd = open(remtty,2);        /*  open it */
  157.     if (remfd < 0)            /*  check for failure */
  158.      {
  159.       fprintf(stderr,"Kermit: cannot open %s\n",remtty);
  160.       exit(-1);             /*  Failed, quit. */
  161.      }
  162.     host = FALSE;            /* Opened OK, flag local (not host) */
  163.    }
  164.  
  165. /* Put the tty(s) into the correct modes */
  166.  
  167.   gtty(0,&cookedmode);            /* Save current mode for later */
  168.   gtty(0,&rawmode);
  169.   rawmode.sg_flags |= (RAW|TANDEM);
  170.   rawmode.sg_flags &= ~(ECHO|CRMOD);
  171.  
  172.   gtty(remfd,&remttymode);        /* If local kermit, get mode of */
  173.                     /*  assigned tty */
  174.   remttymode.sg_flags |= (RAW|TANDEM);
  175.   remttymode.sg_flags &= ~(ECHO|CRMOD);
  176.  
  177. #if UNIX                /* Speed changing for UNIX only */
  178.   if (speed)                /* User specified a speed? */
  179.    {
  180.     switch(speed)            /* Get internal system code */
  181.      {
  182.     case 110: speed = B110; break;
  183.     case 150: speed = B150; break;
  184.     case 300: speed = B300; break;
  185.     case 1200: speed = B1200; break;
  186.     case 2400: speed = B2400; break;
  187.     case 4800: speed = B4800; break;
  188.     case 9600: speed = B9600; break;
  189.     default: fprintf(stderr,"bad line speed\n");
  190.      }
  191.     remttymode.sg_ispeed = speed;
  192.     remttymode.sg_ospeed = speed;
  193.    }
  194. #endif /* UNIX */
  195.  
  196.   if (remfd) stty(remfd,&remttymode);    /* Put asg'd tty in raw mode */
  197.  
  198.  
  199. /* All set up, now execute the command that was given. */
  200.  
  201.   if (cflg) connect();            /* CONNECT command */
  202.  
  203.   if (sflg)                /* SEND command */
  204.    {
  205.     if (argc--) filnam = *argv++;    /* Get file to send */
  206.       else usage();
  207.     filelist = argv;
  208.     if (host) stty(0,&rawmode);     /* Put tty in raw mode if remote */
  209.     if (sendsw() == FALSE)        /* Send the file(s) */
  210.       printf("Send failed.\n");         /* Report failure */
  211.     else                /*  or */
  212.       printf("OK\n");                   /* success */
  213.     if (host) stty(0,&cookedmode);    /* Restore tty */
  214.    }
  215.  
  216.   if (rflg)                /* RECEIVE command */
  217.    {
  218.     if (host) stty(0,&rawmode);     /* Put tty in raw mode if remote */
  219.     if (recsw() == FALSE)        /* Receive the file */
  220.       printf("Receive failed.\n");      /* Report failure */
  221.     else                /*  or */
  222.       printf("OK\n");                   /* success */
  223.     if (host) stty(0,&cookedmode);    /* Restore tty */
  224.    }
  225.  }
  226.  
  227. usage()                 /* Give message if user makes */
  228. {                    /* a mistake in the command */
  229.   fprintf(stderr,
  230.      "usage: kermit [csr][di][lbe] [line] [baud] [esc char] [f1 f2 ...]\n");
  231.   exit();
  232. }
  233.  
  234. /*
  235.  *    s e n d s w
  236.  *
  237.  *    Sendsw is the state table switcher for sending
  238.  *    files.    It loops until either it finishes, or
  239.  *    an error is encountered.  The routines called by
  240.  *    sendsw are responsible for changing the state.
  241.  *
  242.  */
  243.  
  244. sendsw()
  245. {
  246.  char sinit(),sfile(),seof(),sdata(),sbreak();
  247.  
  248.  state = 'S';                           /* Send initiate is the start state */
  249.  n = 0;                 /* Initialize message number */
  250.  numtry = 0;                /* Say no tries yet */
  251.  while(TRUE)                /* Do this as long as necessary */
  252.   {
  253.    switch(state)
  254.     {
  255.      case 'D':  state = sdata();  break; /* Data-Send state */
  256.      case 'F':  state = sfile();  break; /* File-Send */
  257.      case 'Z':  state = seof();   break; /* End-of-File */
  258.      case 'S':  state = sinit();  break; /* Send-Init */
  259.      case 'B':  state = sbreak(); break; /* Break-Send */
  260.      case 'C':  return (TRUE);           /* Complete */
  261.      case 'A':  return (FALSE);          /* "Abort" */
  262.      default:    return (FALSE);      /* Unknown, fail */
  263.     }
  264.   }
  265. }
  266.  
  267.  
  268. /*
  269.  *    s i n i t
  270.  *
  271.  *    Send Initiate: Send my parameters, get other side's back.
  272.  */
  273.  
  274. char sinit()
  275. {
  276.   int num, len;             /* Packet number, length */
  277.  
  278.   if (debug) fprintf(stderr,"sinit\n");
  279.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  280.   spar(packet);             /* Fill up with init info */
  281.   if (debug) fprintf(stderr,"n = %d\n",n);
  282.  
  283. #if UNIX
  284.   flushinput();             /* Flush pending input */
  285. #endif /* UNIX */
  286.  
  287.   spack('S',n,6,packet);                /* Send an S packet */
  288.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  289.    {
  290.     case 'N':  return(state);           /* NAK */
  291.  
  292.     case 'Y':                           /* ACK */
  293.       if (n != num) return(state);    /* If wrong ACK, stay in S state */
  294.       rpar(recpkt);            /* Get other side's init info */
  295.       if (eol == 0) eol = '\n';         /* Check and set defaults */
  296.       if (quote == 0) quote = '#';      /* Control-prefix quote */
  297.       numtry = 0;            /* Reset try counter */
  298.       n = (n+1)%64;            /* Bump packet count */
  299.       if (debug) fprintf(stderr,"Opening %s\n",filnam);
  300.       fd = open(filnam,0);        /* Open the file to be sent */
  301.       if (fd < 0) return('A');          /* if bad file descriptor, give up */
  302.       if (!host) printf("Sending %s\n",filnam);
  303.       return('F');                      /* OK, switch state to F */
  304.  
  305.     case FALSE: return(state);        /* Receive failure, stay in S state */
  306.     default: return('A');               /* Anythig else, just "abort" */
  307.    }
  308.  }
  309.  
  310.  
  311. /*
  312.  *    s f i l e
  313.  *
  314.  *    Send File Header.
  315.  */
  316.  
  317. char sfile()
  318. {
  319.   int num, len;             /* Packet number, length */
  320.  
  321.   if (debug) fprintf(stderr,"sfile\n");
  322.  
  323.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  324.   for (len=0; filnam[len] != '\0'; len++); /* Add up the length */
  325.  
  326.   spack('F',n,len,filnam);              /* Send an F packet */
  327.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  328.    {
  329.     case 'N':                           /* NAK, just stay in this state, */
  330.       if (n != (num=(--num<0)?63:num))    /*  unless NAK for next packet, */
  331.     return(state);            /*  which is just like an ACK */
  332.                     /*  for this packet, fall thru to... */
  333.     case 'Y':                           /* ACK */
  334.       if (n != num) return(state);    /* If wrong ACK, stay in F state */
  335.       numtry = 0;            /* Reset try counter */
  336.       n = (n+1)%64;            /* Bump packet count */
  337.       size = bufill(packet);        /* Get first data from file */
  338.       return('D');                      /* Switch state to D */
  339.  
  340.     case FALSE: return(state);        /* Receive failure, stay in F state */
  341.     default:    return('A');            /* Something esle, just "abort" */
  342.    }
  343. }
  344.  
  345.  
  346. /*
  347.  *    s d a t a
  348.  *
  349.  *    Send File Data
  350.  */
  351.  
  352. char sdata()
  353. {
  354.   int num, len;             /* Packet number, length */
  355.  
  356.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, give up */
  357.   spack('D',n,size,packet);             /* Send a D packet */
  358.  
  359.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  360.    {
  361.     case 'N':                           /* NAK, just stay in this state, */
  362.       if (n != (num=(--num<0)?63:num))    /*  unless NAK for next packet, */
  363.     return(state);            /*  which is just like an ACK */
  364.                     /*  for this packet, fall thru to... */
  365.     case 'Y':                           /* ACK */
  366.       if (n != num) return(state);    /* If wrong ACK, fail */
  367.       numtry = 0;            /* Reset try counter */
  368.       n = (n+1)%64;            /* Bump packet count */
  369.       if ((size = bufill(packet)) == EOF) /* Get data from file */
  370.     return('Z');                    /* If EOF set state to that */
  371.       return('D');                      /* Got data, stay in state D */
  372.  
  373.     case FALSE: return(state);        /* Receive failure, stay in D */
  374.     default:    return('A');            /* Anything else, "abort" */
  375.    }
  376. }
  377.  
  378.  
  379. /*
  380.  *    s e o f
  381.  *
  382.  *    Send End-Of-File.
  383.  */
  384.  
  385. char seof()
  386. {
  387.   int num, len;             /* Packet number, length */
  388.   if (debug) fprintf(stderr,"seof\n");
  389.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, "abort" */
  390.   spack('Z',n,0,packet);                /* Send a 'Z' packet */
  391.   if (debug) fprintf(stderr,"seof1 ");
  392.   switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  393.    {
  394.     case 'N':                           /* NAK, fail */
  395.       if (n != (num=(--num<0)?63:num))    /* ...unless for previous packet, */
  396.     return(state);            /*  in which case, fall thru to ... */
  397.     case 'Y':                           /* ACK */
  398.       if (debug) fprintf(stderr,"seof2 ");
  399.       if (n != num) return(state);    /* If wrong ACK, hold out */
  400.       numtry = 0;            /* Reset try counter */
  401.       n = (n+1)%64;            /* and bump packet count */
  402.       if (debug) fprintf(stderr,"closing %s, ",filnam);
  403.       close(fd);            /* Close the input file */
  404.       if (debug) fprintf(stderr,"ok, getting next file\n");
  405.       if (gnxtfl() == FALSE)        /* No more files go? */
  406.     return('B');                    /* if not, break, EOT, all done */
  407.       if (debug) fprintf(stderr,"new file is %s\n",filnam);
  408.       return('F');                      /* More files, switch state to F */
  409.  
  410.     case FALSE: return(state);        /* Receive failure, stay in state Z */
  411.     default:    return('A');            /* Something else, "abort" */
  412.    }
  413. }
  414.  
  415.  
  416. /*
  417.  *    s b r e a k
  418.  *
  419.  *    Send Break (EOT)
  420.  */
  421.  
  422. char sbreak()
  423. {
  424.   int num, len;             /* Packet number, length */
  425.   if (debug) fprintf(stderr,"sbreak\n");
  426.   if (numtry++ > MAXTRY) return('A');   /* If too many tries "abort" */
  427.  
  428.   spack('B',n,0,packet);                /* Send a B packet */
  429.   switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  430.    {
  431.     case 'N':                           /* NAK, fail */
  432.       if (n != (num=(--num<0)?63:num))    /* ...unless for previous packet, */
  433.     return(state);            /*  in which case, fall thru to ... */
  434.  
  435.     case 'Y':                           /* ACK */
  436.       if (n != num) return(state);    /* If wrong ACK, fail */
  437.  
  438.       numtry = 0;            /* Reset try counter */
  439.       n = (n+1)%64;            /* and bump packet count */
  440.       return('C');                      /* switch state to Complete */
  441.  
  442.     case FALSE: return(state);        /* Receive failure, stay in state B */
  443.     default:    return ('A');           /* Other, "abort" */
  444.    }
  445. }
  446.  
  447.  
  448. /*
  449.  *    r e c s w
  450.  *
  451.  *    This is the state table switcher for receiving files.
  452.  */
  453.  
  454. recsw()
  455. {
  456.   char rinit(),rdata(),rfile();     /* Use these procedures */
  457.  
  458.   state = 'R';                          /* Receive is the start state */
  459.   n = 0;                /* Initialize message number */
  460.   numtry = 0;                /* Say no tries yet */
  461.  
  462.   while(TRUE) switch(state)        /* Do until done */
  463.    {
  464.     case 'D':   state = rdata(); break; /* Data receive state */
  465.     case 'F':   state = rfile(); break; /* File receive state */
  466.     case 'R':   state = rinit(); break; /* Send initiate state */
  467.     case 'C':   return(TRUE);           /* Complete state */
  468.     case 'A':   return(FALSE);          /* "Abort" state */
  469.     }
  470.   }
  471.  
  472. /*
  473.  *    r i n i t
  474.  *
  475.  *    Receive Initialization
  476.  */
  477.  
  478. char rinit()
  479. {
  480.   int len, num;             /* Packet length, number */
  481.  
  482.   if (numtry++ > MAXTRY) return('A');   /* If too many tries, "abort" */
  483.   switch(rpack(&len,&num,packet))    /* Get a packet */
  484.   {
  485.    case 'S':                            /* Send-Init */
  486.      rpar(packet);            /* Get the other side's init data */
  487.      spar(packet);            /* Fill up packet with my init info */
  488.      spack('Y',n,6,packet);             /* ACK with my parameters */
  489.      oldtry = numtry;            /* Save old try count */
  490.      numtry = 0;            /* Start a new counter */
  491.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  492.      return('F');                       /* Enter File-Send state */
  493.  
  494.    case FALSE:    return (state);     /* Didn't get a packet, keep waiting */
  495.    default:    return('A');            /* Some other packet type, "abort" */
  496.   }
  497. }
  498.  
  499.  
  500. /*
  501.  *    r f i l e
  502.  *
  503.  *    Receive File Header
  504.  */
  505.  
  506. char rfile()
  507. {
  508.  int num, len;                /* Packet number, length */
  509.  
  510.  if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
  511.  switch(rpack(&len,&num,packet))    /* Get a packet */
  512.   {
  513.    case 'S':                            /* Send-Init, maybe our ACK lost */
  514.      if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  515.      if (num == ((n==0)?63:n-1))    /* Previous packet, mod 64? */
  516.       {                 /* Yes, ACK it again */
  517.        spar(packet);            /* with our Send-Init parameters */
  518.        spack('Y',num,6,packet);         /*  ... */
  519.        numtry = 0;            /* Reset try counter */
  520.        return(state);            /* Stay in this state */
  521.       }
  522.      else return('A');                  /* Not previous packet, "abort" */
  523.  
  524.    case 'Z':                            /* End-Of-File */
  525.      if (oldtry++ > MAXTRY) return('A');
  526.      if (num == ((n==0)?63:n-1))    /* Previous packet, mod 64? */
  527.       {                 /* Yes, ACK it again. */
  528.        spack('Y',num,0,0);
  529.        numtry = 0;
  530.        return(state);            /* Stay in this state */
  531.       }
  532.      else return('A');                  /* Not previous packet, "abort" */
  533.  
  534.    case 'F':                            /* File Header, */
  535.      if (num != n) return('A');         /* which is what we really want */
  536.                     /* The packet number must be right */
  537.      if (!getfil(packet))        /* Try to open a new file */
  538.       {
  539.        fprintf(stderr,"Could not create %s\n"); /* Give up if can't */
  540.        return('A');
  541.       }
  542.      else                /* OK, give message */
  543.        if (!host) printf("Receiving %s\n",packet);
  544.      spack('Y',n,0,0);                  /* Acknowledge the file header */
  545.      oldtry = numtry;            /* Reset try counters */
  546.      numtry = 0;            /* ... */
  547.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  548.      return('D');                       /* Switch to Data state */
  549.  
  550.    case 'B':                            /* Break transmission (EOT) */
  551.      if (num != n) return ('A');        /* Need right packet number here */
  552.      spack('Y',n,0,0);                  /* Say OK */
  553.      return('C');                       /* Go to complete state */
  554.  
  555.    case FALSE: return(state);        /* Couldn't get packet, keep trying */
  556.    default:    return ('A');           /* Some other packet, "abort" */
  557.   }
  558. }
  559.  
  560.  
  561. /*
  562.  *    r d a t a
  563.  *
  564.  *    Receive Data
  565.  */
  566.  
  567. char rdata()
  568. {
  569.  int num, len;                /* Packet number, length */
  570.  
  571.  if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
  572.  switch(rpack(&len,&num,packet))    /* Get packet */
  573.   {
  574.    case 'D':                            /* Got Data packet */
  575.      if (num != n)            /* Right packet? */
  576.       {                 /* No */
  577.        if (oldtry++ > MAXTRY) return('A'); /* If too many tries, give up */
  578.        if (num == ((n==0)?63:n-1))    /* Else check packet number */
  579.     {                /* Previous packet again? */
  580.      spack('Y',num,6,packet);       /* Yes, re-ACK it */
  581.      numtry = 0;            /* Reset try counter */
  582.      return(state);         /* Stay in D, don't write out data! */
  583.     }
  584.        else return('A');                /* sorry wrong number */
  585.       }
  586.                     /* Got data with right packet number */
  587.      bufemp(packet,fd,len);        /* Write the data to the file */
  588.      spack('Y',n,0,0);                  /* Acknowledge the packet */
  589.      oldtry = numtry;            /* Reset the try counters */
  590.      numtry = 0;            /* ... */
  591.      n = (n+1)%64;            /* Bump packet number, mod 64 */
  592.      return('D');                       /* Remain in data state */
  593.  
  594.    case 'F':                            /* Got a File Header */
  595.      if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  596.      if (num == ((n==0)?63:n-1))    /* Else check packet number */
  597.       {                 /* It was the previous one */
  598.        spack('Y',num,0,0);              /* ACK it again */
  599.        numtry = 0;            /* Reset try counter */
  600.        return(state);            /* Stay in Data state */
  601.       }
  602.      else return('A');                  /* Not previous packet, "abort" */
  603.  
  604.    case 'Z':                            /* End-Of-File */
  605.      if (num != n) return('A');         /* Must have right packet number */
  606.      spack('Y',n,0,0);                  /* OK, ACK it. */
  607.      close(fd);             /* Close the file */
  608.      n = (n+1)%64;            /* Bump packet number */
  609.      return('F');                       /* Go back to Receive File state */
  610.  
  611.    case FALSE:    return(state);        /* No packet came, keep waiting */
  612.    default:    return('A');            /* Some other packet, "abort" */
  613.   }
  614. }
  615.  
  616. /*
  617.  *    c o n n e c t
  618.  *
  619.  *    Establish a virtual terminal connection with the remote host, over an
  620.  *    assigned tty line.
  621.  *
  622.  */
  623.  
  624. connect()
  625. {
  626.   int parent;                /* Fork handle */
  627.   char c = NULL, r = '\r';
  628.  
  629.   if (host)        /* If in host mode, nothing to connect to */
  630.    {
  631.     fprintf(stderr,"Kermit: nothing to connect to\n");
  632.     return;
  633.    }
  634.  
  635.   parent = fork();    /* Start fork to get typeout from remote host */
  636.  
  637.   if (parent)        /* Parent passes typein to remote host */
  638.    {
  639.     printf("Kermit: connected.\r\n");   /* Give message */
  640.     stty(0,&rawmode);            /* Put tty in raw mode */
  641.     read(0,&c,1);            /* Get a character */
  642.     while ((c&0177) != escchr)        /* Check for escape character */
  643.      {                    /* Not it */
  644.       write(remfd,&c,1);        /* Write the character on screen */
  645.       c = NULL;             /* Nullify it */
  646.       read(0,&c,1);            /* Get next character */
  647.      }                    /* Until escape character typed */
  648.     kill(parent,9);            /* Done, get rid of fork */
  649.     stty(0,&cookedmode);        /* Restore tty mode */
  650.     printf("\nKermit: disconnected.\n"); /* Give message */
  651.     return;                /* Done */
  652.    }
  653.   else            /* Child does the reading from the remote host */
  654.    {
  655.     while(1)                /* Do this forever */
  656.      {
  657.       read(remfd,&c,1);
  658.       write(1,&c,1);
  659.      }
  660.    }
  661. }
  662.  
  663. /*
  664.  *    KERMIT utilities.
  665.  */
  666.  
  667. clkint()                /* Timer interrupt handler */
  668. {
  669.  longjmp(env,TRUE);            /* Tell rpack to give up */
  670. }
  671.  
  672. /* tochar converts a control character to a printable one by adding a space */
  673.  
  674. char tochar(ch)
  675. char ch;
  676. {
  677.   return(ch + ' ');             /* make sure not a control char */
  678. }
  679.  
  680.  
  681. /* unchar undoes tochar */
  682.  
  683. char unchar(ch)
  684. char ch;
  685. {
  686.   return(ch - ' ');             /* restore char */
  687. }
  688.  
  689.  
  690. /*
  691.  * ctl turns a control character into a printable character by toggling the
  692.  * control bit (ie. ^A becomes A and A becomes ^A).
  693.  */
  694.  
  695. char ctl(ch)
  696. char ch;
  697. {
  698.  return(ch ^ 64);        /* toggle the control bit */
  699. }
  700.  
  701.  
  702. /*
  703.  *    s p a c k
  704.  *
  705.  *    Send a Packet
  706.  */
  707.  
  708. spack(type,num,len,data)
  709. char type, *data;
  710. int num, len;
  711. {
  712.   int i;                /* Character loop counter */
  713.   char chksum, buffer[100];        /* Checksum, packet buffer */
  714.   register char *bufp;            /* Buffer pointer */
  715.  
  716.   bufp = buffer;            /* Set up buffer pointer */
  717.   for (i=1; i<=pad; i++) write(remfd,&padchar,1); /* Issue any padding */
  718.  
  719.   *bufp++ = SOH;            /* Packet marker, ASCII 1 (SOH) */
  720.   chksum = tochar(len+3);        /* Initialize the checksum */
  721.   *bufp++ = tochar(len+3);        /* Send the character count */
  722.   chksum = chksum + tochar(num);    /* Init checksum */
  723.   *bufp++ = tochar(num);        /* Packet number */
  724.   chksum = chksum + type;        /* Accumulate checksum */
  725.   *bufp++ = type;            /* Packet type */
  726.  
  727.   for (i=0; i<len; i++)         /* Loop for all data characters */
  728.    {
  729.     *bufp++ = data[i];            /* Get a character */
  730.     chksum = chksum+data[i];        /* Accumulate checksum */
  731.    }
  732.   chksum = (chksum + (chksum & 192) / 64) & 63; /* Compute final checksum */
  733.   *bufp++ = tochar(chksum);        /* Put it in the packet */
  734.   *bufp = eol;                /* Extra-packet line terminator */
  735.   write(remfd, buffer,bufp-buffer+1);    /* Send the packet */
  736.   if (debug) putc('.',stderr);
  737. }
  738.  
  739. /*
  740.  *  r p a c k
  741.  *
  742.  *  Read a Packet
  743.  *
  744.  */
  745.  
  746. rpack(len,num,data)
  747. int *len, *num;             /* Packet length, number */
  748. char *data;                /* Packet data */
  749. {
  750.  int i, done;                /* Data character number, loop exit */
  751.  char chksum, t, type;            /* Checksum, current char, pkt type */
  752.  
  753. #if UNIX                /* TOPS-20 can't handle timeouts... */
  754.  if (setjmp(env)) return FALSE;     /* Timed out, fail */
  755.  signal(SIGALRM,clkint);        /* Setup the timeout */
  756.  if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
  757.  alarm(timint);
  758.  
  759.  flushinput();                /* Flush any pending input */
  760. #endif /* UNIX */
  761.  
  762.  while (t != SOH)            /* Wait for packet header */
  763.   {
  764.    read(remfd,&t,1);
  765.    if (!image) t &= 0177;        /* Handle parity */
  766.   }
  767.  
  768.  done = FALSE;                /* Got SOH, init loop */
  769.  while (!done)                /* Loop to get a packet */
  770.   {
  771.    read(remfd,&t,1);            /* Get character */
  772.    if (!image) t &= 0177;        /* Handle parity */
  773.    if (t == SOH) continue;        /* Resynchronize if SOH */
  774.  
  775.    chksum = t;                /* Start the checksum */
  776.    *len = unchar(t)-3;            /* Character count */
  777.  
  778.    read(remfd,&t,1);            /* Get character */
  779.    if (!image) t &= 0177;        /* Handle parity */
  780.    if (t == SOH) continue;        /* Resynchronize if SOH */
  781.    chksum = chksum + t;         /* Accumulate checksum */
  782.    *num = unchar(t);            /* Packet number */
  783.  
  784.    read(remfd,&t,1);            /* Get character */
  785.    if (!image) t &= 0177;        /* Handle parity */
  786.    if (t == SOH) continue;        /* Resynchronize if SOH */
  787.    chksum = chksum + t;         /* Accumulate checksum */
  788.    type = t;                /* Packet type */
  789.  
  790.    for (i=0; i<*len; i++)        /* The data itself, if any */
  791.     {                    /* Loop for character count */
  792.      read(remfd,&t,1);            /* Get character */
  793.      if (!image) t &= 0177;        /* Handle parity */
  794.      if (t == SOH) continue;        /* Resynch if SOH */
  795.      chksum = chksum + t;        /* Accumulate checksum */
  796.      data[i] = t;            /* Put it in the data buffer */
  797.     }
  798.    data[*len] = 0;            /* Mark the end of the data */
  799.  
  800.    read(remfd,&t,1);            /* Get last character (checksum) */
  801.    if (!image) t &= 0177;        /* Handle parity */
  802.    if (t == SOH) continue;        /* Resynchronize if SOH */
  803.    done = TRUE;             /* Got checksum, done */
  804.   }
  805. #if UNIX
  806.  alarm(0);                /* Disable the timer interrupt */
  807. #endif
  808.  
  809.  chksum = (chksum + (chksum & 192) / 64) & 63; /* Fold bits 7,8 into chksum */
  810.  if (chksum != unchar(t)) return(FALSE); /* Check the checksum, fail if bad */
  811.  
  812.  return(type);                /* All OK, return packet type */
  813. }
  814.  
  815.  
  816.  
  817. /*
  818.  *    b u f i l l
  819.  *
  820.  *    Get a bufferful of data from the file that's being sent.
  821.  *    Only control-quoting is done; 8-bit & repeat count prefixes are
  822.  *    not handled.
  823.  */
  824.  
  825. bufill(buffer)
  826. char buffer[];                /* Buffer */
  827.  {
  828.   int i;                /* Loop index */
  829.   char t,t7;
  830.   i = 0;                /* Init data buffer pointer */
  831.   while(read(fd,&t,1) > 0)        /* Get the next character */
  832.    {
  833.     if (image)                /* In 8-bit mode? */
  834.      {
  835.       t7 = t & 0177;            /* Yes, look at low-order 7 bits */
  836.       if (t7 < SP || t7==DEL || t7==quote) /* Control character? */
  837.        {                /*    Yes, */
  838.     buffer[i++] = quote;        /*    quote this character */
  839.     if (t7 != quote) t = ctl(t);    /* and uncontrollify */
  840.        }
  841.      }
  842.     else                /* Else, ASCII text mode */
  843.      {
  844.       t &= 0177;            /* Strip off the parity bit */
  845.       if (t < SP || t == DEL || t == quote) /* Control character? */
  846.        {                /* Yes */
  847.     if (t=='\n')                    /* If newline, squeeze CR in first */
  848.      {
  849.       buffer[i++] = quote;
  850.       buffer[i++] = ctl('\r');
  851.      }
  852.     buffer[i++] = quote;        /* Insert quote */
  853.     if (t != quote) t=ctl(t);    /* Uncontrollified the character */
  854.        }
  855.      }
  856.     buffer[i++] = t;            /* Deposit the character itself */
  857.     if (i >= spsiz-8) return(i);    /* Check length */
  858.    }
  859.   if (i==0) return(EOF);        /* Wind up here only on EOF */
  860.   return(i);                /* Handle partial buffer */
  861. }
  862.  
  863. /*
  864.  *    b u f e m p
  865.  *
  866.  *    Get data from an incoming packet into a file.
  867.  */
  868.  
  869. bufemp(buffer,fd,len)
  870. char buffer[];                /* Buffer */
  871. int fd, len;                /* File pointer, length */
  872.  {
  873.   int i;                /* Counter */
  874.   char t;                /* Character holder */
  875.  
  876.   for (i=0; i<len; i++)         /* Loop thru the data field */
  877.    {
  878.     t = buffer[i];            /* Get character */
  879.     if (t == MYQUOTE)            /* Control quote? */
  880.      {                    /* Yes */
  881.       t = buffer[++i];            /* Get the quoted character */
  882.       if ((t & 0177) != MYQUOTE)    /* Low order bits match quote char? */
  883.     t = ctl(t);            /* No, uncontrollify it */
  884.      }
  885.     if (image || (t != CR))        /* Don't pass CR in text mode */
  886.       write(fd,&t,1);            /* Put the char in the file */
  887.    }
  888.  }
  889.  
  890.  
  891. /*
  892.  *    g e t f i l
  893.  *
  894.  *    Open a new file
  895.  */
  896.  
  897. getfil(filenm)                /* Bizarre logic, as this is called */
  898. char *filenm;                /* only with packet as the arg! */
  899. {
  900.   if (filenm[0] == '\0')
  901.     fd = creat(packet,0644);        /* If filename known, use it */
  902.   else
  903.     fd = creat(filenm,0644);        /* else use sourcefile name */
  904.   return (fd > 0);            /* Return false if file won't open */
  905. }
  906.  
  907. /*
  908.  *    g n x t f l
  909.  *
  910.  *    Get next file in a file group
  911.  */
  912.  
  913. gnxtfl()
  914. {
  915.   if (debug) fprintf(stderr, "gnxtfl\n");
  916.   filnam = *(filelist++);
  917.   if (filnam == 0) return FALSE;    /* If no more, fail */
  918.   else return TRUE;            /* else succeed */
  919. }
  920.  
  921. /*
  922.  *    s p a r
  923.  *
  924.  *    Fill the data array with my send-init parameters
  925.  *
  926.  */
  927.  
  928. spar(data)
  929. char data[];
  930. {
  931.   data[0] = tochar(MAXPACK);        /* Biggest packet I can receive */
  932.   data[1] = tochar(MYTIME);        /* When I want to be timed out */
  933.   data[2] = tochar(MYPAD);        /* How much padding I need */
  934.   data[3] = ctl(MYPCHAR);        /* Padding character I want */
  935.   data[4] = tochar(MYEOL);        /* End-Of-Line character I want */
  936.   data[5] = MYQUOTE;            /* Control-Quote character I send */
  937. }
  938.  
  939.  
  940. /*    r p a r
  941.  *
  942.  *    Get the other host's send-init parameters
  943.  *
  944.  */
  945.  
  946. rpar(data)
  947. char data[];
  948. {
  949.   spsiz = unchar(data[0]);        /* Maximum send packet size */
  950.   timint = unchar(data[1]);        /* When I should time out */
  951.   pad = unchar(data[2]);        /* Number of pads to send */
  952.   padchar = ctl(data[3]);        /* Padding character to send */
  953.   eol = unchar(data[4]);        /* EOL character I must send */
  954.   quote = data[5];            /* Incoming data quote character */
  955. }
  956.  
  957. #if UNIX
  958. flushinput()
  959. {
  960.   long int count;            /* Number of bytes ready to read */
  961.   long int i;                /* Number of bytes to read in loop */
  962.  
  963.   ioctl(remfd, FIONREAD, &count);    /* See how many bytes pending read */
  964.   if (!count) return;            /* If zero, then no input to flush */
  965.  
  966.   while (count) {            /* Loop till all are flushed */
  967.       i = (count<sizeof(recpkt)) ?    /* Read min of count and size of */
  968.            count : sizeof(recpkt);    /*  the read buffer */
  969.       read(remfd, recpkt, i);        /* Read a bunch */
  970.       count -= i;            /* Subtract from amount to read */
  971.   }
  972. }
  973. #endif /* UNIX */
  974.