/*
    SBR_C - QL-Kermit general subroutines

    Extracts from ck*.c files, (C) Columbia University
*/


/* Include files */

#include "ram1_ker_h"                            /* Kermit definitions */

#include "flp1_fcntl_h"                          /* File opening modes */
#include "flp1_ctype_h"                          /* Character test macros */


/* External variables */

extern int state;                                /* Current switcher state */
extern int ttychid;                              /* QDOS channel ID of comms line */
extern int tvalue;                               /* Receive timeout (frames) */
extern int debug;                                /* Debugging level */
extern int serno;                                /* Serial port in use */
extern int parity;                               /* Parity setting */
extern int speed;                                /* Baud rate */
extern int ttyfd;                                /* FD of communication line */
extern int trans;                                /* File name translation */
extern int tlevel;                               /* TAKE file nesting level */

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

extern bool timer;                               /* Timer enabled flag */
extern bool cxseen,czseen;                       /* Transfer interrupt flags */
extern bool local;                               /* Operation mode flag */
extern bool keep;                                /* Incomplete file flag */

extern char *newfilnam;                          /* Transfer file name */
extern char *errdata;                            /* Data for E packet */
extern char *suffix;                             /* Outgoing file suffix */

extern char ttyname[];                           /* Name of communication line */

extern int fp;                                   /* Transfer file */

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


/* External functions */

char *strchr();                                  /* Find first character */
char *strrchr();                                 /* Find last character */


/* Local variables */

static char buf[BLKSIZ];                         /* Transfer file buffer */
static int bufp;                                 /* Index into above */
static int n;                                    /* Character count */


/* DELCH - Special QL console character delete */

delch()
{
    sd_pcol(fgetchid(stdout),1);
    putchar(' ');
    sd_pcol(fgetchid(stdout),1);
}


/* TREAD - Read a character from line, with timeout */

int tread()
{
    unsigned char t;
    int e;
    int w = 0;

    do
    {
         if (!io_pend(ttychid,1))                /* Check for pending input */
         {
              io_fbyte(ttychid,0,&t);            /* If a byte available, */
              if (parity!=PYNONE) t &= 0177;     /* remove parity */
              return((int) t);                   /* and return immediately */
         }
         w++;                                    /* otherwise continue */
    } while ((e = chkint())>=0 &&
             (w<tvalue || !timer));              /* until timeout or interrupt */
    if (e==INTE) return(e);                      /* ^E interrupt */
    if (debon) printf("Receiver timeout\n");
    return(BAD);                                 /* Timeout */
}


/* CHKINT - Check for console interrupts */

int chkint()
{
    unsigned char ch;

    if (!io_pend(fgetchid(stdin),0))
    {
         io_fbyte(fgetchid(stdin),0,&ch);         /* If a key pressed, read it */
         switch (ch)
         {

case 'B'-'@':                                    /* CTRL-B */
case 'Z'-'@':                                    /* CTRL-Z */
              if (state==S_DATA || state==R_DATA)
              {
                   intrpt("Batch aborted");
                   czseen = TRUE;
              }
              endcase;

case 'F'-'@':                                    /* CTRL-F */
case 'X'-'@':                                    /* CTRL-X */
              if (state==S_DATA || state==R_DATA)
              {
                   intrpt("File aborted");
                   cxseen = TRUE;
              }
              endcase;

case 'E'-'@': return(INTE);                      /* CTRL-E */

case 'T'-'@': intrpt("Forced timeout");          /* ENTER */
              return(BAD);

         }
    }
    return(0);
}


/* FLUSHINPUT - Dump all pending input to clear stacked up NAKs */

flushinput()
{
    char dump;

    if (debon) printf("Flushing input on %s\n",ttyname);
    while (io_pend(ttychid,1)==0) io_fbyte(ttychid,0,&dump);
    if (debon) printf("Input flushed\n");
}


/* ERROR - Print an error message */

error(fmt,a1,a2,a3,a4,a5)
char *fmt;
{
    printf("ERROR: ");
    printf(fmt,a1,a2,a3,a4,a5);
    printf("\n");
}


/* PRERRPKT - Print contents of error packet received from remote host */

prerrpkt(msg)
char *msg;
{
    printf("REMOTE ERROR: %s\n",msg);
}


/* INTRPT - Print a transfer interruption message */

intrpt(msg)
char *msg;
{
    printf("INTERRUPT: %s\n",msg);
}


/* STNAME - return a printable state name */

char *stname(st)
int st;
{
    switch(st)
    {

case ABORT:   return("ABORT");
case COMP:    return("COMPLETE");
case IDLE:    return("IDLE");
case R_INIT:  return("REC_INIT");
case R_FILE:  return("REC_FILE");
case R_DATA:  return("REC_DATA");
case S_INIT:  return("SEND_INIT");
case S_FILE:  return("SEND_FILE");
case S_DATA:  return("SEND_DATA");
case S_EOF:   return("SEND_EOF");
case S_BRK:   return("SEND_EOT");
case S_COMD:  return("SEND_SERVER_CMD");
case G_INIT:  return("SEND_SERVER_INIT");
case K_ERR:   return("SEND_ERROR");
default:      return("UNKNOWN");

    }
}


