/*
    US2_C - QL-Kermit SET command

    Based on ckuus[123].c, (C) Columbia University
*/


/* Include files */

#include "flp1_ctype_h"                          /* Character type functions */
#include "flp1_fcntl_h"                          /* File opening modes */

#include "ram1_ker_h"                            /* Kermit definitions */
#include "ram1_cmd_h"                            /* Command interpreter definitions */
#include "ram1_usr_h"                            /* User command definitions */


/* External variables */

extern int slen;                                 /* Send packet size */
extern int rlen;                                 /* Receive packet size */
extern int rtmo;                                 /* Timeout we use */
extern int stmo;                                 /* Timeout remote uses */
extern int ttychid;                              /* QDOS channel ID for serial line */
extern int debug;                                /* Debugging level */
extern int trans;                                /* File name translation */
extern int speed;                                /* Line speed (baud rate) */
extern int xfertyp;                              /* File transfer type */
extern int ttyfd;                                /* FD for communication line */
extern int retry;                                /* Retry limit */
extern int delay;                                /* Delay before SEND starts */
extern int serno;                                /* Serial port in use */
extern int parity;                               /* Parity setting */
extern int npad;                                 /* How much padding remote needs */

extern bool timer;                               /* Timer enabled flag */
extern bool echo;                                /* Local echo flag */
extern bool local;                               /* Local/remote flag */
extern bool keep;                                /* Incomplete file disposition */
extern bool tkecho;                              /* Show TAKE commands flag */
extern bool tkabort;                             /* Error abort flag */

extern char ssop;                                /* Packet start remote needs */
extern char rsop;                                /* Packet start I need */
extern char reol;                                /* EOL I need */
extern char seol;                                /* EOL remote needs */
extern char quote;                               /* Control prefix */
extern char pebq;                                /* Eight-bit prefix */
extern char enter[];                             /* What ENTER sends */

extern unsigned char padch;                      /* Pad character remote needs */

extern char ttyname[];                           /* Communication device name */
extern char *dftty;                              /* Default ditto */

extern char *sourdev;                            /* Default source device */
extern char *destdev;                            /* Default destination device */
extern char *takedev;                            /* Default TAKE file device */
extern char *suffix;                             /* Sending file suffix */

extern int _oserr;                               /* QDOS error code */
extern int oserr;                                /* Copy of above */


/* External functions */

extern bool chkquote();                          /* Check for valid quote character */


/* Keyword tables */

struct keytab srtab[] =                          /* SEND/RECEIVE parameters */
{
    0,        "end-of-line",      XYEOL,
    0,        "packet-length",    XYLEN,
    CM_INV,   "start-of-packet",  XYMARK,
    0,        "marker",           XYMARK,
    0,        "timeout",          XYTIMO
};

int nsrtab = (sizeof(srtab) / sizeof(struct keytab));


struct keytab filetab[] =                        /* FILE parameters */
{
    0,        "type",             XZTYPE,
    0,        "incomplete",       XZIFD,
    0,        "name",             XZNAME,
    0,        "suffix",           XZSUFF
};

int nfiletab = (sizeof(filetab) / sizeof(struct keytab));


struct keytab devtab[] =                         /* DEVICE parameters */
{
    0,        "source",           XZSOUR,
    0,        "destination",      XZDEST,
    0,        "take-file",        XZTAKE
};

int ndevtab = (sizeof(devtab) / sizeof(struct keytab));


struct keytab fntab[4] =                         /* File name translation */
{
    0,        "normal",           FNNORM,
    CM_INV,   "translated",       FNNORM,
    0,        "untranslated",     FNUNTR,
    CM_INV,   "literal",          FNUNTR
};


struct keytab onoff[2] =                         /* On/off */
{
    0,        "off",              0,
    0,        "on",               1
};


struct keytab deblvl[] =                         /* Debugging level */
{
    0,        "off",              DBOFF,
    0,        "on",               DBON,
    0,        "full",             DBFULL
};

int ndeblvl = (sizeof(deblvl) / sizeof(struct keytab));


struct keytab ifdtab[3] =                        /* Incomplete file disposition */
{
    0,        "delete",           0,
    CM_INV,   "discard",          0,
    0,        "keep",             1
};


struct keytab fttab[3] =                         /* File transfer type */
{
    0,        "ascii",            FTASC,
    CM_INV,   "text",             FTASC,
    0,        "binary",           FTBIN
};


struct keytab ptytab[] =                         /* PARITY setting */
{
    0,        "none",             PYNONE,
    CM_INV,   "off",              PYNONE,
    0,        "even",             PYEVEN,
    0,        "odd",              PYODD,
    0,        "mark",             PYMARK,
    0,        "space",            PYSPC
};

int nptytab = (sizeof(ptytab) / sizeof(struct keytab));


struct keytab linetab[2] =                       /* LINE setting */
{
    0,        "1",                1,
    0,        "2",                2
};


