#include "ufk.h"
#include <pwd.h>
#include <sgtty.h>
#include <signal.h>
#include <setjmp.h>
#include <stat.h>

/*
 * split command line into seperate strings
 */

split(cmd_str)
char  *cmd_str;
{
   char  *malloc();
   char  pass,
         *p,
         *q,
         result[128];

   numprm = 0;                          /* no parameters yet */
   for (pass = 0; pass < 2; pass++)     /* find # of params in pass 1 */
   {                                    /* alloc pointer array and store */
      if (pass > 0)                     /* pointers in pass 2 */
      {
         if ((params = malloc(numprm * sizeof(char *))) == NULL)
         {               /* allocate space for pointer array to parameters */
            prterr(ER_NOMEM);
            kerm_exit();
         }
         numprm = 0;                    /* restart scan */
      }
      p = cmd_str;                      /* setup pointer to input string */
      while (*p && (*p != COMMENT))     /* do till end of string */
      {
         while (isspace(*p))              /* strip leading white space */
            p++;
         if (pass > 0)
         {
            sscanf(p, "%s", result);      /* get part of input string */
            if ((q = malloc(strlen(result) + 1)) == NULL) /* get str space */
            {
               prterr(ER_NOMEM);
               kerm_exit();
            }
            strcpy(q, result);            /* copy part to allocated memory */
            params[numprm] = q;           /* save pointer to string */
         }
         numprm++;                        /* one more */
         while ((!isspace(*p)) && *p)     /* skip to end of this string */
            p++;
      }
   }
   return (TRUE);
}

/*
 * parse command components
 */

parse(command_string, table)
char *command_string;
TABLE table[];
{
   char  i = 0,
         j;
   int   mstat,
         cmd_num = 0,
         tv = 0,
         unique = FALSE;

   make_lower(command_string);         /* convert to lower case */
   while (*table[i].string)            /* do until end of command table */
   {
      mstat = match(table[i].string, command_string); /* do compare */
      if (mstat == -1)
         return (table[i].funcadr);    /* exact match found */
      if (mstat != 0)                  /* some characters did match */
      {
         if (mstat > tv)               /* longer than so far ? */
         {
            tv = mstat;                /* save longest length */
            cmd_num = i;               /* remember which command */
            unique = TRUE;             /* it's unique so far */
         }
         j = i;
         while (*table[++j].string)    /* start new search from next entry */
         {
            mstat = match(table[j].string, command_string); /* other match ? */
            if (mstat == -1)
               return (table[j].funcadr); /* exact match */
            if (mstat != 0)            /* some characters did match */
            {
               if (mstat > tv)         /* longer than so far ? */
               {
                  tv = mstat;          /* save greatest length */
                  cmd_num = i;         /* remember which command */
                  unique = TRUE;       /* it's unique so far */
               }
               if (mstat == tv)        /* another one of the same length ? */
                  unique = FALSE;      /* yes, it's not unique anymore */
            }
         }
         if (!unique)                  /* if it was'nt unique it's ambiguous */
            return (NULL);
         else
            return (table[cmd_num].funcadr); /* else it's a valid command */
      }
      i++;                             /* try next entry */
   }
   if (cmd_num == 0)
      return (ERROR);                  /* no such command */
   else return (table[cmd_num].funcadr); /* return command number */
}

match(s, t)
char *s, *t;
{
   int   len = 0,
         inputlen;

   inputlen = strlen(t);               /* length of input command */
   while (*s)                          /* do until end of string */
   {
      if (*s++ != *t++)                /* if no match */
      {
         if (inputlen == 0)            /* total command checked */
            return (len);              /* return matched length */
         else
            return (0);                /* no match for this command */
      }
      len++;                           /* one more character done */
      --inputlen;                      /* count down input string length */
   }
   return (-1);                        /* full match found */
}

/*
 *  m a k e _ u p p e r
 *
 *  Convert string to upper case
 */

make_upper(p)
char  *p;
{
   while (*p)
   {
      if (islower(*p))
         *p = toupper(*p);
      p++;
   }
}

/*
 *  m a k e _ l o w e r
 *
 *  Convert string to lower case
 */

make_lower(p)
char *p;
{
   while (*p)
   {
      if (isupper(*p))
         *p = tolower(*p);
      p++;
   }
}