/* RTLIM - Retry limit exceeded */

int rtlim()
{
    strcpy(errdata,"Retry limit exceeded");
    error(errdata);
    return(K_ERR);
}


/* INTERRUPT - CTRL-E pressed to cause error abort */

int interrupt()
{
    strcpy(errdata,"CTRL-E interrupt");
    intrpt("Transaction aborted");
    return(K_ERR);
}


/* ERRSTR - give a description for a QDOS error */

char *errstr(code)
int code;
{
    switch (-code)
    {

case 0:  return("No error");
case 1:  return("Not complete");
case 2:  return("Invalid job");
case 3:  return("Out of memory");
case 4:  return("Out of range");
case 5:  return("Buffer full");
case 6:  return("Channel not found");
case 7:  return("Not found");
case 8:  return("Already exists");
case 9:  return("In use");
case 10: return("End of file");
case 11: return("Drive full");
case 12: return("Bad name");
case 13: return("Transmit error");
case 14: return("Format failed");
case 15: return("Bad parameter");
case 16: return("Bad medium");
case 17: return("Error in expression");
case 18: return("Overflow");
case 19: return("Not implemented");
case 20: return("Read only");
case 21: return("Bad line");
default: return("Unknown error");

    }
}


/* QDOSERR - An error from QDOS encountered */

int qdoserr()
{
    strcpy(errdata,errstr(oserr));
    printf("QDOS: %s\n",errdata);
    return(K_ERR);
}


/* CLOSEF - Close transfer file if open */

closef()
{
    if (fp>=0) close(fp);                        /* File open? */
    fp = -1;
}


/* TFGET - Get a character from transfer file (from K&R - MCC library
   getchar() gives EOF if the file contains more than one $FF byte
   in succession!)
*/

int tfget()
{
    if (n==0)                                    /* Buffer empty? */
    {
         n = read(fp,buf,BLKSIZ);                /* Read data */
         bufp = 0;                               /* and restore pointer */
    }

    return((--n>=0) ? buf[bufp++]&0377 : EOF);   /* Return data or EOF indication */
}


/* DEVOPEN - open transfer file, using appropriate device if necessary */

int devopen(name,dev,mode)
char *name,*dev;
int mode;
{
    strcpy(newfilnam,name);                      /* Name for first try */
    if ((fp = open(newfilnam,mode))<0)
    {
         oserr = _oserr;
         if (debon) printf("Open %s failed\n",newfilnam);
         if (oserr==ERR_NF)                      /* Maybe device name left off */
         {
              strcpy(newfilnam,dev);             /* Add device name */
              strcat(newfilnam,name);            /* to given name */
              if ((fp = open(newfilnam,mode))<0)
              {
                   oserr = _oserr;
                   if (debon) printf("Open %s failed\n",newfilnam);
              }
         }
    }

    n = bufp = 0;                                /* Initialise read pointers */
    return(fp);
}


/* CLS - clear a window */

cls(chan)
char *chan;
{
    struct REGS in,out;

    in.D0 = 0x20;                                /* SD.CLEAR */
    in.D3 = -1;                                  /* Timeout */
    in.A0 = chan;                                /* Channel */

    QDOS3(&in,&out);
}


/* SETBAUD - Set the communication line baud rate */

setbaud(speed)
int speed;
{
    struct REGS in,out;

    in.D0 = 0x12;                                /* MT.BAUD */
    in.D1 = (short) speed;                       /* Baud rate */

    if (QDOS1(&in,&out)==0) return(speed);
    else return(-1);
}


/* WAIT - Delay for d seconds, or until a key pressed */

wait(d)
int d;
{
    int i;

    for (i = 0; i<d && (io_pend(fgetchid(stdin),0)!=0); i++) mt_susjb(1*50);
}


/* MT_SUSJB - Sleep this job for w frames (1/50s) */

mt_susjb(w)
int w;
{
    struct REGS in,out;

    in.D0 = 0x08;                                /* MT.SUSJB */
    in.D1 = -1;                                  /* Current job */
    in.D3 = (short) w;                           /* Timeout */
    in.A1 = 0;                                   /* No flag byte */

    QDOS1(&in,&out);
}


/* PTYPE - return a string for parity type p */

char *ptype(p)
int p;
{
    switch(p)
    {

case PYNONE: return("none");
case PYEVEN: return("even");
case PYODD:  return("odd");
case PYMARK: return("mark");
case PYSPC:  return("space");
default:     return("UNKNOWN");

    }
}


/* MKTTNAM - build a name to open the serial port with */

