
/************************************************************************/

/*  File  KUTIL2.C  -  Extended buffer filling/emptying procedures etc.
		Chris Kennington	RML.	9th July 1985	*/

#define	 DEFS1		1
#define  DEFS3		1
#define	 DEFS4		1

#include  "stdio.h"
#include  "ctype.h"
#include  "b:kext.h"

/* Variables global to this file:-				*/

static	int	oldsz, oldt, qu8=0, rpt, sz;
static	char	*dt;
static  char	del1[]  = {BKSP,SP,0};
static	char	del7[]	= {BKSP,BKSP,BKSP,BKSP,BKSP,BKSP,BKSP,SP,SP,SP,SP,SP,SP,SP,0};
#ifndef MPUZ80
static  char	*images[] = {"7-bit stripped","8-bit binary","8th-bit prefixed",0};
#endif

extern  int  outfc(), outfile();



char  ascch(c)		/* edit out multiple CR/LFs		*/
/* Returns c unless redundant, when returns 0			*/
char	c;
{
    static char oldc = 0;

    c &= 0x7f;			/* strip to 7 bits		*/
    if ( ( (c == CR) && (oldc == LF) ) || ( (c == LF) && (oldc == CR) ) )
	c = oldc = 0;
    else {
	oldc = c;
	if (c == LF)
	    c = CR;
    }
    return(c);
}		/* end of ascch()				*/



bufemp(buffer,len)
/*  Puts data from an incoming packet into a file,
	sleeping & waking comms & network.
    If no file open (fp = 0), displays on screen
    Returns 0 if OK, else contents of "errno".			*/
char  buffer[];				/* Buffer */
int   len;
{
    int  ret;

    if (fp == 0)
	ret = decode(buffer,len,outfc);
    else {
	s4sleep();			/* Inhibit communications */
	if (list == 1)
	    outc('.');
	ret = decode(buffer,len,outfile);
    }
    netcool();
    return(ret);
}				/* end of bufemp()		*/



bufill(buffer)
/*  Get a bufferful of data from the file that's being sent, sleeping
 *    & waking comms & network.
 *  Traps CP/M soft EoF (ctl-Z).				 */
char *buffer;
{
    int t;

    s4sleep();				/* Inhibit communications */
    errno = 0;
    dt = buffer;
    if (softeof) {			/* Trap CP/M text-file	*/
	sz = EOF;
	goto Exit;
    }
    if (list == 1)
	outc('.');			/* dot-printing	*/
    rpt = sz = 0;			/* Init data buffer pointer */
    oldt = -2;				/* impossible value	*/
    while((t = getc(fp)) != EOF) {	/* Get the next character */
  /* if there is an error, getc() will also return EOF and there is an
     error-code in "errno".  This is picked up in sdata().		*/
#ifdef MPUZ80
	if (list == 2) 
#else
	if ( (list == 2) && (t != LF) )
#endif
	    outc(t);
	encode(t);
	if ( (image == 0) && (t == CTLZ) ) {
	    softeof = TRUE;
	    goto Exit;			/* CP/M text EOF	*/
	}
	if (sz > spsiz-7)		/* Check length		*/
	     goto Exit;
    }
  /* reach here on (hard) EOF or error				*/
    softeof = TRUE;			/* so dont reread	*/
    if (sz==0)
	sz = EOF;
Exit:
    netcool();
    return(sz);				/* Handle partial buffer */
}			/* end of bufill()		*/



clear5()		/* clear bottom lines of screen		*/
{
    int	lin;

    lin = (SCRBOT+3)*256;
    while (lin < (SCRLEN*256)) {
	vtline(lin,blanx);
	lin += 256;
    }
}			/* end of clear5()			*/



compmode()		/* display transfer modes		*/
{
    if (image == 2)
	txtout(" 8-bit data, ");
    else if (oldimage == 2) {
	printmsg("Warning: 8-bit transfer not agreed by remote Kermit.\r");
	oldimage = image;
    }
    if (rptflg == TRUE)
	txtout(" Compressing, ");
    return;
}			/* end of compmode()			*/



decode(buf,len,fn)		/* packet decoding procedure	*/
/* Called with string to be decoded and an output function.
	Returns 0 or error-code.				*/
