/************************************************************************/ /* 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 **************************/