int mkttnam(line,pty)
int line,pty;
{
     int i;

     strcpy(ttyname,"ser");                      /* First part of name */
     i = strlen(ttyname);

     ttyname[i++] = line+'0';                    /* Port number */

     switch (pty)                                /* Parity setting */
     {
case PYEVEN: ttyname[i++] = 'e'; endcase;
case PYODD:  ttyname[i++] = 'o'; endcase;
case PYMARK: ttyname[i++] = 'm'; endcase;
case PYSPC:  ttyname[i++] = 's'; endcase;
     }

     ttyname[i++] = 'h';                         /* Use handshake */
     ttyname[i++] = 'r';                         /* Raw data */
     ttyname[i] = '\0';                          /* Terminate the string */
     return(i);                                  /* and return the length */
}


/* NEWTTY - re-open a serial port with the current settings */

int newtty()
{
    if (ttyfd>0)
    {
         close(ttyfd);                           /* Close old line if open */
         if (debon) printf("Closed %s\n",ttyname);
         ttyfd = -1;
    }

    ttyname[0] = '\0';                           /* and clear out name */
    local = FALSE;                               /* and status */

    if (serno>0)                                 /* New line to be opened? */
    {
         mkttnam(serno,parity);                  /* Make new name */
         ttyfd = open(ttyname,O_RDWR);           /* Open line */
         if (ttyfd<0)                            /* Open failed? */
         {
              oserr = _oserr;                    /* Save QDOS error code */
              error("Cannot open %s",ttyname);   /* Print error messages */
              qdoserr();
              speed = -1;                        /* Unset line parameters */
              return(-2);
         }
         local = TRUE;                           /* Set local/remote status */
         ttychid = (int) getchid(ttyfd);         /* Reset channel ID */
         if (debon) printf("Opened %s\n",ttyname);
    }
    else speed = -1;                             /* Remote mode, unset speed */

    return(0);
}


/* DISCARD - delete an incomplete file, if appropriate flag set */

discard(fn)
char *fn;
{
    if (!keep)
    {
         unlink(fn);
         printf("Discarded %s\n",fn);
    }
}


/* CHKQUOTE - Check for a valid quote character */

bool chkquote(c)
char c;
{
    return((c>' ' && c<'@') || (c>'a' && c<DEL));
}


/* NAMEPATCH - change our job name from "C-PROG" to something sensible */

namepatch()
{
    char *p;                                     /* Destination pointer */
    char *s = "Kermit";                          /* New job name */
    struct REGS in,out;

    in.D0 = 0;                                   /* MT.INF */
    QDOS1(&in,&out);                             /* Get our job ID */

    in.D0 = 2;                                   /* MT.JINF */
    in.D1 = out.D1;                              /* Job ID */
    in.D2 = 0;                                   /* Parent ID */
    QDOS1(&in,&out);                             /* Get our base address */

    p = out.A0+8;                                /* Point to job name length */
    if (*((short *) p)==6)                       /* Check it */
    {
         p += 2;
         while (*s!='\0') *p++ = *s++;           /* and copy in new one */
    }
}


/* RTOL - convert a filename from Kermit- to QL-format:  (a) change '.'s
   to '_'s, (b) make letters lower case
*/

rtol(new,old)
char new[],old[];
{
    int i;
    char c;

    if (trans==FNUNTR) strcpy(new,old);          /* Literal mode , don't convert */
    else
    {
         for (i = 0; (c = old[i])!='\0'; i++)
         {
              if (c=='.') new[i] = '_';
              else if (isupper(c)) new[i] = tolower(c);
              else new[i] = c;
         }
         new[i] = '\0';
    }

    if (debon) printf("rtol: converted %s to %s\n",old,new);

}


/* LTOR - convert a filename from QL- to Kermit-format:  (a) remove first
   component (device name), (b) change last '_' to '.', (c) make letters
   upper case.  We know that old[] has at least 2 components here (the QL
   device name and the file name).
*/

ltor(new,old)
char new[],old[];
{
    int l,s,j;
    char c;
    char wrk[30];

    if (trans==FNUNTR) strcpy(new,old);          /* Literal mode, don't convert */
    else
    {
         strcpy(wrk,old);                        /* Take local copy of name */
         if (strlen(suffix)>0)                   /* Suffix to be added? */
         {
              strcat(wrk,"_");                   /* Yes, add seperator */
              strcat(wrk,suffix);                /* then the suffix */
         }

         s = (int) (strchr(wrk,'_')-wrk+1);      /* Find start of 2nd component */
         l = (int) (strrchr(wrk,'_')-wrk);       /* and last underscore */

         for (j = 0; (c = wrk[s])!='\0'; s++,j++)
         {
              if (s==l) new[j] = '.';
              else if (islower(c)) new[j] = toupper(c);
              else new[j] = c;
         }
         new[j] = '\0';
    }

    if (debon) printf("ltor: converted %s to %s\n",old,new);

}


/* TCLOSE - close the current TAKE file */

tclose()
{
    fclose(tfile[tlevel--]);
    if (debon) printf("TAKE file level %d closed\n",tlevel+1);
}
