/*
    US1_C - QL-Kermit user command routines (Part 1)

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


/* Include files */

#include "flp1_ctype_h"                          /* Character type macros */
#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;                                 /* Initial receive packet size */
extern int rtmo;                                 /* Timeout we use */
extern int stmo;                                 /* Timeout remote uses */
extern int state;                                /* Switcher start state */
extern int ttychid;                              /* QDOS channel ID for serial line */
extern int tlevel;                               /* TAKE file nesting level */
extern int trans;                                /* File name translation */
extern int debug;                                /* Debugging level */
extern int speed;                                /* Line speed (baud rate) */
extern int xfertyp;                              /* File transfer type */
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;                                 /* Padding amount remote needs */

extern bool timer;                               /* Timer enabled flag */
extern bool echo;                                /* Local echo flag */
extern bool local;                               /* Operating mode */
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 quote prefix */
extern char pebq;                                /* 8-bit quote prefix */
extern char enter[];                             /* What ENTER sends */

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

extern char stype;                               /* Server command to be done */

extern char ttyname[];                           /* Communication device name */

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 char *vers;                               /* Program version */

extern char *cmarg,*cmarg2;                      /* Switcher parameters */

extern FILE *tfile[MAXTAKE];                     /* TAKE file stack */


/* External functions */

extern char *ptype();                            /* Parity type name */
extern bool test();                              /* Bit test */


/* Local variables */

char line[CMDBL+10];                             /* General character buffer */
char *lp;                                        /* General pointer */

int n;                                           /* General temporary */

bool repars;                                     /* Reparse flag */

char cmdbuf[CMDBL+4];                            /* Command string buffer */


/* Macros */

#define CHKLINE    { if (!local) { error("Communication line not set"); return(-2); } }


/* Keyword tables */

struct keytab cmdtab[] =                         /* Top-level commands */
{
    0,        "connect",          XXCON,
    0,        "do",               XXDOC,
    0,        "exit",             XXQUI,
    0,        "finish",           XXFIN,
    0,        "get",              XXGET,
    0,        "help",             XXHELP,
    CM_INV,   "quit",             XXQUI,
    0,        "receive",          XXREC,
    0,        "send",             XXSEN,
    0,        "set",              XXSET,
    0,        "show",             XXSHO,
    0,        "take",             XXTAK
};

int ncmd = (sizeof(cmdtab) / sizeof(struct keytab));


struct keytab prmtab[] =                         /* SET parameters */
{
    0,        "baud",             XYSPEE,
    0,        "control-quote",    XYQCTL,
    0,        "debugging",        XYDEB,
    0,        "delay",            XYDEL,
    0,        "device",           XYDEV,
    0,        "eight-bit-quote",  XYEBQ,
    CM_INV,   "end-of-line",      XYEOL,
    0,        "enter",            XYENT,
    0,        "file",             XYFILE,
    0,        "line",             XYLINE,
    0,        "local-echo",       XYECHO,
    CM_INV,   "marker",           XYMARK,
    CM_INV,   "packet-length",    XYLEN,
    0,        "padding",          XYPAD,
    0,        "parity",           XYPARI,
    0,        "receive",          XYRECV,
    0,        "retries",          XYRETR,
    0,        "send",             XYSEND,
    CM_INV,   "speed",            XYSPEE,
    CM_INV,   "start-of-packet",  XYMARK,
    0,        "take-abort",       XYTKAB,
    0,        "take-echo",        XYTKEC,
    CM_INV,   "timeout",          XYTIMO,
    0,        "timer",            XYTIME
};

int nprm = (sizeof(prmtab) / sizeof(struct keytab));


struct keytab shotab[] =                         /* SHOW parameters */
{
    0,        "all",              SHALL,
    0,        "communication",    SHCOM,
    0,        "devices",          SHDEV,
    0,        "file",             SHFILE,
    0,        "prefix",           SHPRE,
    CM_INV,   "quote",            SHPRE,
    0,        "system",           SHSYS,
    0,        "transfer",         SHXFER,
    0,        "version",          SHVER
};