/*
 *  p r e r r p k t
 *
 *  Print contents of error packet received from remote host.
 */
prerrpkt(msg)
char  *msg;
{
   if (!nooutput)
   {
      disp(0,19,"Kermit aborting with following error from remote host:");
      foreground();
      disp(0,20,msg);
      background();
      beep();
   }
}

/*
 * send ASCII file to remote without protocol.
 * handshake is used.
 */

jmp_buf trn_env;

trnsmit()
{
   FILE *xmitfp;
   char c1;
   int  c, trnsm_int();
   struct sgttyb mode;

   if (numprm <= 1)                       /* check for parameters */
      prterr(ER_FSPCREQ);
   else
   {
      if ((xmitfp = fopen(params[1], "r")) == NULL)
         prterr(ER_OPENERR);
      else
      {
         if (!open_port(FALSE,FALSE))
            prterr(ER_POPNERR);
         else
         {
            signal(SIGINT,trnsm_int);
            if (!setjmp(trn_env))
               while ((c = getc(xmitfp)) != EOF) /* get character from file */
               {
                  if (debug)
                     putc(c, stdout);         /* send to console as well */
                  gtty(ttyfd,&mode);
                  if (mode.sg_speed & INCHR)
                  {
                     read(ttyfd,&c1,1);       /* Check received character */
                     if ((c1 & 0177) == dstop && dstop != 0)/* Stop chr ? */
                        do                    /* Yes */
                           read(ttyfd,&c1,1); /* Wait for next character */
                        while ((c1 & 0177) != dstart);/* Wait for start chr */
                  }
                  c1 = c;       /* Only high byte of int would be send (=0) */
                  write(ttyfd,&c1,1);         /* Send to output port */
               }
            else
               if (debug)
                  fputs("\n",stdout);
            fclose(xmitfp);
            kdelay(1);                      /* Give the port time to finish */
            close_port(FALSE,FALSE);
         }
      }
   }
}

trnsm_int()
{
   longjmp(trn_env,TRUE);               /* Transmit interrupted... */
}


/*
 * Read KERMIT commands from file
 */

take(fspec,echo)
char *fspec, echo;
{
   FILE *tfp;
   char  data[133],                       /* Command input */
         fname[80];

   if (fspec == NULL)
   {
      if (numprm > 1)
         strcpy(fname, params[1]);
      else
         return(prterr(ER_FSPCREQ));
   }
   else
      strcpy(fname,fspec);

   if ((tfp = fopen(fname,"r")) != NULL)    /* Look for command file */
   {                                        /* Found file, read commands */
      while (fgets(data,132,tfp))
      {
         if (echo)
         {
            fputs(prompt,stdout);           /* Echo prompt */
            fputs(data,stdout);             /* Echo command */
         }
         data[strlen(data) - 1] = '\0';     /* Zap newline */
         if (data[0] && data[0] != COMMENT) /* Not a blank line */
            kerm_command(data);             /* Do command */
      }
      fclose(tfp);
   }
   else
   {
      if (fspec == NULL)
         return(prterr(ER_OPENERR));
      else
         return(ER_OPENERR);
   }
   return(NULL);
}

/*
 * call UniFLEX to perform external command
 */

do_uniflex()
{
   static char *shell = { "/bin/shell" };
   static char *opt = { "+xc" };
   char *prm[4],
        *p,
        cmdline[256];
   int i,
       kerm_exit();

   prm[0] = &shell[5];                  /* setup program name */
   prm[1] = prm[3] = 0;                 /* end of parameters pointer */
   if (numprm > 1)
   {
      prm[1] = opt;                     /* one shot option */
      prm[2] = p = cmdline;             /* pointers to start of commandline */
      for (i = 1; i < numprm; i++)
      {
         strcpy(p,params[i]);           /* copy params into one line */
         p += strlen(p);                /* point after command */
         *p++ = ' ';                    /* separator */
      }
      *p = '\0';                        /* end of string */
   }
   if (!fork())                         /* duplicate myself */
   {
      execvp(shell,prm);                /* execute command in child */
      prterr(ER_SPAWN);                 /* should not come here */
   }
   else
   {
      signal(SIGTERM,SIG_IGN);          /* Ignore this signals */
      signal(SIGHUP,SIG_IGN);
      signal(SIGQUIT,SIG_IGN);
      signal(SIGINT,SIG_IGN);
      wait(0);                          /* wait for child to die */
      signal(SIGTERM,kerm_exit);        /* Set signals again */
      signal(SIGHUP,kerm_exit);
      signal(SIGQUIT,SIG_DFL);          /* Make these default */
      signal(SIGINT,SIG_DFL);
   }
}

