/*
 *	K e r m i t  File Transfer Utility
 *
 *	UNIX Kermit, Columbia University, 1981, 1982, 1983
 *      Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz
 *
 *	usage: kermit [csr][dlbe line baud escapechar] [f1 f2 ...]
 *
 *	where c=connect, s=send [files], r=receive, d=debug,
 *	l=tty line, b=baud rate, e=escape char (decimal ascii code).
 *	For "host" mode Kermit, format is either "kermit r" to
 *	receive files, or "kermit s f1 f2 ..." to send f1 .. fn.
 *
 *
 *      Fixed up again for Unix, Jim Guyton 7/13/83 (Rand Corp)
 */

#include <stdio.h>			/* Standard UNIX definitions */
#include <sgtty.h>
#include <signal.h>
#include <setjmp.h>

/* Conditional Compilation: 0 means don't compile it, nonzero means do */

#define UNIX            1       /* Conditional compilation for UNIX */
#define TOPS_20         0       /* Conditional compilation for TOPS-20 */
#define VAX_VMS		0	/* Ditto for VAX/VMS */
#define IBM_UTS		0	/* Ditto for Amdahl UTS on IBM systems */


/* Symbol Definitions */

#define MAXPACK		94	/* Maximum packet size */
#define SOH		1	/* Start of header */
#define SP		32	/* ASCII space */
#define CR              015	/* ASCII Carriage Return */
#define DEL		127	/* Delete (rubout) */
#define CTRLD		4
#define BRKCHR		CTRLD	/* Default escape character for CONNECT */

#define MAXTRY		5	/* Times to retry a packet */
#define MYQUOTE		'#'	/* Quote character I will use */
#define MYPAD		0	/* Number of padding characters I will need */
#define MYPCHAR		0	/* Padding character I need */
#define MYEOL		'\n'	/* End-Of-Line character I need */
#define MYTIME		5	/* Seconds after which I should be timed out */
#define MAXTIM		20	/* Maximum timeout interval */
#define MINTIM		2	/* Minumum timeout interval */

#define TRUE		-1	/* Boolean constants */
#define FALSE		0


/* Global Variables */

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,			/* Timeout for foreign host on sends */
	numtry,			/* Times this packet retried */
	oldtry,			/* Times previous packet retried */
	fd,			/* File pointer of file to read/write */
	remfd,			/* File pointer of the host's tty */
	image,			/* -1 means 8-bit mode */
	remspd,			/* Speed of this tty */
	host,			/* -1 means we're a host-mode kermit */
	debug;			/* -1 means debugging */

char	state,			/* Present state of the automaton */
	padchar,		/* Padding character to send */
	eol,			/* End-Of-Line character to send */
	escchr,			/* Connect command escape character */
	quote,			/* Quote character in incoming data */
	**filelist,		/* List of files to be sent */
	*filnam,		/* Current file name */
	recpkt[MAXPACK],	/* Receive packet buffer */
	packet[MAXPACK];	/* Packet buffer */

struct sgttyb
	rawmode,		/* Host tty "raw" mode */
	cookedmode,		/* Host tty "normal" mode */
	remttymode;		/* Assigned tty line "raw" mode */

jmp_buf	env;			/* Environment ptr for timeout longjump */


/*
 *      m a i n
 *
 *      Main routine - parse command and options, set up the
 *      tty lines, and dispatch to the appropriate routine.
 */