char *buf;
int  len, (*fn)();
{
    char  a, a7, b8, *end, rep;
    int   flg8=0, r, rr=0;

    if (image == 2)
	flg8 = -1;

    end = buf + len;
    while (buf < end) {
	a = *buf++;
	if ( rptflg && (a == '~') ) {	/* got a repeat prefix? */
		rep = unchar(*buf++);	/* Yes, get the repeat count, */
		a = *buf++;		/* and get the prefixed character. */
	}
	else
	    rep = 1;
	b8 = 0;				/* Check high order "8th" bit */
	if ( flg8 && (a == qu8) ) {
		b8 = 0200;
		a = *buf++;		/* and get the prefixed character. */
	    }
	if (a == quote) {		/* If control prefix, */
	    a  = *buf++;		/* get its operand. */
	    a7 = a & 0177;		/* Only look at low 7 bits. */
	    if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') /* Uncontrollify */
	    a = ctl(a);			/* if in control range. */
	}
	a |= b8;			/* OR in the 8th bit */
	while (rep-- > 0) {
	    r = (*fn)(a);		/* Send them to the output function. */
	    rr |= r;
    }	}
    return(rr);
}			/* end of decode()			*/



encode(a)				
/* encode single character into packet for transmission	*/
int a;				/* char to be encoded		*/
{
    int a7;				/* Low order 7 bits	*/
    int b8;				/* 8th bit of character */
    int flg8 = 0;

    if (image == 2)
	flg8 = -1;

    if (rptflg)	{		/* repeat-count processing	*/
        if (a == oldt) {		/* char is same		*/
/* This algorithm is simple but relatively inefficient; it stores
	the repeat flag, count and character each time around so
	that when the run is broken the buffer is valid; also it
	treats a pair as a run, which requires 3 bytes not 2 unless
	the pair is control- or 8bit-prefixed; but it does not
	require lookahead logic from the data-read.		*/
	    sz = oldsz;			/* wind back pointer	*/
	    dt[sz++] = '~';		/*  store prefix	*/
	    dt[sz++] = tochar(++rpt);	/*   & count		*/
	    if (rpt > 93)		/* force new start	*/
		oldt = -2;		/* impossible value	*/
	}
	else {				/* not run, or end	*/
	    rpt = 1;
	    oldt = a;			/* save char		*/
	    oldsz = sz;
    }	}

    a7 = a & 0177;			/* Isolate ASCII part */
    b8 = a & 0200;			/* and 8th (parity) bit. */

    if (flg8 && b8) {			/* Do 8th bit prefix if necessary. */
        dt[sz++] = qu8;
        a = a7;
    }
    if ((a7 < SP) || (a7==DELT)) {	/* Do control prefix if necessary */
        dt[sz++] = MYQUOTE;
	a = ctl(a);
    }
    if (a7 == MYQUOTE)			/* Prefix the control prefix */
        dt[sz++] = MYQUOTE;
    else if (rptflg && (a7 == '~'))	/* If it's the repeat prefix, */
        dt[sz++] = MYQUOTE;		/* quote it if doing repeat counts. */
    else if (flg8 && (a7 == qu8))		/* Prefix the 8th bit prefix */
        dt[sz++] = MYQUOTE;		/* if doing 8th-bit prefixes */

    dt[sz++] = a;			/* Finally, insert the character */
    dt[sz] = '\0';			/* itself, and mark the end. */
    return;
}			/* end of encode()		*/




error(fmt, a1, a2, a3, a4, a5)
/*  Generate both error-message to user and if suitable
	error-packet to remote.				*/
char *fmt;
{
    int len;

    sprintf(packet,fmt,a1,a2,a3,a4,a5);
    printmsg(packet);
    if ( (kmode == RECV) || (kmode == SEND) ) {
	len = strlen(packet) + 1;
	spack('E',n,len,packet);
    }
    return;
}			/* end of error()	*/




/*  kropen(), kwopen(), kclose():
  Open and close CP/M files making necessary calls to sleep
    and wake disk system & network.				*/

kclose(fptr)		/* close file				*/
FILE  *fptr;
{
    int	  ret;

    if (fptr == 0)
	return;
    bell();
    ++files;
    s4sleep();
    if ( (ret = fclose(fptr)) < 0)
	printmsg(diskfail,errno);	/* Aztec's  error-code	*/
    else
	ret = 0;
    netcool();
    return(ret);
}			/* end of kclose()			*/