disk_free()
{
   char dev[30];
   long size;

   if (numprm <= 1)
      strcpy(dev,".");               /* use current place if not specified */
   else
      strcpy(dev,params[1]);         /* use specified place */
   if ((size = get_free(dev)) != -1l)/* if no error... */
      printf("Number of free blocks: %ld (%ld bytes)\n",size, size * 512);
}


chd()
{
   struct passwd *p;

   if (numprm <= 1)
   {
      if ((p = getpwuid(getuid())) == NULL)
         prterr(ER_GETPWDID);
      chdir(p->pw_dir);
      set_dir(p->pw_dir);
   }
   else
   {
      if (chdir(params[1]) == ERROR)
         prterr(ER_INVDIR);
      else
         set_dir(params[1]);
   }
}


/*
 * show statistics about connection
 */

statistics()
{
   static char s1[] = "Characters sent:\t\t%ld\n";
   static char s2[] = "Data characters sent:\t\t%ld\n";
   static char s3[] = "NAK's sent:\t\t\t%d\n";
   static char s4[] = "Packets sent:\t\t\t%d\n";
   static char s5[] = "Characters received:\t\t%ld\n";
   static char s6[] = "Data characters received:\t%ld\n";
   static char s7[] = "NAK's received:\t\t\t%d\n";
   static char s8[] = "Packets received:\t\t%d\n";

   printf("Totals for the last transfer:\n\n");
   printf(s1, chr_sent);
   printf(s2, dchr_sent);
   printf(s3, nak_sent);
   printf(s4, pack_sent);
   printf(s5, chr_rec);
   printf(s6, dchr_rec);
   printf(s7, nak_rec);
   printf(s8, pack_rec);
   printf("Effective data rate:\t\t%d baud\n", data_rate);
   printf("\nTotals since KERMIT was started:\n\n");
   printf(s1, t_chr_sent);
   printf(s2, t_dchr_sent);
   printf(s3, t_nak_sent);
   printf(s4, t_pack_sent);
   printf(s5, t_chr_rec);
   printf(s6, t_dchr_rec);
   printf(s7, t_nak_rec);
   printf(s8, t_pack_rec);
   printf("Mean effective data rate:\t%d baud\n", t_data_rate);
}

#define  POLYIT  0x8408       /* Polynomial for CRC-CCITT */
#define  POLY16  0xa001       /* Polynomial for CRC-16 (not used here) */

unsigned int crc_value, crc[256];    /* Global accessible variables */

init_crc_table()
{
   int count, nr;
   unsigned int accum, crcch;

   for (count = 0; count < 256; count++)     /* Build CRC lookup table */
   {
      accum = 0;
      crcch = count;
      for (nr = 0; nr < 8; nr++)
      {
         if (((accum & 0xff) ^ crcch) & 1)
            accum = (accum / 2) ^ POLYIT;
         else
            accum = (accum / 2);
         crcch = (crcch / 2);
      }
      crc[count] = accum;                 /* Save calculated value in table */
   }
}

zero_crc()
{
   crc_value = 0;                         /* Initial value is zero */
}

unsigned int calc_crc(value)
unsigned char value;
{
   crc_value = crc[value ^ (crc_value & 0xff)] ^ ((crc_value >> 8) & 0xff);
   return crc_value;                      /* Return accumulated value so far */
}

beep()
{
   if (!remote && !nooutput)
      putc(BELL,stdout);
}

disp(x,y,string)
int x,y;
char *string;
{
   if (!remote)
   {
      posit(x,y);
      fputs(string,stdout);
      if (!screen && !nooutput)
         fputs("\n",stdout);
   }
}