main(argc,argv)
int argc;				/* Character pointer for */
char **argv;				/*  command line arguments */
{
  char *remtty,*cp;			/* tty for CONNECT, char pointer */
  int speed, cflg, rflg, sflg;		/* speed of assigned tty, */
					/* flags for CONNECT, RECEIVE, SEND */
  if (argc < 2) usage();		/* Make sure there's a command line. */

  cp = *++argv; argv++; argc -= 2;	/* Set up pointers to args */

/* Initialize this side's SEND-INIT parameters */

  eol = CR;				/* EOL for outgoing packets */
  quote = MYQUOTE;			/* Standard control-quote char "#" */
  pad = 0;				/* No padding */
  padchar = NULL;			/* Use null if any padding wanted */

  speed = cflg = sflg = rflg = 0;	/* Turn off all parse flags */
  remtty = 0;				/* Default is host (remote) mode */
  image = FALSE;                        /* Default to 7-bit mode */
  escchr = BRKCHR;			/* Default escape character */
 
  while ((*cp) != NULL)			/* Get a character from the cmd line */
    switch (*cp++)			/* Based on what the character is, */
     {					/*  do one of the folloing */
      case '-': break;			/* Ignore dash (UNIX style) */
      case 'c': cflg++; break;		/* C = CONNECT command */
      case 's': sflg++; break;		/* S = SEND command */
      case 'r': rflg++; break;		/* R = RECEIVE command */
      case 'e': if (argc--)		/* E = specify escape char */
                  escchr = atoi(*argv++); /*  as ascii decimal number */
                else usage();
                if (debug) fprintf(stderr,"escape char is ascii %d\n",escchr);
                break;
      case 'l': if (argc--)		/* L = specify tty line to use */
                  remtty = *argv++;
                else usage(); 
                if (debug) fprintf(stderr,"line %s\n",remtty); 
                break;
#if UNIX				/* This part only for UNIX systems */
      case 'b': if (argc--) speed = atoi(*argv++); /* Set baud rate */
                  else usage();
                if (debug) fprintf(stderr,"speed %d\n",speed); break;
 
      case 'i': image = TRUE; break;	/* Image (8-bit) mode */
#endif /* UNIX */

      case 'd': debug = TRUE; break;	/* Debug mode */
     }

/* Done parsing */

  if ((cflg+sflg+rflg) != 1) usage();	/* Only one command allowed */

  remfd = 0;				/* Start out as a host (remote) */
  host = TRUE;

  if (remtty)				/* If another tty was specified, */
   {
    remfd = open(remtty,2);		/*  open it */
    if (remfd < 0)			/*  check for failure */
     {
      fprintf(stderr,"Kermit: cannot open %s\n",remtty);
      exit(-1);				/*  Failed, quit. */
     }
    host = FALSE;			/* Opened OK, flag local (not host) */
   }
  
/* Put the tty(s) into the correct modes */

  gtty(0,&cookedmode);			/* Save current mode for later */
  gtty(0,&rawmode);
  rawmode.sg_flags |= (RAW|TANDEM);
  rawmode.sg_flags &= ~(ECHO|CRMOD);

  gtty(remfd,&remttymode);		/* If local kermit, get mode of */
					/*  assigned tty */ 
  remttymode.sg_flags |= (RAW|TANDEM);
  remttymode.sg_flags &= ~(ECHO|CRMOD);

#if UNIX				/* Speed changing for UNIX only */
  if (speed)				/* User specified a speed? */
   {
    switch(speed)			/* Get internal system code */
     {
        case 110: speed = B110; break;
        case 150: speed = B150; break;
        case 300: speed = B300; break;
        case 1200: speed = B1200; break;
        case 2400: speed = B2400; break;
        case 4800: speed = B4800; break;
        case 9600: speed = B9600; break; 
        default: fprintf(stderr,"bad line speed\n");
     }
    remttymode.sg_ispeed = speed;
    remttymode.sg_ospeed = speed;
   }
#endif /* UNIX */

  if (remfd) stty(remfd,&remttymode);	/* Put asg'd tty in raw mode */


/* All set up, now execute the command that was given. */

  if (cflg) connect();                  /* CONNECT command */

  if (sflg)                             /* SEND command */ 
   {
    if (argc--) filnam = *argv++;	/* Get file to send */
      else usage();
    filelist = argv;
    if (host) stty(0,&rawmode);		/* Put tty in raw mode if remote */
    if (sendsw() == FALSE)		/* Send the file(s) */
      printf("Send failed.\n");		/* Report failure */
    else				/*  or */
      printf("OK\n");			/* success */
    if (host) stty(0,&cookedmode);	/* Restore tty */
   }

  if (rflg)                             /* RECEIVE command */
   {
    if (host) stty(0,&rawmode);		/* Put tty in raw mode if remote */
    if (recsw() == FALSE)		/* Receive the file */
      printf("Receive failed.\n");	/* Report failure */
    else				/*  or */
      printf("OK\n");			/* success */
    if (host) stty(0,&cookedmode);	/* Restore tty */
   }
 }

usage()					/* Give message if user makes */
{					/* a mistake in the command */
  fprintf(stderr,
     "usage: kermit [csr][di][lbe] [line] [baud] [esc char] [f1 f2 ...]\n");
  exit();
}