struct keytab spdtab[] =                         /* Baud rate */
{
    0,        "75",               75,
    0,        "300",              300,
    0,        "600",              600,
    0,        "1200",             1200,
    0,        "2400",             2400,
    0,        "4800",             4800,
    0,        "9600",             9600
};

int nspdtab = (sizeof(spdtab) / sizeof(struct keytab));


struct keytab enttab[] =                         /* ENTER action */
{
    0,        "cr",               256*CR,
    0,        "lf",               256*LF,
    0,        "crlf",             256*CR+LF,
    0,        "lfcr",             256*LF+CR
};

int nenttab = (sizeof(enttab) / sizeof(struct keytab));


/* DOPRM - Handle the SET command

    Returns:
       -2: illegal input
       -1: reparse needed
        0: success
*/

int doprm(xx)
int xx;
{
    int x,y,z;
    char *s;

    switch (xx)
    {

case XYEOL:                                      /* These have all been moved */
case XYLEN:                                      /* to SET SEND/RECEIVE, so */
case XYMARK:                                     /* let the user know what to do */
case XYTIMO:
         x = cmcfm();
         error("Use SET SEND or SET RECEIVE");
         return(-2);

case XYENT:                                      /* ENTER action */
         if ((y = cmkey(enttab,nenttab,"cr","What ENTER sends"))<0) return(y);
         if ((x = cmcfm())<0) return(x);
         enter[0] = (y&0177400)>>8;
         enter[1] = y&0377;
         return(0);

case XYTKAB:                                     /* TAKE file abort */
         return(seton(&tkabort));

case XYTIME:                                     /* Timer status */
         return(seton(&timer));

case XYTKEC:                                     /* TAKE command echo */
         return(seton(&tkecho));

case XYDEB:                                      /* Debugging display level */
         if ((y = cmkey(deblvl,ndeblvl,"off","Debug level"))<0) return(y);
         if ((x = cmcfm())<0) return(x);
         debug = y;
         return(0);

case XYECHO:                                     /* Local echo */
         return(seton(&echo));

case XYLINE:                                     /* Communication line */
         x = cmkey(linetab,2,"","Serial port");
         if (x==-1 || x==-2) return(x);
         if ((y = cmcfm())<0) return(y);

         if (x==-3) x = -1;                      /* Nothing given, unset line */
         serno = x;                              /* Set new port number */
         return(newtty());                       /* and reopen it */

case XYPARI:                                     /* Parity */
         x = cmkey(ptytab,nptytab,"","Parity setting");
         if (x==-3)
         {
              error("Parity setting not specified");
              x = -2;
         }
         if (x<0) return(x);
         if ((y = cmcfm())<0) return(y);

         parity = x;                             /* Set new parity type */
         if (serno>0) return(newtty());          /* and re-open line if set */
         else return(0);                         /* otherwise leave it */

case XYRETR:                                     /* Retry limit */
         y = cmnum("5",&x,"Number of retries");
         return(setnum(&retry,x,y));

case XYDEL:                                      /* SEND delay */
         y = cmnum("5",&x,"Delay before SEND");
         return(setnum(&delay,x,y));

case XYPAD:                                      /* Padding */
         y = cmnum("0",&x,"Amount of padding");  /* Get how much to use */
         if (y<0) return(y);
         y = cmnum("0",&z,"Character to pad with (decimal)");
         if (y<0) return(y);                     /* Get character to use */
         if ((y = cmcfm())<0) return(y);

         npad = x;                               /* Store results */
         padch = (unsigned char) z;
         return(0);

case XYRECV:                                     /* SET SEND */
case XYSEND:                                     /* SET RECEIVE */
          y = cmkey(srtab,nsrtab,"","What to set");
          if (y==-3)
          {
              error("SEND or RECEIVE parameter not specified");
              y = -2;
          }
          if (y<0) return(y);

          switch (y)
          {

case XYEOL:   y = cmnum("0",&x,"Packet end character");
              if ((y = setcc(&z,x,y))<0) return(y);
              if (xx==XYRECV) reol = z; else seol = z;
              return(0);

case XYLEN:   y = cmnum("90",&x,"Maximum packet length");
              if ((y = setnum(&z,x,y))<0) return(y);
              if (xx==XYRECV) rlen = z; else slen = z;
              return(0);

case XYMARK:  y = cmnum("1",&x,"Packet start character");
              if ((y = setcc(&z,x,y))<0) return(y);
              if (xx==XYRECV) rsop = z; else ssop = z;
              return(0);

case XYTIMO:  if ((y = cmnum("5",&x,"Timeout in seconds"))<0) return(y);
              if ((x<1) || (x>94))
              {
                   error("Timeout must be in range 1 to 94");
                   return(-2);
              }
              if ((y = cmcfm())<0) return(y);

              if (xx==XYRECV) rtmo = x; else stmo = x;
              return(0);

default:      error("Unknown SET SEND/RECEIVE option");
              return(-2);
         }

case XYFILE:                                     /* SET FILE */
         y = cmkey(filetab,nfiletab,"","File option");
         if (y==-3)
         {
             error("FILE parameter not specified");
             y = -2;
         }
         if (y<0) return(y);

         switch (y)
         {

case XZTYPE:  if ((y = cmkey(fttab,3,"ascii","Transfer type"))<0) return(y);
              if ((x = cmcfm())<0) return(x);
              xfertyp = y;
              return(0);

case XZIFD:   if ((y = cmkey(ifdtab,3,"discard","Incomplete file action"))<0) return(y);
              if ((x = cmcfm())<0) return(x);
              keep = (y==1);
              return(0);

case XZNAME:  if ((y = cmkey(fntab,4,"normal","File name action"))<0) return(y);
              if ((x = cmcfm())<0) return(x);
              trans = y;
              return(0);

case XZSUFF:  y = cmfld("",&s,"Outgoing file suffix");
              if (y==-1 || y==-2) return(y);
              if ((x = cmcfm())<0) return(x);
              strcpy(suffix,s);
              return(0);

default:      error("Unknown SET FILE option");
              return(-2);
         }

case XYDEV:                                      /* SET DEVICE */
         y = cmkey(devtab,ndevtab,"","Device to set");
         if (y==-3)
         {
              error("DEVICE parameter not specified");
              y = -2;
         }
         if (y<0) return(y);

         switch(y)
         {

case XZSOUR:  return(setdev("Transfer source device",sourdev));

case XZDEST:  return(setdev("Transfer destination device",destdev));

case XZTAKE:  return(setdev("Command file device",takedev));

default:      error("Unknown SET DEVICE option");
              return(-2);
         }

case XYSPEE:                                     /* Speed (baud rate) */
         if (!local)
         {
              error("Communication line not set");
              return(-2);
         }

         if ((y = cmkey(spdtab,nspdtab,DFSPEED,"Baud rate"))<0) return(y);
         if (x = (cmcfm())<0) return(x);
         if ((x = setbaud(y))<0)
         {
              error("Can't set speed to %d",y);
              return(-2);
         }
         speed = y;
         if (debon) printf("%s set to %d baud\n",ttyname,speed);
         return(0);

case XYQCTL:                                     /* Control quote */
         return(setq(&quote,pebq,"#","Control quote character"));

case XYEBQ:                                      /* 8-bit quote */
         return(setq(&pebq,quote,"&","8-bit quote character"));

default: if ((x = cmcfm())<0) return(x);
         error("Unknown SET option");
         return(-2);
    }
}