int nshotab = (sizeof(shotab) / sizeof(struct keytab));


/* CMDINI - Initialize the interactive command parser */

cmdini()
{
    cls(fgetchid(stdout));                       /* Clear the screen window */
    printf("\n%s\n\n",vers);                     /* Say who we are */
    cmsetp("QLK> ");                             /* Set up prompt */
    tlevel = -1;                                 /* No TAKE files */

    if ((tfile[++tlevel] = fopen(DFTAKE,"r"))==NULL)
    {                                            /* Try startup file */
         if (debon) printf("No startup file found\n");
         tlevel--;
    }
}


/* PARSER - Top-level interactive command parser */

parser(status)
bool status;
{
    int xx,cbn;
    char *cbp;

/* Abandon all the TAKE files, if the previous file transfer failed */

    if (!status && tkabort && tlevel>-1)
    {
         error("Transfer error, TAKE files abandoned");
         while (tlevel>-1) tclose();
    }
    status = TRUE;

/* state becomes nonzero when a command has been parsed that requires some
   action from the protocol module.  Any non-protocol actions, such as local
   directory listing or terminal emulation, are invoked directly from below.
*/

    state = 0;                                   /* Begin with no start state */
    while (state==0)                             /* Parse cmds until action requested */
    {
         *cmarg = '\0';                          /* Start clean */
         *cmarg2 = '\0';

         if (tlevel>-1)                          /* If in TAKE file, */
         {
              cmini();
              cbp = cmdbuf;
              cbn = CMDBL;                       /* try for next command from there */
              if (fgets(line,cbn,tfile[tlevel])==NULL)
              {
                   if (feof(tfile[tlevel])!=0)   /* If end of file, close & pop level */
                        tclose();
                   continue;
              }
              if (tkecho) printf("TAKE-%d> %s",tlevel,line);

              lp = line;                         /* Got one, copy it */
              while (*cbp++ = *lp++);
         }
         else                                    /* No TAKE file, get user input */
         {
              prompt();                          /* Issue interactive prompt */
              cmini();
         }

         repars = TRUE;
         while (repars)                          /* Repeat until a command done */
         {
              cmres();                           /* Reset buffer pointers */
              switch (xx = docmd(cmkey(cmdtab,ncmd,"","Command")))
              {
case -4:           doexit();                     /* EOF, exit successfully */

case -1:           repars = TRUE;                /* Reparse needed */
                   continue;

case -2:           if (tkabort && tlevel>-1)     /* Unknown command */
                   {                             /* If in TAKE file, quit */
                        error("Command error, TAKE files abandoned");
                        while (tlevel>-1) tclose();
                   }
                   cmini();                      /* Fall through */

case -3:                                         /* Empty command, OK at top level */
default:           repars = FALSE;               /* Anything else, get new command */
                   continue;
              }
         }
    }
}


/* DOEXIT - Exit from the program */

doexit()
{
    cls(fgetchid(stdout));                       /* Clear the screen window */
    exit(0);                                     /* Exit from the program */
}


/* DOCMD - Do a command

    Returns:
       -2: user typed an illegal command
       -1: reparse needed
        0: parse was successful (even though command may have failed)
*/