/*
 *	s e n d s w
 *
 *	Sendsw is the state table switcher for sending
 *	files.  It loops until either it finishes, or
 *	an error is encountered.  The routines called by
 *	sendsw are responsible for changing the state.
 *
 */

sendsw()
{
 char sinit(),sfile(),seof(),sdata(),sbreak();

 state = 'S';				/* Send initiate is the start state */
 n = 0;					/* Initialize message number */
 numtry = 0;				/* Say no tries yet */
 while(TRUE)				/* Do this as long as necessary */
  {
   switch(state)
    {
     case 'D':	state = sdata();  break; /* Data-Send state */
     case 'F':	state = sfile();  break; /* File-Send */
     case 'Z':	state = seof();   break; /* End-of-File */
     case 'S':	state = sinit();  break; /* Send-Init */
     case 'B':	state = sbreak(); break; /* Break-Send */
     case 'C':	return (TRUE); 	         /* Complete */
     case 'A':	return (FALSE);		 /* "Abort" */
     default:	return (FALSE);		 /* Unknown, fail */
    }
  }
}


/*
 *	s i n i t
 *
 *	Send Initiate: Send my parameters, get other side's back.
 */

char sinit()
{
  int num, len;				/* Packet number, length */

  if (debug) fprintf(stderr,"sinit\n");
  if (numtry++ > MAXTRY) return('A');	/* If too many tries, give up */
  spar(packet);				/* Fill up with init info */
  if (debug) fprintf(stderr,"n = %d\n",n);

#if UNIX
  flushinput();                         /* Flush pending input */
#endif /* UNIX */

  spack('S',n,6,packet);		/* Send an S packet */
  switch(rpack(&len,&num,recpkt))	/* What was the reply? */
   {
    case 'N':  return(state);  		/* NAK */

    case 'Y':				/* ACK */
      if (n != num) return(state);	/* If wrong ACK, stay in S state */
      rpar(recpkt);			/* Get other side's init info */
      if (eol == 0) eol = '\n';		/* Check and set defaults */
      if (quote == 0) quote = '#';	/* Control-prefix quote */
      numtry = 0;			/* Reset try counter */
      n = (n+1)%64;			/* Bump packet count */
      if (debug) fprintf(stderr,"Opening %s\n",filnam);
      fd = open(filnam,0);		/* Open the file to be sent */
      if (fd < 0) return('A');		/* if bad file descriptor, give up */
      if (!host) printf("Sending %s\n",filnam);
      return('F');			/* OK, switch state to F */

    case FALSE: return(state);		/* Receive failure, stay in S state */
    default: return('A');		/* Anythig else, just "abort" */
   }
 }


/*
 *	s f i l e
 *
 *	Send File Header.
 */

char sfile()
{
  int num, len;				/* Packet number, length */

  if (debug) fprintf(stderr,"sfile\n");

  if (numtry++ > MAXTRY) return('A');	/* If too many tries, give up */
  for (len=0; filnam[len] != '\0'; len++); /* Add up the length */

  spack('F',n,len,filnam); 		/* Send an F packet */
  switch(rpack(&len,&num,recpkt))	/* What was the reply? */
   {					
    case 'N':				/* NAK, just stay in this state, */
      if (n != (num=(--num<0)?63:num))	/*  unless NAK for next packet, */
        return(state);			/*  which is just like an ACK */
					/*  for this packet, fall thru to... */
    case 'Y':				/* ACK */
      if (n != num) return(state); 	/* If wrong ACK, stay in F state */
      numtry = 0; 			/* Reset try counter */
      n = (n+1)%64; 			/* Bump packet count */
      size = bufill(packet); 		/* Get first data from file */
      return('D');			/* Switch state to D */

    case FALSE: return(state); 		/* Receive failure, stay in F state */
    default:    return('A'); 		/* Something esle, just "abort" */
   }
}


/*
 *	s d a t a
 *
 *	Send File Data
 */