/* SETQ - Parse and set a quote character */

int setq(c,other,xdef,xhlp)
char *c;
char other;
char *xdef,*xhlp;
{
    int x,y;
    char *s;

    if ((y = cmfld(xdef,&s,xhlp))<0) return(y);  /* Read parameter */
    if ((y = cmcfm())<0) return(y);              /* Confirm end */

    if (strlen(s)!=1)                            /* Check length */
    {
         error("Not a single character - %s",s);
         return(-2);
    }

    x = *s;
    if (!chkquote(x))                            /* Check for valid quote character */
    {
         error("Not a valid quote character - %c",x);
         return(-2);
    }

    if (x==other)                                /* Check for duplication */
    {
         error("Quote characters identical");
         return(-2);
    }

    *c = x;                                      /* Valid, set new value */
    return(0);
}


/* SETON - Parse ON/OFF (default ON), set parameter to result */

int seton(prm)
int *prm;
{
    int x, y;

    if ((y = cmkey(onoff,2,"on","Option"))<0) return(y);
    if ((x = cmcfm())<0) return(x);
    *prm = (y==1);
    return(0);
}


/* SETNUM - Set parameter to result of cmnum() parse

    Call with:
       x = number from cnum parse
       y = return code from cmnum
*/

int setnum(prm,x,y)
int *prm,x,y;
{
    if (y<0) return(y);                          /* cmnum error */
    if (x>94 || x<0)                             /* Check range */
    {
         error("Number not in range 0..94 - %d",x);
         return(-2);
    }
    if ((y = cmcfm())<0) return(y);
    *prm = x;
    return(0);
}


/* SETCC - Set parameter to an ASCII control character value */

int setcc(prm,x,y)
int *prm,x,y;
{
    if (y<0) return(y);                          /* cmnum error */
    if ((x>037) && (x!=0177))                    /* Check range */
    {
         error("Number not in ASCII control range - %d",x);
         return(-2);
    }
    if ((y = cmcfm())<0) return(y);
    *prm = x;
    return(0);
}


/* SETDEV - Read and set a device name */

int setdev(xhlp,str)
char *xhlp,*str;
{
     char *s;
     int x;

     x = cmfld("",&s,xhlp);                      /* Read device name */
     if (x==-3)
     {
          error("Device not specified");         /* Must be given */
          x = -2;
     }
     if (x<0) return(x);
     if ((x = cmcfm())<0) return(x);             /* Check end of line */

     strcpy(str,s);                              /* Copy name to resting place */
     return(0);
}