int docmd(cx)
int cx;
{
    int x,y;
    char *s,*cp;

    switch (cx)
    {

case -3: return(0);                              /* Null command */

case -4:                                         /* EOF */
case -2:                                         /* Error */
case -1: return(cx);                             /* Reparse needed */

case XXCON:                                      /* CONNECT */
         if ((x = cmcfm())<0) return(x);
         CHKLINE;
         doconnect();
         return(0);

case XXDOC:                                      /* DO */
         if ((x = cmtxt("",&s,"Remote command"))<0) return(x);
         CHKLINE;
         strcpy(cmarg,s);
         stype = 'C';
         state = S_COMD;                         /* Set start state */
         return(0);

case XXFIN:                                      /* FINISH */
         if ((x = cmcfm())<0) return(x);
         CHKLINE;
         strcpy(cmarg,"F");
         stype = 'G';
         state = S_COMD;
         return(0);

case XXGET:                                      /* GET remote [local=F] */
         CHKLINE;
         x = cmfld("",&s,"Remote filename");     /* Get remote name */
         if (x==-3)
         {
              error("Remote name not specified");
              x = -2;
         }
         if (x<0) return(x);

         strcpy(cmarg,s);                        /* Save remote filename */

         if ((y = cmfld("",&s,"Optional local filename"))==-3) s = "";
         else if (y<0) return(y);
         strcpy(cmarg2,s);                       /* Get & save local name */

         if ((x = cmcfm())<0) return(x);         /* Confirm end of command */

         state = G_INIT;                         /* All OK, set start state */
         return(0);

case XXHELP:                                     /* HELP */
         if ((x = cmcfm())<0) return(x);
         printf("\nQL-Kermit control keys:\n");
         printf(" Command mode:\n  F1  Context-sensitive help\n  F3  Delete line\n");
         printf("  F4  Complete/default word\n  F5  Delete character\n");
         printf("  ^R  Replay line\n");
         printf("\n Terminal mode:\n  F2  Return to command mode\n");
         printf("  F3  Send a NULL\n  F4  Send a ^C\n  F5  Send a DEL\n");
         printf("\n Transfer mode:\n  ^X  Interrupt file\n  ^Z  Interrupt batch\n");
         printf("  ^E  Interrupt transaction\n  ^T  Resend packet\n");
         printf("\n");
         return(0);

case XXQUI:                                      /* QUIT or EXIT */
         if ((x = cmcfm())>-1) doexit();
         else return(x);

case XXREC:                                      /* RECEIVE [local=F] */
         CHKLINE;
         x = cmfld("",&s,"Optional local filename");
         if ((x==-1) || (x==-2)) return(x);      /* Get local name */
         if (x==-3) s = "";                      /* Can be omitted */
         strcpy(cmarg2,s);                       /* Given, save it away */

         if ((x = cmcfm())<0) return(x);         /* Confirm end */

         printf("Waiting\n");
         state = R_INIT;
         return(0);

case XXSEN:                                      /* SEND local [remote=local]*/
         CHKLINE;
         x = cmfld("",&s,"Local filename");      /* Get local name */
         if (x==-3)
         {
              error("Local name not specified");
              x = -2;
         }
         if (x<0) return(x);

         strcpy(cmarg,s);                        /* Save local name */
         y = cmfld("",&s,"Optional remote filename");
         if ((y==-1) || (y==-2)) return(y);      /* Get remote name */
         if (y!=-3) strcpy(cmarg2,s);            /* If given, store it away */
         if ((y = cmcfm())<0) return(y);

         printf("Pausing\n");
         wait(delay);                            /* Wait for the set time */
         state = S_INIT;                         /* Set start state */
         return(0);

case XXSET:                                      /* SET */
         x = cmkey(prmtab,nprm,"","What to set");
         if (x==-3)
         {
              error("SET parameter not specified");
              x = -2;
         }
         if (x<0) return(x);
         else return(doprm(x));

case XXSHO:                                      /* SHOW */
         x = cmkey(shotab,nshotab,"all","What to show");
         if (x<0) return(x);                     /* Find out what to show */
         if (y = (cmcfm())<0) return(y);

         if (x==SHVER) printf("\nVersion: %s\n\n",vers);
         else shopar(x);
         return(0);

case XXTAK:                                      /* TAKE */
         if (tlevel>MAXTAKE-1)                   /* Check not too many */
         {
              error("TAKE files nested too deeply");
              return(-2);
         }
         if ((y = cmfld("",&s,"Command file name"))<0)
         {                                       /* Get file name */
              if (y==-3)
              {
                   error("Command file not specified");
                   return(-2);
              }
              else return(y);
         }
         strcpy(line,s);                         /* Make a safe copy of the name */
         if ((y = cmcfm())<0) return(y);

         if ((tfile[++tlevel] = fopen(line,"r"))==NULL)
         {                                       /* Try to open file */
              if (debon) printf("Couldn't open TAKE file %s\n",line);
              strcpy(s,takedev);                 /* Prepend TAKE device */
              cp = line;
              strncat(s,cp,15);
              if ((tfile[tlevel] = fopen(s,"r"))==NULL)
              {                                   /* Try again */
                   qdoserr();
                   error("Cannot open %s or %s",line,s);
                   tlevel--;                     /* Give up if can't */
              }
         }
         return(0);                              /* Whether opened or not */

default: error("Invalid command - %s\n",cmdbuf);
         return(-2);
    }
}