char sdata()
{
  int num, len;				/* Packet number, length */

  if (numtry++ > MAXTRY) return('A');	/* If too many tries, give up */
  spack('D',n,size,packet);		/* Send a D packet */

  switch(rpack(&len,&num,recpkt))	/* What was the reply? */
   {				
    case 'N':				/* NAK, just stay in this state, */
      if (n != (num=(--num<0)?63:num))	/*  unless NAK for next packet, */
        return(state);			/*  which is just like an ACK */
					/*  for this packet, fall thru to... */
    case 'Y':				/* ACK */
      if (n != num) return(state);	/* If wrong ACK, fail */
      numtry = 0;			/* Reset try counter */
      n = (n+1)%64;			/* Bump packet count */
      if ((size = bufill(packet)) == EOF) /* Get data from file */
        return('Z');			/* If EOF set state to that */
      return('D');			/* Got data, stay in state D */

    case FALSE: return(state);		/* Receive failure, stay in D */
    default:	return('A');		/* Anything else, "abort" */
   }
}


/*
 *	s e o f
 *
 *	Send End-Of-File.
 */

char seof()
{
  int num, len;				/* Packet number, length */
  if (debug) fprintf(stderr,"seof\n");
  if (numtry++ > MAXTRY) return('A');	/* If too many tries, "abort" */
  spack('Z',n,0,packet);		/* Send a 'Z' packet */
  if (debug) fprintf(stderr,"seof1 ");
  switch(rpack(&len,&num,recpkt))	/* What was the reply? */
   {
    case 'N':				/* NAK, fail */
      if (n != (num=(--num<0)?63:num))	/* ...unless for previous packet, */
        return(state);			/*  in which case, fall thru to ... */
    case 'Y':				/* ACK */
      if (debug) fprintf(stderr,"seof2 ");
      if (n != num) return(state);	/* If wrong ACK, hold out */
      numtry = 0; 			/* Reset try counter */
      n = (n+1)%64; 			/* and bump packet count */
      if (debug) fprintf(stderr,"closing %s, ",filnam);
      close(fd);			/* Close the input file */
      if (debug) fprintf(stderr,"ok, getting next file\n");
      if (gnxtfl() == FALSE)		/* No more files go? */
        return('B');			/* if not, break, EOT, all done */
      if (debug) fprintf(stderr,"new file is %s\n",filnam);
      return('F');			/* More files, switch state to F */

    case FALSE: return(state); 		/* Receive failure, stay in state Z */
    default:	return('A'); 		/* Something else, "abort" */
   }
}


/*
 *	s b r e a k
 *
 *	Send Break (EOT)
 */

char sbreak()
{
  int num, len;				/* Packet number, length */
  if (debug) fprintf(stderr,"sbreak\n");
  if (numtry++ > MAXTRY) return('A');	/* If too many tries "abort" */

  spack('B',n,0,packet);		/* Send a B packet */
  switch (rpack(&len,&num,recpkt))	/* What was the reply? */
   {
    case 'N':				/* NAK, fail */
      if (n != (num=(--num<0)?63:num))	/* ...unless for previous packet, */
        return(state);			/*  in which case, fall thru to ... */

    case 'Y':				/* ACK */
      if (n != num) return(state);	/* If wrong ACK, fail */

      numtry = 0;			/* Reset try counter */
      n = (n+1)%64;			/* and bump packet count */
      return('C');			/* switch state to Complete */

    case FALSE: return(state);		/* Receive failure, stay in state B */
    default:	return ('A');		/* Other, "abort" */
   }
}


/*
 *      r e c s w
 *
 *	This is the state table switcher for receiving files.
 */

recsw()
{
  char rinit(),rdata(),rfile();		/* Use these procedures */

  state = 'R';                          /* Receive is the start state */
  n = 0;                                /* Initialize message number */
  numtry = 0;                           /* Say no tries yet */

  while(TRUE) switch(state)             /* Do until done */
   {
    case 'D':   state = rdata(); break; /* Data receive state */
    case 'F':   state = rfile(); break; /* File receive state */
    case 'R':   state = rinit(); break; /* Send initiate state */
    case 'C':   return(TRUE);           /* Complete state */
    case 'A':   return(FALSE);          /* "Abort" state */
    }
  }
    
/*
 *      r i n i t
 *
 *	Receive Initialization
 */
  
char rinit()
{
  int len, num;				/* Packet length, number */

  if (numtry++ > MAXTRY) return('A');   /* If too many tries, "abort" */
  switch(rpack(&len,&num,packet))       /* Get a packet */
  {
   case 'S':                            /* Send-Init */
     rpar(packet);                      /* Get the other side's init data */
     spar(packet);                      /* Fill up packet with my init info */
     spack('Y',n,6,packet);             /* ACK with my parameters */
     oldtry = numtry;                   /* Save old try count */
     numtry = 0;                        /* Start a new counter */
     n = (n+1)%64;                      /* Bump packet number, mod 64 */
     return('F');                       /* Enter File-Send state */

   case FALSE:  return (state);         /* Didn't get a packet, keep waiting */
   default:     return('A');            /* Some other packet type, "abort" */
  }
}


