C-KERMIT (Part 10 of 10)
gregg at okstate.UUCP
gregg at okstate.UUCP
Fri Mar 8 17:53:00 AEST 1985
echo x - ckcmd.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckcmd.c
Xchar *cmdv = "Unix cmd package V1.0(015) 27 Feb 85";
X
X/* C K C M D -- Interactive command package for Unix */
X/*
X Modelled after the DECSYSTEM-20 command parser (the COMND JSYS)
X
X Features:
X . parses and verifies keywords, text strings, numbers, and other data
X . displays appropriate menu or help message when user types "?"
X . does keyword and filename completion when user types ESC
X . accepts any unique abbreviation for a keyword
X . allows keywords to have attributes, like "invisible"
X . can supply defaults for fields omitted by user
X . provides command line editing (character, word, and line deletion)
X . accepts input from keyboard, command files, or redirected stdin
X . allows for full or half duplex operation, character or line input
X . settable prompt, protected from deletion
X
X Functions:
X cmsetp - Set prompt
X cmsavp - Save current prompt
X prompt - Issue prompt
X cmini - Clear the command buffer (before parsing a new command)
X cmres - Reset command buffer pointers (before reparsing)
X cmkey - Parse a keyword
X cmnum - Parse a number
X cmifi - Parse an input file name
X cmofi - Parse an output file name
X cmfld - Parse an arbitrary field
X cmtxt - Parse a text string
X cmcfm - Parse command confirmation (end of line)
X stripq - Strip out backslash quotes from a string.
X
X Return codes:
X -3: no input provided when required
X -2: input was invalid
X -1: reparse required (user deleted into a preceding field)
X 0 or greater: success
X See individual functions for greater detail.
X
X Before using these routines, the caller should #include ckcmd.h, and
X set the program's prompt by calling cmsetp(). If the file parsing
X functions cmifi and cmofi are to be used, this module must be linked
X with a ckz??? file system support module for the appropriate system,
X e.g. ckzbsd for Berkeley Unix. If the caller puts the terminal in
X character wakeup ("cbreak") mode with no echo, then these functions will
X provide line editing -- character, word, and line deletion, as well as
X keyword and filename completion upon ESC and help strings, keyword, or
X file menus upon '?'. If the caller puts the terminal into character
X wakeup/noecho mode, care should be taken to restore it before exit from
X or interruption of the program. If the character wakeup mode is not
X set, the system's own line editor may be used.
X
X Author: Frank da Cruz (SY.FDC at CU20B),
X Columbia University Center for Computing Activities, Jan 1985.
X Copyright (C) 1985, Trustees of Columbia University in the City of New York.
X Permission is granted to any individual or institution to copy or use this
X software except for explicitly commercial purposes, provided this copyright
X notice is retained.
X*/
X
X/* Includes */
X
X#include <stdio.h> /* Standard C I/O package */
X#include <ctype.h> /* Character types */
X#include "ckcmd.h" /* Command parsing definitions */
X#include "ckdebu.h" /* Formats for debug() */
X
X/* Local variables */
X
Xint psetf = 0, /* Flag that prompt has been set */
X cc = 0, /* Character count */
X dpx = 0; /* Duplex (0 = full) */
X
Xint hw = HLPLW, /* Help line width */
X hc = HLPCW, /* Help line column width */
X hh, /* Current help column number */
X hx; /* Current help line position */
X
X#define PROML 60 /* Maximum length for prompt */
X
Xchar cmprom[PROML+1]; /* Program's prompt */
Xchar *dfprom = "Command? "; /* Default prompt */
X
Xint cmflgs; /* Command flags */
X
Xchar cmdbuf[CMDBL+4]; /* Command buffer */
Xchar hlpbuf[HLPBL+4]; /* Help string buffer */
Xchar atmbuf[ATMBL+4]; /* Atom buffer */
Xchar filbuf[ATMBL+4]; /* File name buffer */
X
X/* Command buffer pointers */
X
Xstatic char *bp, /* Current command buffer position */
X *pp, /* Start of current field */
X *np; /* Start of next field */
X
X/* C M S E T P -- Set the program prompt. */
X
Xcmsetp(s) char *s; {
X char *strncpy();
X psetf = 1; /* Flag that prompt has been set. */
X strncpy(cmprom,s,PROML - 1); /* Copy the string. */
X cmprom[PROML] = NUL;
X}
X
X
X/* C M S A V P -- Save a copy of the current prompt. */
X
Xcmsavp(s,n) int n; char s[]; {
X strncpy(s,cmprom,n-1);
X s[n] = NUL;
X}
X
X
X/* P R O M P T -- Issue the program prompt. */
X
Xprompt() {
X if (psetf == 0) cmsetp(dfprom); /* If no prompt set, set default. */
X printf("\r%s",cmprom); /* Print the prompt. */
X}
X
X
X/* C M R E S -- Reset pointers to beginning of command buffer. */
X
Xcmres() {
X cc = 0; /* Reset character counter. */
X pp = np = bp = cmdbuf; /* Point to command buffer. */
X cmflgs = -5; /* Parse not yet started. */
X}
X
X
X/* C M I N I -- Clear the command and atom buffers, reset pointers. */
X
X/*
XThe argument specifies who is to echo the user's typein --
X 1 means the cmd package echoes
X 0 somebody else (system, front end, terminal) echoes
X*/
Xcmini(d) int d; {
X for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
X *atmbuf = NUL;
X dpx = d;
X cmres();
X}
X
Xstripq(s) char *s; { /* Function to strip '\' quotes */
X char *t;
X while (*s) {
X if (*s == '\\') {
X for (t = s; *t != '\0'; t++) *t = *(t+1);
X }
X s++;
X }
X}
X
X/* C M N U M -- Parse a number in the indicated radix */
X
X/* For now, only works for positive numbers in base 10. */
X
X/*
X Returns
X -3 if no input present when required,
X -2 if user typed an illegal number,
X -1 if reparse needed,
X 0 otherwise, with n set to number that was parsed
X*/
Xcmnum(xhlp,xdef,radix,n) char *xhlp, *xdef; int radix, *n; {
X int x; char *s;
X
X if (radix != 10) { /* Just do base 10 for now */
X printf("cmnum: illegal radix - %d\n",radix);
X return(-1);
X }
X
X x = cmfld(xhlp,xdef,&s);
X debug(F101,"cmnum: cmfld","",x);
X if (x < 0) return(x); /* Parse a field */
X
X if (digits(atmbuf)) { /* Convert to number */
X *n = atoi(atmbuf);
X return(x);
X } else {
X printf("\n?not a number - %s\n",s);
X return(-2);
X }
X}
X
X/* C M O F I -- Parse the name of an output file */
X
X/*
X Depends on the external function zchko(); if zchko() not available, use
X cmfld() to parse output file names.
X
X Returns
X -3 if no input present when required,
X -2 if permission would be denied to create the file,
X -1 if reparse needed,
X 0 or 1 otherwise, with xp pointing to name.
X*/
Xcmofi(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
X int x; char *s;
X
X if (*xhlp == NUL) xhlp = "Output file";
X *xp = "";
X
X if ((x = cmfld(xhlp,xdef,&s)) < 0) return(x);
X
X if (chkwld(s)) {
X printf("\n?Wildcards not allowed - %s\n",s);
X return(-2);
X }
X if (zchko(s) < 0) {
X printf("\n?Write permission denied - %s\n",s);
X return(-2);
X } else {
X *xp = s;
X return(x);
X }
X}
X
X/* C M I F I -- Parse the name of an existing file */
X
X/*
X This function depends on the external functions:
X zchki() - Check if input file exists and is readable.
X zxpand() - Expand a wild file specification into a list.
X znext() - Return next file name from list.
X If these functions aren't available, then use cmfld() to parse filenames.
X*/
X/*
X Returns
X -4 EOF
X -3 if no input present when required,
X -2 if file does not exist or is not readable,
X -1 if reparse needed,
X 0 or 1 otherwise, with:
X xp pointing to name,
X wild = 1 if name contains '*' or '?', 0 otherwise.
X*/
Xcmifi(xhlp,xdef,xp,wild) char *xhlp, *xdef, **xp; int *wild; {
X int i, x, xc, y; char *sp;
X
X cc = xc = 0; /* Initialize counts & pointers */
X *xp = "";
X if ((x = cmflgs) != 1) { /* Already confirmed? */
X x = getwd(); /* No, get a word */
X } else {
X cc = setatm(xdef); /* If so, use default, if any. */
X }
X *xp = atmbuf; /* Point to result. */
X *wild = chkwld(*xp);
X
X while (1) {
X xc += cc; /* Count the characters. */
X debug(F111,"cmifi: getwd",atmbuf,xc);
X switch (x) {
X case -4: /* EOF */
X case -2: /* Out of space. */
X case -1: /* Reparse needed */
X return(x);
X
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X case 0: /* SP or NL */
X case 1:
X if (xc == 0) *xp = xdef; /* If no input, return default. */
X else *xp = atmbuf;
X if (**xp == NUL) return(-3); /* If field empty, return -3. */
X
X /* If filespec is wild, see if there are any matches */
X
X *wild = chkwld(*xp);
X debug(F101," *wild","",*wild);
X if (*wild != 0) {
X y = zxpand(*xp);
X if (y == 0) {
X printf("\n?No files match - %s\n",*xp);
X return(-2);
X } else if (y < 0) {
X printf("\n?Too many files match - %s\n",*xp);
X return(-2);
X } else return(x);
X }
X
X /* If not wild, see if it exists and is readable. */
X
X y = zchki(*xp);
X if (y == -3) {
X printf("\n?Read permission denied - %s\n",*xp);
X return(-2);
X } else if (y == -2) {
X printf("\n?File not readable - %s\n",*xp);
X return(-2);
X } else if (y < 0) {
X printf("\n?File not found - %s\n",*xp);
X return(-2);
X }
X return(x);
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X case 2: /* ESC */
X if (xc == 0) {
X if (*xdef != '\0') {
X printf("%s ",xdef); /* If at beginning of field, */
X addbuf(xdef); /* supply default. */
X cc = setatm(xdef);
X } else { /* No default */
X putchar(BEL);
X }
X break;
X }
X if (*wild = chkwld(*xp)) { /* No completion if wild */
X putchar(BEL);
X break;
X }
X sp = atmbuf + cc;
X *sp++ = '*';
X *sp-- = '\0';
X y = zxpand(atmbuf); /* Add * and expand list. */
X *sp = '\0'; /* Remove *. */
X
X if (y == 0) {
X printf("\n?No files match - %s\n",atmbuf);
X return(-2);
X } else if (y < 0) {
X printf("\n?Too many files match - %s\n",atmbuf);
X return(-2);
X } else if (y > 1) { /* Not unique, just beep. */
X putchar(BEL);
X } else { /* Unique, complete it. */
X znext(filbuf); /* Get whole name of file. */
X sp = filbuf + cc; /* Point past what user typed. */
X printf("%s ",sp); /* Complete the name. */
X addbuf(sp); /* Add the characters to cmdbuf. */
X setatm(pp); /* And to atmbuf. */
X *xp = atmbuf; /* Return pointer to atmbuf. */
X return(cmflgs = 0);
X }
X break;
X
X/* cont'd... */
X
X/* ...cmifi(), cont'd */
X
X
X case 3: /* Question mark */
X if (*xhlp == NUL)
X printf(" Input file specification");
X else
X printf(" %s",xhlp);
X if (xc > 0) {
X sp = atmbuf + cc; /* Insert * at end */
X *sp++ = '*';
X *sp-- = '\0';
X y = zxpand(atmbuf);
X *sp = '\0';
X if (y == 0) {
X printf("\n?No files match - %s\n",atmbuf);
X return(-2);
X } else if (y < 0) {
X printf("\n?Too many file match - %s\n",atmbuf);
X return(-2);
X } else {
X printf(", one of the following:\n");
X clrhlp();
X for (i = 0; i < y; i++) {
X znext(filbuf);
X addhlp(filbuf);
X }
X dmphlp();
X }
X } else printf("\n");
X printf("%s%s",cmprom,cmdbuf);
X break;
X }
X x = getwd();
X }
X}
X
X
X
X/* C H K W L D -- Check for wildcard characters '*' or '?' */
X
Xchkwld(s) char *s; {
X
X for ( ; *s != '\0'; s++) {
X if ((*s == '*') || (*s == '?'))
X return(1);
X }
X return(0);
X}
X
X/* C M F L D -- Parse an arbitrary field */
X/*
X Returns
X -3 if no input present when required,
X -2 if field too big for buffer,
X -1 if reparse needed,
X 0 otherwise, xp pointing to string result.
X*/
Xcmfld(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
X int x, xc;
X
X cc = xc = 0; /* Initialize counts & pointers */
X *xp = "";
X if ((x = cmflgs) != 1) { /* Already confirmed? */
X x = getwd(); /* No, get a word */
X } else {
X cc = setatm(xdef); /* If so, use default, if any. */
X }
X *xp = atmbuf; /* Point to result. */
X
X while (1) {
X xc += cc; /* Count the characters. */
X debug(F111,"cmfld: getwd",atmbuf,xc);
X debug(F101," x","",x);
X switch (x) {
X case -4: /* EOF */
X case -2: /* Out of space. */
X case -1: /* Reparse needed */
X return(x);
X case 0: /* SP or NL */
X case 1:
X if (xc == 0) *xp = xdef; /* If no input, return default. */
X else *xp = atmbuf;
X if (**xp == NUL) x = -3; /* If field empty, return -3. */
X return(x);
X case 2: /* ESC *** (maybe treat as SP) */
X if (xc == 0) {
X printf("%s ",xdef); /* If at beginning of field, */
X addbuf(xdef); /* supply default. */
X cc = setatm(xdef);
X } else {
X putchar(BEL); /* Beep if already into field. */
X }
X break;
X case 3: /* Question mark */
X if (*xhlp == NUL)
X printf(" Please complete this field");
X else
X printf(" %s",xhlp);
X printf("\n%s%s",cmprom,cmdbuf);
X break;
X }
X x = getwd();
X }
X}
X
X/* C M T X T -- Get a text string, including confirmation */
X
X/*
X Print help message 'xhlp' if ? typed, supply default 'xdef' if null
X string typed. Returns
X
X -1 if reparse needed or buffer overflows.
X 1 otherwise.
X
X with cmflgs set to return code, and xp pointing to result string.
X*/
X
Xcmtxt(xhlp,xdef,xp) char *xhlp; char *xdef; char **xp; {
X
X int x, xc;
X
X cc = xc = 0; /* Start counters off at 0 */
X *xp = ""; /* And pointer to null string. */
X *atmbuf = NUL; /* And empty atom buffer. */
X if ((x = cmflgs) != 1) {
X x = getwd(); /* Get first word. */
X *xp = pp; /* Save pointer to it. */
X }
X while (1) {
X xc += cc; /* Accumulate count. */
X debug(F111,"cmtxt: getwd",atmbuf,xc);
X switch (x) {
X case -4: /* EOF */
X case -2: /* Overflow */
X case -1: /* Deletion */
X return(x);
X case 0: /* Space */
X break;
X case 1: /* CR or LF */
X if (xc == 0) *xp = xdef;
X return(x);
X case 2: /* ESC */
X if (xc == 0) {
X printf("%s ",xdef);
X cc = addbuf(xdef);
X } else {
X putchar(BEL);
X }
X break;
X case 3: /* Question Mark */
X if (*xhlp == NUL)
X printf(" Text string");
X else
X printf(" %s",xhlp);
X printf("\n%s%s",cmprom,cmdbuf);
X break;
X default:
X printf("\n?Unexpected return code from getwd() - %d\n",x);
X return(-2);
X }
X x = getwd();
X }
X}
X
X/* C M K E Y -- Parse a keyword */
X
X/*
X Call with:
X table -- keyword table, in 'struct keytab' format;
X n -- number of entries in table;
X xhlp -- pointer to help string;
X xdef -- pointer to default keyword;
X
X Returns:
X -3 -- no input supplied and no default available
X -2 -- input doesn't uniquely match a keyword in the table
X -1 -- user deleted too much, command reparse required
X n >= 0 -- value associated with keyword
X*/
X
Xcmkey(table,n,xhlp,xdef) struct keytab table[]; int n; char *xhlp, *xdef; {
X int i, y, z, zz, xc;
X char *xp;
X
X xc = cc = 0; /* Clear character counters. */
X
X if ((zz = cmflgs) == 1) /* Command already entered? */
X setatm(xdef);
X else zz = getwd();
X
Xdebug(F101,"cmkey: table length","",n);
Xdebug(F101," cmflgs","",cmflgs);
Xdebug(F101," zz","",zz);
Xwhile (1) {
X xc += cc;
X debug(F111,"cmkey: getwd",atmbuf,xc);
X
X switch(zz) {
X case -4: /* EOF */
X case -2: /* Buffer overflow */
X case -1: /* Or user did some deleting. */
X return(zz);
X
X case 0: /* User terminated word with space */
X case 1: /* or newline */
X if (cc == 0) setatm(xdef);
X y = lookup(table,atmbuf,n,&z);
X switch (y) {
X case -2:
X printf("\n?Ambiguous - %s\n",atmbuf);
X return(cmflgs = -2);
X case -1:
X printf("\n?Invalid - %s\n",atmbuf);
X return(cmflgs = -2);
X default:
X break;
X }
X return(y);
X
X/* cont'd... */
X
X/* ...cmkey(), cont'd */
X
X case 2: /* User terminated word with ESC */
X if (cc == 0) {
X if (*xdef != NUL) { /* Nothing in atmbuf */
X printf("%s ",xdef); /* Supply default if any */
X addbuf(xdef);
X cc = setatm(xdef);
X debug(F111,"cmkey: default",atmbuf,cc);
X } else {
X putchar(BEL); /* No default, just beep */
X break;
X }
X }
X y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */
X debug(F111,"cmkey: esc",atmbuf,y);
X if (y == -2) {
X putchar(BEL);
X break;
X }
X if (y == -1) {
X printf("\n?Invalid - %s\n",atmbuf);
X return(cmflgs = -2);
X }
X xp = table[z].kwd + cc;
X printf("%s ",xp);
X addbuf(xp);
X debug(F110,"cmkey: addbuf",cmdbuf,0);
X return(y);
X
X/* cont'd... */
X
X/* ...cmkey(), cont'd */
X
X case 3: /* User terminated word with "?" */
X y = lookup(table,atmbuf,n,&z);
X if (y > -1) {
X printf(" %s\n%s%s",table[z].kwd,cmprom,cmdbuf);
X break;
X } else if (y == -1) {
X printf("\n?Invalid\n");
X return(cmflgs = -2);
X }
X
X if (*xhlp == NUL)
X printf(" One of the following:\n");
X else
X printf(" %s, one of the following:\n",xhlp);
X
X clrhlp();
X for (i = 0; i < n; i++) {
X if (!strncmp(table[i].kwd,atmbuf,cc)
X && !test(table[i].flgs,CM_INV))
X addhlp(table[i].kwd);
X }
X dmphlp();
X printf("%s%s", cmprom, cmdbuf);
X break;
X
X default:
X printf("\n%d - Unexpected return code from getwd\n",zz);
X return(cmflgs = -2);
X }
X zz = getwd();
X }
X}
X
X/* C M C F M -- Parse command confirmation (end of line) */
X
X/*
X Returns
X -2: User typed anything but whitespace or newline
X -1: Reparse needed
X 0: Confirmation was received
X*/
X
Xcmcfm() {
X int x, xc;
X
X debug(F101,"cmcfm: cmflgs","",cmflgs);
X
X xc = cc = 0;
X if (cmflgs == 1) return(0);
X
X while (1) {
X x = getwd();
X xc += cc;
X debug(F111,"cmcfm: getwd",atmbuf,xc);
X switch (x) {
X case -4: /* EOF */
X case -2:
X case -1:
X return(x);
X
X case 0: /* Space */
X continue;
X case 1: /* End of line */
X if (xc > 0) {
X printf("?Not confirmed - %s\n",atmbuf);
X return(-2);
X } else return(0);
X case 2:
X putchar(BEL);
X continue;
X
X case 3:
X if (xc > 0) {
X printf("\n?Not confirmed - %s\n",atmbuf);
X return(-2);
X }
X printf("\n Type a carriage return to confirm the command\n");
X printf("%s%s",cmprom,cmdbuf);
X continue;
X }
X }
X}
X
X/* Keyword help routines */
X
X
X/* C L R H L P -- Initialize/Clear the help line buffer */
X
Xclrhlp() { /* Clear the help buffer */
X hlpbuf[0] = NUL;
X hh = hx = 0;
X}
X
X
X/* A D D H L P -- Add a string to the help line buffer */
X
Xaddhlp(s) char *s; { /* Add a word to the help buffer */
X int j;
X
X hh++; /* Count this column */
X
X for (j = 0; j < hc; j++) { /* Fill the column */
X if (*s != NUL) /* First with chars from the string */
X hlpbuf[hx++] = *s++;
X else {
X if (hh < (hw / hc)) /* Then with spaces */
X hlpbuf[hx++] = SP;
X else {
X hlpbuf[hx++] = NUL; /* If last column, no spaces. */
X dmphlp(); /* Print it. */
X return;
X }
X }
X }
X if (*s != NUL) /* Still some chars left in string? */
X hlpbuf[hx-1] = '+'; /* Mark as too long for column. */
X}
X
X
X/* D M P H L P -- Dump the help line buffer */
X
Xdmphlp() { /* Print the help buffer */
X hlpbuf[hx++] = NUL;
X printf(" %s\n",hlpbuf);
X clrhlp();
X}
X
X/* L O O K U P -- Lookup the string in the given array of strings */
X
X/*
X Call this way: v = lookup(table,word,n,&x);
X
X table - a 'struct keytab' table.
X word - the target string to look up in the table.
X n - the number of elements in the table.
X x - address of an integer for returning the table array index.
X
X The keyword table must be arranged in ascending alphabetical order, and
X all letters must be lowercase.
X
X Returns the keyword's associated value ( zero or greater ) if found,
X with the variable x set to the array index, or:
X
X -3 if nothing to look up (target was null),
X -2 if ambiguous,
X -1 if not found.
X
X A match is successful if the target matches a keyword exactly, or if
X the target is a prefix of exactly one keyword. It is ambiguous if the
X target matches two or more keywords from the table.
X*/
X
Xlookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
X
X int i, v, cmdlen;
X
X/* Lowercase & get length of target, if it's null return code -3. */
X
X if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
X
X/* Not null, look it up */
X
X for (i = 0; i < n-1; i++) {
X if (!strcmp(table[i].kwd,cmd) ||
X ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
X strncmp(table[i+1].kwd,cmd,cmdlen))) {
X *x = i;
X return(table[i].val);
X }
X if (v) return(-2);
X }
X
X/* Last (or only) element */
X
X if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
X *x = n-1;
X return(table[n-1].val);
X } else return(-1);
X}
X
X/* G E T W D -- Gets a "word" from the command input stream */
X
X/*
XUsage: retcode = getwd();
X
XReturns:
X -4 if end of file (e.g. pipe broken)
X -2 if command buffer overflows
X -1 if user did some deleting
X 0 if word terminates with SP or tab
X 1 if ... CR
X 2 if ... ESC
X 3 if ... ?
X
XWith:
X pp pointing to beginning of word in buffer
X bp pointing to after current position
X atmbuf containing a copy of the word
X cc containing the number of characters in the word copied to atmbuf
X*/
Xgetwd() {
X
X int c; /* Current char */
X static int inword = 0; /* Flag for start of word found */
X int quote = 0; /* Flag for quote character */
X int echof = 0; /* Flag for whether to echo */
X int ignore = 0;
X
X pp = np; /* Start of current field */
X debug(F101,"getwd: cmdbuf","",(int) cmdbuf);
X debug(F101," bp","",(int) bp);
X debug(F101," pp","",(int) pp);
X debug(F110," cmdbuf",cmdbuf,0);
X
X while (bp < cmdbuf+CMDBL) { /* Loop */
X
X ignore = echof = 0; /* Flag for whether to echo */
X
X if ((c = *bp) == NUL) { /* Get next character */
X if (dpx) echof = 1; /* from reparse buffer */
X c = getchar(); /* or from tty. */
X if (c == EOF) return(-4);
X } else ignore = 1;
X
X if (quote == 0) {
X
X if (!ignore && (c == '\\')) { /* Quote character */
X quote = 1;
X continue;
X }
X if (c == FF) { /* Formfeed. */
X c = NL; /* Replace with newline */
X system("clear"); /* and clear the screen. */
X }
X
X if (c == HT) c = SP; /* Substitute space for tab. */
X
X/* cont'd... */
X
X/* ...getwd(), cont'd */
X
X if (c == SP) { /* If space */
X *bp++ = c; /* deposit it in buffer. */
X if (echof) putchar(c); /* echo it. */
X if (inword == 0) { /* If leading, gobble it. */
X pp++;
X continue;
X } else { /* If terminating, return. */
X np = bp;
X setatm(pp);
X inword = 0;
X return(cmflgs = 0);
X }
X }
X if (c == NL) { /* CR, LF */
X *bp = NUL; /* End the string */
X if (echof) putchar(c); /* Echo the typein */
X np = bp; /* Where to start next field. */
X setatm(pp); /* Copy this field to atom buffer. */
X inword = 0;
X return(cmflgs = 1);
X }
X if (!ignore && (c == '?')) { /* Question mark */
X putchar(c);
X *bp = NUL;
X setatm(pp);
X return(cmflgs = 3);
X }
X if (c == ESC) { /* ESC */
X *bp = NUL;
X setatm(pp);
X return(cmflgs = 2);
X }
X if (c == BS || c == RUB) { /* Character deletion */
X if (bp > cmdbuf) { /* If still in buffer... */
X printf("\b \b"); /* erase character from screen, */
X bp--; /* point behind it, */
X if (*bp == SP) inword = 0; /* Flag if current field gone */
X *bp = NUL; /* Erase character from buffer. */
X } else { /* Otherwise, */
X putchar(BEL); /* beep, */
X cmres(); /* and start parsing a new command. */
X }
X if (pp < bp) continue;
X else return(cmflgs = -1);
X }
X if (c == LDEL) { /* ^U, line deletion */
X while ((bp--) > cmdbuf) {
X printf("\b \b");
X *bp = NUL;
X }
X cmres(); /* Restart the command. */
X inword = 0;
X return(cmflgs = -2);
X }
X
X/* cont'd... */
X
X/* ...getwd(), cont'd */
X
X if (c == WDEL) { /* ^W, word deletion */
X if (bp <= cmdbuf) { /* Beep if nothing to delete */
X putchar(BEL);
X cmres();
X return(cmflgs = -1);
X }
X bp--;
X for ( ; (bp >= cmdbuf) && (*bp == SP) ; bp--) {
X printf("\b \b");
X *bp = NUL;
X }
X for ( ; (bp >= cmdbuf) && (*bp != SP) ; bp--) {
X printf("\b \b");
X *bp = NUL;
X }
X *bp++ == NUL;
X inword = 0;
X return(cmflgs = -1);
X }
X if (c == RDIS) { /* ^R, redisplay */
X *bp = NUL;
X printf("\n%s%s",cmprom,cmdbuf);
X continue;
X }
X }
X if (echof) putchar(c); /* If tty input, echo. */
X inword = 1; /* Flag we're in a word. */
X quote = 0; /* Turn off quote. */
X *bp++ = c; /* And deposit it. */
X } /* end of big while */
X putchar(BEL); /* Get here if... */
X printf("\n?Buffer full\n");
X return(cmflgs = -2);
X}
X
X/* Utility functions */
X
X/* A D D B U F -- Add the string pointed to by cp to the command buffer */
X
Xaddbuf(cp) char *cp; {
X int len = 0;
X while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) {
X *bp++ = *cp++; /* Copy and */
X len++; /* count the characters. */
X }
X *bp++ = SP; /* Put a space at the end */
X *bp = NUL; /* Terminate with a null */
X np = bp; /* Update the next-field pointer */
X return(len); /* Return the length */
X}
X
X/* S E T A T M -- Deposit a string in the atom buffer */
X
Xsetatm(cp) char *cp; {
X char *ap;
X cc = 0;
X ap = atmbuf;
X *ap = NUL;
X while (*cp == SP) cp++;
X while ((*cp != SP) && (*cp != NL) && (*cp != NUL)) {
X *ap++ = *cp++;
X cc++;
X }
X *ap++ = NUL;
X return(cc); /* Return length */
X}
X
X/* D I G I T S -- Verify that all the characters in line are digits */
X
Xdigits(s) char *s; {
X while (*s) {
X if (!isdigit(*s)) return(0);
X s++;
X }
X return(1);
X}
X
X/* L O W E R -- Lowercase a string */
X
Xlower(s) char *s; {
X int n = 0;
X while (*s) {
X if (isupper(*s)) *s = tolower(*s);
X s++, n++;
X }
X return(n);
X}
X
X/* T E S T -- Bit test */
X
Xtest(x,m) int x, m; { /* Returns 1 if any bits from m are on in x, else 0 */
X return((x & m) ? 1 : 0);
X}
!FUNKY!STUFF!
echo x - ckconu.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckconu.c
Xchar *connv = "Connect Command for Unix, V4.2(006) 5 March 85";
X
X/* C K C O N U -- Dumb terminal connection to remote system, for Unix */
X/*
X This module should work under all versions of Unix. It calls externally
X defined system-dependent functions for i/o, but depends upon the existence
X of the fork() function.
X
X Enhanced by H. Fischer to detect when child process (modem reader)
X reports that the communications line has been broken and hang up.
X Also enhanced to allow escaping from connect state to command
X interpreter, to allow sending/receiving without breaking connection.
X*/
X
X#include "ckermi.h"
X#include <signal.h>
X#include <setjmp.h>
X
X#ifndef SIGUSR1
X#define SIGUSR1 16
X#endif
X
Xextern int local, speed, escape, duplex, parity, flow, seslog, mdmtyp;
Xextern char ttname[], sesfil[];
X
Xint i, active; /* Variables global to this module */
Xchar *chstr();
X
X#define LBUFL 100 /* Line buffer */
Xchar lbuf[LBUFL];
X
X/* Connect state parent/child communication signal handlers */
X
Xstatic jmp_buf env_con; /* Envir ptr for connect errors */
X
Xstatic
Xconn_int() { /* Modem read failure handler, */
X longjmp(env_con,1); /* notifies parent process to stop */
X}
X
X/* C O N E C T -- Perform terminal connection */
X
Xconect() {
X int pid, /* process id of child (modem reader) */
X parent_id, /* process id of parent (keyboard reader) */
X n;
X int c; /* c is a character, but must be signed
X integer to pass thru -1, which is the
X modem disconnection signal, and is
X different from the character 0377 */
X char errmsg[50], *erp;
X
X if (!local) {
X printf("Sorry, you must 'set line' first\n");
X return(-2);
X }
X if (speed < 0) {
X printf("Sorry, you must 'set speed' first\n");
X return(-2);
X }
X if ((escape < 0) || (escape > 0177)) {
X printf("Your escape character is not ASCII - %d\n",escape);
X return(-2);
X }
X if (ttopen(ttname,local,mdmtyp) < 0) {
X erp = errmsg;
X sprintf(erp,"Sorry, can't open %s",ttname);
X perror(errmsg);
X return(-2);
X }
X printf("Connecting thru %s, speed %d.\r\n",ttname,speed);
X printf("The escape character is %s (%d).\r\n",chstr(escape),escape);
X printf("Type the escape character followed by C to get back,\r\n");
X printf("or followed by ? to see other options.\r\n");
X if (seslog) printf("(Session logged to %s.)\r\n",sesfil);
X
X/* Condition console terminal and communication line */
X
X if (conbin(escape) < 0) {
X printf("Sorry, can't condition console terminal\n");
X return(-2);
X }
X if (ttvt(speed,flow) < 0) {
X conres();
X printf("Sorry, Can't condition communication line\n");
X return(-2);
X }
X
X/* cont'd... */
X
X/* ...connect, cont'd */
X
X
X parent_id = getpid(); /* get parent id for signalling */
X pid = fork(); /* All ok, make a fork */
X if (pid) {
X active = 1; /* This fork reads, sends keystrokes */
X if (!setjmp(env_con)) { /* comm error in child process */
X signal(SIGUSR1,conn_int); /* routine for child process exit */
X while (active) {
X c = coninc(0) & 0177;
X if (c == escape) { /* Look for escape char */
X c = coninc(0) & 0177;
X doesc(c);
X } else { /* Ordinary character */
X ttoc(dopar(c)); /* Send it out with desired parity */
X if (duplex) { /* Half duplex? */
X conoc(c); /* Yes, also echo it. */
X if (seslog) zchout(ZSFILE,c); /* And maybe log it. */
X }
X }
X }
X } /* come here on death of child */
X kill(pid,9); /* Done, kill inferior. */
X wait(0); /* Wait till gone. */
X conres(); /* Reset the console. */
X printf("C-Kermit Disconnected\n");
X return(0);
X
X } else { /* Inferior reads, prints port input */
X
X while (1) { /* Fresh read, wait for a character */
X if ((c = ttinc(0)) < 0) { /* Comm line hangup detected*/
X printf("\r\nC-Kermit: Communications line failure\r\n");
X kill(parent_id,SIGUSR1); /* notify parent. */
X pause(); /* Wait to be killed by parent. */
X }
X c &= 0177; /* Got a char, strip parity. */
X conoc(c); /* Put it on the screen. */
X if (seslog) zchout(ZSFILE,c); /* If logging, log it. */
X n = ttchk(); /* Any more left in buffer? */
X if (n > 0) {
X if (n > LBUFL) n = LBUFL; /* Get them all at once. */
X if ((n = ttxin(n,lbuf)) > 0) {
X for (i = 0; i < n; i++) lbuf[i] &= 0177;
X conxo(n,lbuf);
X if (seslog) zsoutx(ZSFILE,lbuf,n);
X }
X }
X }
X }
X}
X
X/* H C O N N E -- Give help message for connect. */
X
Xhconne() {
X int c;
X static char *hlpmsg[] = {"\
X\r\nC to close the connection, or:",
X"\r\n S for status",
X"\r\n ? for help",
X"\r\n B to send a BREAK",
X"\r\n 0 to send a null",
X"\r\n escape character twice to send the escape character.\r\n\r\n",
X"" };
X
X conola(hlpmsg); /* Print the help message. */
X conol("Command>"); /* Prompt for command. */
X c = coninc(0);
X conoc(c); /* Echo it. */
X conoll("");
X c &= 0177; /* Strip any parity. */
X return(c); /* Return it. */
X}
X
X
X/* C H S T R -- Make a printable string out of a character */
X
Xchar *
Xchstr(c) int c; {
X static char s[8];
X char *cp = s;
X
X if (c < SP) {
X sprintf(cp,"CTRL-%c",ctl(c));
X } else sprintf(cp,"'%c'\n",c);
X cp = s;
X return(cp);
X}
X
X/* D O E S C -- Process an escape character argument */
X
Xdoesc(c) char c; {
X int d;
X
X c &= 0177;
X while (1) {
X if (c == escape) { /* Send escape character */
X d = dopar(c);
X ttoc(d);
X return;
X } else /* Or else look it up below. */
X if (isupper(c)) c = tolower(c);
X
X switch (c) {
X
X case 'c': /* Close connection */
X case '\03':
X active = 0;
X conol("\r\n");
X return;
X
X case 'b': /* Send a BREAK */
X case '\02':
X ttsndb();
X return;
X
X case 's': /* Status */
X case '\023':
X conol("\r\nConnected thru ");
X conoll(ttname);
X if (seslog) {
X conol(", logging to ");
X conol(sesfil);
X }
X return;
X
X case '?': /* Help */
X c = hconne();
X continue;
X
X case '0': /* Send a null */
X c = '\0';
X d = dopar(c);
X ttoc(d);
X return;
X
X case SP: /* Space, ignore */
X return;
X
X default: /* Other */
X conoc(BEL); /* Invalid esc arg, beep */
X return;
X }
X }
X}
!FUNKY!STUFF!
echo x - ckdial.c
sed '1,$s/^X//' <<\!FUNKY!STUFF! > ckdial.c
Xchar *dialv = "Dial Command for Unix, V0.0(005) 5 Mar 85";
X
X/* C K D I A L -- Dialing program for connection to remote system */
X
X/*
X This module should work under all versions of Unix. It calls externally
X defined system-depended functions for i/o, but depends upon the existence
X of various modem control functions.
X
X Author: Herm Fischer, Litton Data Systems, Van Nuys CA (HFISCHER at USC-ECLB)
X*/
X
X#include "ckermi.h"
X#include <signal.h>
X#include <setjmp.h>
X#include "ckcmd.h"
X
Xextern int local, speed, flow, mdmtyp;
Xextern char ttname[], sesfil[];
X
X#define HAYES 1 /* for mdmtyp settings */
X#define VENTEL 2
X#define HAYESNV 3 /* internal use, non-verbal V0 setting */
X
Xstruct keytab mdmtab[] = { /* Modem types for command parsing */
X "direct", 0, 0,
X "hayes", HAYES, 0,
X "ventel", VENTEL, 0
X};
Xint nmdm = (sizeof(mdmtab) / sizeof(struct keytab));
X
X#define DIALING 4 /* for ttvt parameter */
X#define CONNECT 5
X
X#define CONNECTED 1 /* for completion status */
X#define FAILED 2
X
Xstatic int tries = 0;
X
X#define LBUFL 100
Xstatic char lbuf[LBUFL];
Xstatic char *lbp;
X
Xstatic jmp_buf sjbuf;
X
Xstatic
Xtimerh() { /* timer interrupt handler */
X longjmp(sjbuf,1);
X}
X
Xstatic
Xstripp(s) char *s; { /* parity stripper aid */
X for ( ; *s ; *s++ &= 0177 );
X}
X
X/* D I A L -- Dial up the remote system */
X
Xdial(telnbr) char *telnbr; {
X
X char c;
X char *i;
X int waitct, status;
X char errmsg[50], *erp;
X int augMdm; /* mdmtyp with switch settings added */
X int mdmEcho = 0; /* assume modem does not echo */
X int n;
X int (*savAlrm)(); /* save incomming alarm function */
X
X if (!mdmtyp) {
X printf("Sorry, you must 'set modem' first\n");
X return(-2);
X }
X augMdm = mdmtyp; /* internal use, to add dialer switches info*/
X
X if (!local) {
X printf("Sorry, you must 'set line' first\n");
X return(-2);
X }
X if (speed < 0) {
X printf("Sorry, you must 'set speed' first\n");
X return(-2);
X }
X if (ttopen(ttname,local,mdmtyp) < 0) {/* Open, no wait for carrier */
X erp = errmsg;
X sprintf(erp,"Sorry, can't open %s",ttname);
X perror(errmsg);
X return(-2);
X }
X/* cont'd... */
X /* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X
X waitct = 1*strlen(telnbr) ; /* compute time to dial worst case */
X switch (augMdm) {
X case HAYES:
X case HAYESNV:
X waitct += 35; /* dial tone + carrier waits + slop */
X for (i=telnbr; *i; i++) if (*i == ',') waitct += 2;
X break;
X case VENTEL:
X waitct += 10; /* guess actual time for dialtones */
X waitct += 10; /* ventel's apparent patience for carrier */
X for (i=telnbr; *i; i++) if (*i == '%') waitct += 5;
X break;
X }
X
X printf("Dialing thru %s, speed %d, number %s.\r\n",ttname,speed,telnbr);
X printf("The timeout for completing the call is %d seconds.\r\n",waitct);
X
X/* Condition console terminal and communication line */
X /* place line into "clocal" dialing state */
X if ( ttpkt(speed,DIALING) < 0 ) {
X printf("Sorry, Can't condition communication line\n");
X return(-2);
X }
X
X/* Put modem into command state */
X
X savAlrm = signal(SIGALRM,timerh);
X alarm(10); /* give modem 10 seconds to wake up */
X if (setjmp(sjbuf)) {
X alarm(0);
X signal(SIGALRM,savAlrm); /* cancel timer */
X ttpkt(speed,CONNECT); /* cancel dialing state ioctl */
X printf("Sorry, unable to complete dialed connection\r\n");
X return(-2);
X }
X ttflui(); /* flush input buffer if any */
X
X#define OKAY 1 /* modem attention attempt status */
X#define IGNORE 2
X#define GOT_O -2
X#define GOT_A -3
X
Xswitch (augMdm) {
X case HAYES:
X case HAYESNV:
X while(tries++ < 4) {
X ttol("AT\r",3); /* signal for attention, look for response */
X status = 0;
X while ( status <= 0 ) {
X switch (ttinc(0) & 0177) {
X case 'A': /* echoing, ignore */
X status = GOT_A;
X break;
X case 'T':
X if (status == GOT_A) {
X mdmEcho = 1; /* expect echoing later */
X status = 0;
X break;
X }
X status = IGNORE;
X break;
X case '\n':
X case '\r':
X status = 0;
X break;
X case '0': /* numeric result code */
X augMdm = HAYESNV; /* nonverbal result codes */
X status = OKAY;
X break;
X case 'O': /* maybe English result code*/
X status = GOT_O;
X break;
X case 'K':
X if (status == GOT_O) {
X augMdm = HAYES;
X status = OKAY;
X break;
X } /* else its default anyway */
X default:
X status = IGNORE;
X break;
X }
X }
X if (status == OKAY) break;
X if (status == IGNORE) ttflui();
X sleep(1); /* wait before retrying */
X }
X if (status != 0) break;
X printf("Sorry, can't initialize modem\n");
X ttpkt(speed,CONNECT); /* cancel dialing state ioctl */
X alarm(0);
X signal(SIGALRM,savAlrm); /* cancel timer */
X return(-2);
X
X/* cont'd... */
X /* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X case VENTEL:
X ttoc('\r'); /* Put Ventel into command mode */
X sleep(1);
X ttoc('\r');
X sleep(1);
X ttoc('\r');
X while( (ttinc(0) & 0177) != '$');
X break;
X }
X alarm(0); /* turn off alarm */
X sleep(1); /* give things settling time */
X
X
X/* Dial the number */
X
Xswitch (augMdm) {
X case HAYES:
X case HAYESNV:
X sprintf(lbuf,"AT DT %s\r",telnbr);
X break;
X case VENTEL:
X sprintf(lbuf,"<K%s\r>",telnbr);
X break;
X }
X
X alarm(waitct); /* time to allow for connecting */
X ttflui(); /* clear out stuff from waking modem up */
X ttol(lbuf,strlen(lbuf)); /* send dialing string */
X
X/* cont'd... */
X /* interdigit waits for tone dial */
X/* ...dial, cont'd */
X
X
X/* Check for connection */
X
X status = 0;
X while (status == 0) {
X switch (augMdm) {
X case HAYES:
X for (n = 0; n < LBUFL; n++) lbuf[n] = '\0';
X n = ttinl(lbuf,LBUFL,0,'\n');
X if (n > 2) {
X lbp = lbuf;
X while ((*lbp == '\r') || (*lbp == '\n')) lbp++;
X stripp(lbp);
X if (strncmp(lbp,"CONNECT",7) == 0) status = CONNECTED;
X if (strncmp(lbp,"NO CARRIER",10) == 0) status = FAILED;
X }
X break;
X
X case HAYESNV:
X c = ttinc(0) & 0177;
X if (mdmEcho) { /* sponge up dialing string */
X mdmEcho = c!='\r'; /* until return is echoed */
X break;
X }
X if (c == '1') status = CONNECTED;
X if (c == '3') status = FAILED;
X if (c == '5') status = CONNECTED;
X break;
X
X case VENTEL:
X if ( (ttinc(0) & 0177) == '!') status = CONNECTED;
X break;
X }
X }
X
X/* Place line into modem-control (non-clocal) state */
X
X if (status == 0) printf("Sorry, Can't get response from modem\r\n");
X else if (status == CONNECTED) printf("Connected!\r\n");
X else if (status == FAILED) printf("Sorry, No Carrier\r\n");
X else printf("Failed to complete call\r\n");
X
X ttpkt(speed,CONNECT); /* cancel dialing state ioctl */
X alarm(0);
X signal(SIGALRM,savAlrm); /* cancel timer */
X return((status==CONNECTED) ? 0 : -2);
X}
!FUNKY!STUFF!
More information about the Comp.sources.unix
mailing list