set_frame()
{
   if (screen && !remote && !nooutput)
   {
      clear_screen();
      posit(28,0);
      foreground();
      fprintf(stdout," KERMIT version %s ",VERSION);
      background();
      if (sflg || rflg)
      {
         disp(0,4,"Transferred:");
         disp(27,4,"Percentage:");
      }
      disp(0,6,"Packet received:");
      disp(0,7,"Packet transmitted:");
      if (debug >= 1)
      {
         disp(0,9,"Data received:");
         disp(0,11,"Data transmitted:");
         disp(0,13,"Receive state:");
         disp(36,13,"Send state:");
         disp(27,6,"Type:");
         disp(27,7,"Type:");
         disp(37,6,"Length:");
         disp(37,7,"Length:");
         disp(50,6,"Checksum:");
         disp(50,7,"Checksum:");
      }
      curs_off();                       /* Turn off cursor */
   }
}

disp_state(x,y,state)
int x,y;
char state;
{
   static char sa[] = "Attribute   ";
   static char sb[] = "Break       ";
   static char sc[] = "Complete    ";
   static char sd[] = "Data        ";
   static char sf[] = "Filename    ";
   static char sq[] = "Abort       ";
   static char sr[] = "Receive init";
   static char ss[] = "Send init   ";
   static char st[] = "Timeout     ";
   static char sz[] = "End of file ";
   static char un[] = "Unknown     ";
   char dsc[5];

   if (debug == 2)
      strcpy(dsc,"%s\r");
   else
      strcpy(dsc,"%s\r\l");
   posit(x,y);
   switch(state)
   {
      case 'A':
                  prtdbg(dsc,sa);
                  break;
      case 'B':
                  prtdbg(dsc,sb);
                  break;
      case 'C':
                  prtdbg(dsc,sc);
                  break;
      case 'D':
                  prtdbg(dsc,sd);
                  break;
      case 'F':
                  prtdbg(dsc,sf);
                  break;
      case 'Q':
                  prtdbg(dsc,sq);
                  break;
      case 'R':
                  prtdbg(dsc,sr);
                  break;
      case 'S':
                  prtdbg(dsc,ss);
                  break;
      case 'T':
                  prtdbg(dsc,st);
                  break;
      case 'Z':
                  prtdbg(dsc,sz);
                  break;
      default:
                  prtdbg(dsc,un);
   }
}

prtdbg(fmt, a1, a2, a3, a4, a5, a6)
char *fmt;
{
   if ((debug >= 1) && !remote)
      fprintf(stdout,fmt,a1,a2,a3,a4,a5,a6);
   if (debug == 2)
      fprintf(dbgfil,fmt,a1,a2,a3,a4,a5,a6);
}

prtdbgf(fmt, a1, a2, a3, a4, a5, a6)
char *fmt;
{
   if (debug == 2)
      fprintf(dbgfil,fmt,a1,a2,a3,a4,a5,a6);
}

map_case(name,mode)
char *name;
int mode;
{
   if (mapping)
   {
      if (mode == IN)
         make_lower(name);
      else if (mode == OUT)
         make_upper(name);
   }
}

new_name(s)
char *s;
{
   struct stat buf;
   int num;
   char new_str[15],
        num_str[5];

   num = 0;
   do                                   /* Construct new filename */
   {                                    /* until new name not found */
      if (num++ > 4095)                 /* Bump counter */
      {
         error(PER_CREATE,"%s",new_str);/* FATAL error */
         kerm_exit();                   /* More than 4096 files ??? */
      }
      strncpy(new_str,s,10);            /* Copy 10 characters at most */
      new_str[10] = '\0';               /* Truncate at position 10 */
      sprintf(num_str,".%03x",num);     /* Format new filename */
      strcat(new_str,num_str);
   }
   while (!stat(new_str,&buf));         /* If it exists, try again */
   strcpy(s,new_str);                   /* Save new filename */
}

synerr(threshold)
int threshold;
{
   if ((threshold != 0) && (numprm >= threshold))
      return FALSE;
   prterr(ER_SYNERR);
   return TRUE;
}

check_bg()
{
   int oldsig;

   oldsig = signal(SIGINT,SIG_IGN);     /* Get old KBD signal */
   signal(SIGINT,oldsig);               /* Restore */
   return oldsig;                       /* TRUE if we're in the background */
}