FILE  *kropen(name)	/* open file for reading		*/
char  name[];
{
    FILE  *frp;

    s4sleep();
    softeof = FALSE;
    frp = fopen(name,"r");
    netcool();
    return(frp);
}			/* end of fropen()			*/


FILE  *kwopen(name)	/* open file for writing		*/
char  *name;
{
    FILE  *frp;

    s4sleep();
    frp = fopen(name,"w");
    netcool();
    return(frp);
}			/* end of kwopen()			*/

/* end of open-&-close routines					*/



no_op()			/* null routine				*/
{
	return;
}			/* End of no_op()			*/



outfc(ch)		/* output char with control		*/
char	ch;
/* expands tabs (every 8) and handles paging;
   returns 1 if CTLC input at CR or LF, else 0.			*/
{
    static char col=0;
    char	c, *more, *dlt;

    if (ch < SP) switch(ch) {
  /* deal with selected control-characters			*/
      case CR:
	if (kbdin() == CTLC)
	    return(1);
	if (pager != 0)
	    if ( ++scrline > (pager << 2) ) {
		if (col < 73) {		/* room for "[more]"	*/
		    while (col++ < 73)
			outc(SP);
		    more = "[more]";
		    while ( (c = *more++) != 0)
			outc(c);
		    dlt = del7;
		}
		else if (col < 79)  {			/* no room		*/
		    while (col++ < 78)
			outc(SP);
		    outc('*');
		    dlt = del1;
		}
		else
		    dlt = null;
		if ( (commode & 0x02) != 0 )
		    nextout(XOFF);		/* hold up	*/
		while ( (c = kbdin()) == 0)
		    ;
		if ( (commode & 0x02) != 0 )
		    nextout(XON);		/* restart	*/
		scrline = 0;
		if (c == CTLC)
		    return(1);
		while ( (c = *dlt++) != 0)
		    outc(c);
	    }
	outc(ch);
	col = 0;
	break;

      case LF:
  /* ignored - CR is expected to be treated as "newline"	*/
	break;

      case TAB:
	c = 8 - (col & (char)0x07);
	col &= (char)0xf8;
	col += 0x08;
	while (c-- > 0)
	    outc(SP);
	break;

      case BKSP:
	if (col-- < 0)
	    col = 0;
	else
	    outc(BKSP);
	break;

      default:			/* pass all other controls	*/
	outc(ch);
	break;
    }				/* end if-switch		*/
    else {			/* normal chars			*/
	outc(ch);
	++col;
    }
    return(0);
}				/* end of outfc()		*/




static int outfile(ch)   /* char to file			*/
/* Returns 0 or error-code					*/
char	ch;
{
    int	r;

    r = errno = 0;
    if (image == 0)		/* for 7-bit transfers	*/
	if ( (ch = ascch(ch)) == 0 ) /* CR/LF trapping	*/
	    return(0);		/* ignore nulls		*/
	else if (ch == CR) {	/*  CR => CR/LF	*/
	    putc(CR,fp);
	    if (list == 2)
		outc(CR);
	    ch = LF;
	}				/* end elseif	*/
#ifndef MPUZ80
    if ( (list == 2) && (ch != LF) )
#else
    if (list == 2)
#endif
	outc(ch);
    if (putc(ch,fp) < 0)
	r = errno;		/* accumulate errors	*/
    return(r);
}		/* end of outfile()				*/



parex()		/* exchange parameters			*/
/* returns 1 if successful, -1 if refused, 0 if no answer;
	only allows 3 timeouts/NAKs			*/
{
    int	  ln1, ln2, num, r=0, t;

    ln1 = spar(packet);		/* fill with ours	*/
    forever {
	spack('I',0,ln1,packet);
	switch( (t = rpack(&ln2,&num,recpkt)) ) {
	  case 'Y':		/* ACK			*/
	    rpar(recpkt,ln2);	/* take theirs		*/
	    txtout(" OK ");
	    compmode();
	    n = num + 1;	/* initial number	*/
	    return(1);
	  case 'N':		/* NAK			*/
	  case FALSE:		/* timeout		*/
	    if (r++ > 3)
		return(0);
	    else
		break;
	  default:		/* protocl error	*/
	    printmsg(badmsg,'Y',t);
	  case 'E':		/* cant cope		*/
	    return(-1);
	}		/* end switch			*/
	txtout(trying);
    }			/* end forever			*/
}		/* end of parex()			*/