/*
 *      r f i l e
 *
 *	Receive File Header
 */

char rfile()
{
 int num, len;				/* Packet number, length */

 if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
 switch(rpack(&len,&num,packet))        /* Get a packet */
  {
   case 'S':                            /* Send-Init, maybe our ACK lost */
     if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
     if (num == ((n==0)?63:n-1))	/* Previous packet, mod 64? */
      {					/* Yes, ACK it again */
       spar(packet);                    /* with our Send-Init parameters */
       spack('Y',num,6,packet);         /*  ... */
       numtry = 0;                      /* Reset try counter */
       return(state);			/* Stay in this state */
      }
     else return('A');                  /* Not previous packet, "abort" */

   case 'Z':                            /* End-Of-File */
     if (oldtry++ > MAXTRY) return('A');
     if (num == ((n==0)?63:n-1))	/* Previous packet, mod 64? */
      {					/* Yes, ACK it again. */
       spack('Y',num,0,0);
       numtry = 0;
       return(state);			/* Stay in this state */
      }
     else return('A');                  /* Not previous packet, "abort" */

   case 'F':                            /* File Header, */
     if (num != n) return('A');		/* which is what we really want */
					/* The packet number must be right */
     if (!getfil(packet))		/* Try to open a new file */
      {
       fprintf(stderr,"Could not create %s\n");	/* Give up if can't */
       return('A');
      }
     else				/* OK, give message */
       if (!host) printf("Receiving %s\n",packet);
     spack('Y',n,0,0);                  /* Acknowledge the file header */
     oldtry = numtry;			/* Reset try counters */
     numtry = 0;			/* ... */
     n = (n+1)%64;			/* Bump packet number, mod 64 */
     return('D');                       /* Switch to Data state */

   case 'B':                            /* Break transmission (EOT) */
     if (num != n) return ('A');	/* Need right packet number here */
     spack('Y',n,0,0);                  /* Say OK */
     return('C');                       /* Go to complete state */

   case FALSE: return(state);		/* Couldn't get packet, keep trying */
   default:	return ('A');		/* Some other packet, "abort" */
  }
}


/*
 *      r d a t a
 *
 *	Receive Data
 */