/*  SHOPAR - Show Parameters */

shopar(f)
int f;
{
    if (test(f,SHCOM))
    {
         printf("\nCommunications parameters:\n");
         printf(" Line ");
         if (serno<0) printf("not set"); else printf("%s",ttyname);
         printf(", speed ");
         if (speed<0) printf("not set"); else printf("%d",speed);
         printf(", parity %s\n",ptype(parity));
         printf(" Local echo %s\n",(echo ? "on" : "off"));
         printf(" ENTER sends %s",(enter[0]==CR ? "CR" : "LF"));
         if (enter[1]!=0) printf("+%s",(enter[1]==CR ? "CR" : "LF"));
         printf("\n");
    }

    if (test(f,SHXFER))
    {
         printf("\nTransfer parameters:    Send   Receive\n");
         printf(" Timeout        %11d%9d\n",stmo,rtmo);
         printf(" Packet start   %11d%9d\n",ssop,rsop);
         printf(" Packet end     %11d%9d\n",seol,reol);
         printf(" Packet length  %11d%9d\n",slen,rlen);
         printf(" Delay          %11d\n",delay);
         printf(" Padding        ");
         if (npad==0) printf("%12s\n","none");
         else printf("%11d\n  of                     0%o ('%c')\n",npad,padch,padch);
    }

    if (test(f,SHFILE))
    {
         printf("\nFile parameters:\n");
         printf(" Incomplete files %s\n",keep ? "kept" : "deleted");
         printf(" Transfer mode %s\n",xfertyp==FTASC ? "ASCII" : "binary");
         printf(" Naming %s\n",trans==FNNORM ? "normal" : "untranslated");
         if (strlen(suffix)==0) printf(" No send suffix\n");
         else printf(" Send suffix %s\n",suffix);
    }

    if (test(f,SHPRE))
    {
         printf("\nPrefix characters:\n");
         printf(" Control quote     %c\n",quote);
         printf(" Eight-bit quote   %c\n",pebq);
    }

    if (test(f,SHSYS))
    {
         printf("\nSystem parameters:\n");
         printf(" Mode %s\n",(local ? "local" : "remote"));
         printf(" Timer %s, retry limit %d\n",timer ? "enabled" : "disabled",retry);
         printf(" Debugging ");
         switch (debug)
         {
case DBOFF:   printf("off"); endcase;
case DBON:    printf("on"); endcase;
case DBFULL:  printf("full"); endcase;
         }
         printf("\n TAKE file echo %s,",tkecho ? "on" : "off");
         printf(" %s on error\n",tkabort ? "abort" : "continue");
    }

    if (test(f,SHDEV))
    {
         printf("\nDevices:\n");
         printf(" Transfer source       %s\n",sourdev);
         printf(" Transfer destination  %s\n",destdev);
         printf(" TAKE file source      %s\n",takedev);
    }

    printf("\n");
}