rpar(data,len)	/* instal received parameters safely		*/
char	*data, len;
/* Set up incoming parameters to either what has been received
	or, if nil, to defaults.
  RML Kermit does not handle variable timeouts.
  No return-code.						*/
{
    char	i, p;

  /* initialize to defaults in case incoming block is short	*/
    spsiz = 80;			/* packet-length 80 chars	*/
    eol = CR;			/* terminator normally CR	*/
    quote = '#';		/* standard control-quote char	*/
    qu8 = 0;			/* no 8th-bit quoting		*/
    rptflg = 0;			/*  nor repeat-quoting		*/
  /* image is not changed					*/

    i = 0;
    while (len-- > 0) {
	p = data[len];
	
	switch (len) {		/* for each parameter		*/

	  case 0:			/* MAXL			*/
	    spsiz = unchar(p);
	    break;

	  case 2:			/* NPAD			*/
	    pad = unchar(p);
	    break;

	  case 3:			/* PADC			*/
	    padchar = ctl(p);
	    break;

	  case 4:			/* EOL			*/
	    eol = unchar(p);
	    break;

	  case 5:			/* QCTL			*/
	    quote = p;
	    break;

	  case 6:			/* QBIN			*/
  /* 8th-bit quoting only negotiable if set by user		*/
	    i = 1;
	    if (image == 2) switch (p) {
	      case 'Y':
		qu8 = '&';
		break;
	      case 'N':
		qu8 = image = 0;
		break;
	      default:
		if (isalnum(p) == 0)	/* provided punctuation */
		    qu8 = p;
		else
		    qu8 = image = 0;
		break;
	    }			/* end inner switch		*/
	    break;

	  case 8:			/* REPT			*/
	    if (p == '~')
		rptflg = TRUE;
	    break;

	  default:			/* CHKT, CAPAS etc.	*/
	    break;

    }	}			/* end while & outer switch	*/

    if (i == 0)				/* no data[6]		*/
	qu8 = 0;
    if ( (qu8 == 0) && (image == 2) )	/* invlaid setting	*/
	image = 0;
    if (list > 2) {			/* debug only		*/
	printf("\rDbg: Parameters in, 8th-bit setting now %d ",image);
#ifndef MPUZ80
	printf("(%s)",images[image]);
	if (image == 2)
	    printf(", prefix %c. ",qu8);
	else
	    printf(". ");
#endif
    }
    return;
}			/* end of rpar()			*/




show5(text)
/* display up to 5 lines of text at bottom of screen	*/
char **text;
{
    char	c,  *hp;
    int		lin;

    clear5();			/* clear it first	*/
    lin = (SCRBOT+3)*256;
    for (c=0; c<5; ++c) {
	if ( (hp = text[c]) == 0 )
	    break;
	vtline(lin,hp);
	if ( (lin += 256) > 24*256)
	    break;
    }
    return;
}			/* end of show5()			*/



spar(data)		/* fill up packet with own parameters	*/
char	*data;
/* returns length of parameter block (6, 7 or 9)		*/
{
    char	len;

    data[0] = tochar(spsiz);
    data[1] = tochar(MYTIME);
    data[2] = tochar(MYPAD);
    data[3] = ctl(0);
    data[4] = tochar(MYEOL);
    data[5] = MYQUOTE;
    len = 6;				/* min length		*/
    if (image == 2) {
	data[6] = 'Y';			/*  feed back 8-quote	*/
	if (qu8 == 0)
	    qu8 = '&';
	len = 7;
    }
    else {
	data[6] = 'N';			/*  otherwise reject	*/
	qu8 = 0;
    }
    if (rptflg) {			/* if repeat-quoting	*/
	data[7] = '1';			/*  1-byte checksum	*/
	data[8] = '~';			/*  only ~ for repeating*/
	len = 9;
    }
    if (list > 2) {			/* debug only		*/
	printf("\rDbg: Parameters out, 8th-bit setting now %d ",image);
#ifndef MPUZ80
	printf("(%s)",images[image]);
	if (image == 2)
	    printf(", prefix %c. ",qu8);
	else
	    printf(". ");
#endif
    }
    return(len);
}			/* end of spar()			*/



/**********************  End of KUTIL2.C  **************************/