char rdata()
{
 int num, len;				/* Packet number, length */

 if (numtry++ > MAXTRY) return('A');    /* "abort" if too many tries */
 switch(rpack(&len,&num,packet))        /* Get packet */
  {
   case 'D':                            /* Got Data packet */
     if (num != n)                      /* Right packet? */
      {                                 /* No */
       if (oldtry++ > MAXTRY) return('A'); /* If too many tries, give up */
       if (num == ((n==0)?63:n-1))	/* Else check packet number */
        {				/* Previous packet again? */
         spack('Y',num,6,packet);       /* Yes, re-ACK it */
         numtry = 0;                    /* Reset try counter */
         return(state);			/* Stay in D, don't write out data! */
        }
       else return('A');                /* sorry wrong number */
      }
                                        /* Got data with right packet number */
     bufemp(packet,fd,len);             /* Write the data to the file */
     spack('Y',n,0,0);                  /* Acknowledge the packet */
     oldtry = numtry;			/* Reset the try counters */
     numtry = 0;			/* ... */
     n = (n+1)%64;			/* Bump packet number, mod 64 */
     return('D');                       /* Remain in data state */

   case 'F':                            /* Got a File Header */
     if (oldtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
     if (num == ((n==0)?63:n-1))	/* Else check packet number */
      {					/* It was the previous one */
       spack('Y',num,0,0);		/* ACK it again */
       numtry = 0;			/* Reset try counter */
       return(state);			/* Stay in Data state */
      }
     else return('A');                  /* Not previous packet, "abort" */

   case 'Z':                            /* End-Of-File */
     if (num != n) return('A');		/* Must have right packet number */
     spack('Y',n,0,0);                  /* OK, ACK it. */
     close(fd);                         /* Close the file */
     n = (n+1)%64;			/* Bump packet number */
     return('F');                       /* Go back to Receive File state */

   case FALSE:  return(state);		/* No packet came, keep waiting */
   default:     return('A');		/* Some other packet, "abort" */
  }
}

/*
 *      c o n n e c t
 *
 *      Establish a virtual terminal connection with the remote host, over an
 *      assigned tty line. 
 *
 */

connect()
{
  int parent;				/* Fork handle */
  char c = NULL, r = '\r';

  if (host)		/* If in host mode, nothing to connect to */
   {
    fprintf(stderr,"Kermit: nothing to connect to\n");
    return;
   }

  parent = fork();      /* Start fork to get typeout from remote host */

  if (parent)           /* Parent passes typein to remote host */
   {
    printf("Kermit: connected.\r\n");	/* Give message */
    stty(0,&rawmode);			/* Put tty in raw mode */
    read(0,&c,1);			/* Get a character */
    while ((c&0177) != escchr)          /* Check for escape character */
     {					/* Not it */
      write(remfd,&c,1);		/* Write the character on screen */
      c = NULL;				/* Nullify it */
      read(0,&c,1);			/* Get next character */
     }					/* Until escape character typed */
    kill(parent,9);			/* Done, get rid of fork */
    stty(0,&cookedmode);		/* Restore tty mode */
    printf("\nKermit: disconnected.\n"); /* Give message */
    return;				/* Done */
   }
  else                  /* Child does the reading from the remote host */
   {
    while(1)				/* Do this forever */
     {
      read(remfd,&c,1);
      write(1,&c,1);
     }
   }
}

/*
 *      KERMIT utilities.
 */

clkint()                                /* Timer interrupt handler */
{
 longjmp(env,TRUE);                     /* Tell rpack to give up */
}

/* tochar converts a control character to a printable one by adding a space */

char tochar(ch)
char ch;
{
  return(ch + ' ');             /* make sure not a control char */
}


/* unchar undoes tochar */

char unchar(ch)
char ch;
{
  return(ch - ' ');             /* restore char */
}


/*
 * ctl turns a control character into a printable character by toggling the
 * control bit (ie. ^A becomes A and A becomes ^A).
 */

char ctl(ch)
char ch;
{
 return(ch ^ 64);               /* toggle the control bit */
}


/*
 *      s p a c k
 *
 *	Send a Packet
 */

spack(type,num,len,data)
char type, *data;
int num, len;
{
  int i;				/* Character loop counter */
  char chksum, buffer[100];		/* Checksum, packet buffer */
  register char *bufp;			/* Buffer pointer */
  
  bufp = buffer;			/* Set up buffer pointer */
  for (i=1; i<=pad; i++) write(remfd,&padchar,1); /* Issue any padding */

  *bufp++ = SOH;                        /* Packet marker, ASCII 1 (SOH) */
  chksum = tochar(len+3);               /* Initialize the checksum */
  *bufp++ = tochar(len+3);              /* Send the character count */
  chksum = chksum + tochar(num);        /* Init checksum */
  *bufp++ = tochar(num);                /* Packet number */
  chksum = chksum + type;		/* Accumulate checksum */
  *bufp++ = type;                       /* Packet type */

  for (i=0; i<len; i++)			/* Loop for all data characters */
   {
    *bufp++ = data[i];			/* Get a character */
    chksum = chksum+data[i];		/* Accumulate checksum */
   }
  chksum = (chksum + (chksum & 192) / 64) & 63;	/* Compute final checksum */
  *bufp++ = tochar(chksum);             /* Put it in the packet */
  *bufp = eol;                          /* Extra-packet line terminator */
  write(remfd, buffer,bufp-buffer+1);   /* Send the packet */
  if (debug) putc('.',stderr);
}

/*
 *  r p a c k
 *
 *  Read a Packet
 *
 */

rpack(len,num,data)
int *len, *num;				/* Packet length, number */
char *data;				/* Packet data */
{
 int i, done;				/* Data character number, loop exit */
 char chksum, t, type;			/* Checksum, current char, pkt type */
 
#if UNIX				/* TOPS-20 can't handle timeouts... */
 if (setjmp(env)) return FALSE;		/* Timed out, fail */
 signal(SIGALRM,clkint);		/* Setup the timeout */
 if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
 alarm(timint);

 flushinput();                          /* Flush any pending input */
#endif /* UNIX */

 while (t != SOH)                       /* Wait for packet header */
  {