New UNIX Kermit (Part 5 of 5)

Sam Chin tsc2597 at acf4.UUCP
Thu Feb 28 14:04:00 AEST 1985


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by acf4!tsc2597  on Wed Feb 27 22:21:33 EST 1985
# Contents:  ckwart.c ckwart.doc ckwho.txt ckxbsd.bwr ckxbsd.c ckzbsd.c
 
echo x - ckwart.c
sed 's/^@//' > "ckwart.c" <<'@//E*O*F ckwart.c//'
/* W A R T
 *
 * pre-process a lex-like file into a C program.
 *
 * Jeff Damens, Columbia University Center for Computing Activites, 11/84.
 * (Reorganized by Frank da Cruz into a single source module for ease
 * of distribution).
 * Copyright (C) 1984, Trustees of Columbia University.
 * May be copied and used except for explicitly commercial purposes.
 *
 * input format is:
 *  lines to be copied | %state <state names...>
 *  %%
 * <state> | <state,state,...> CHAR  { actions }
 * ...
 *  %%
 */

#include <stdio.h>
#include <ctype.h>

/* token types */

#define SEP 1
#define LBRACK 2
#define RBRACK 3
#define WORD 4
#define COMMA 5

/* storage sizes */

#define MAXSTATES 50			/* max number of states */
#define MAXWORD 50			/* max # of chars/word */
#define SBYTES ((MAXSTATES+7)/8)	/* # of bytes for state bitmask */

/* name of wart function in generated program */

#ifndef FNAME
#define FNAME "wart"
#endif

/* data structure for state information */

typedef unsigned short CHAR;

struct trans { CHAR states[SBYTES];	/* included states */
    	       int anyst;		/* true if this good from any state */
    	       CHAR inchr;		/* input character */
	       int actno;		/* associated action */
	       struct trans *nxt; };	/* next transition */

typedef struct trans *Trans;

/* Variables and tables */

int lines,nstates,nacts;

char tokval[MAXWORD];

int tbl[MAXSTATES*128];



char *txt1 = "\n\
#define BEGIN state =\n\
\n\
int state = 0;\n\
\n";

char *fname = FNAME;		/* function name goes here */

/* rest of program... */

char *txt2 = "()\n\
{\n\
  int c,actno;\n\
  extern int tbl[];\n\
  while (1) {\n\
	c = input();\n\
	if ((actno = tbl[c + state*128]) != -1)\n\
	  switch(actno) {\n";

/* this program's output goes here, followed by final text... */

char *txt3 = "\n    }\n  }\n\}\n\n";

/*
 * turn on the bit associated with the given state
 *
 */
setstate(state,t)
int state;
Trans t;
{
  int idx,msk;
  idx = state/8;			/* byte associated with state */
  msk = 0x80 >> (state % 8);		/* bit mask for state */
  t->states[idx] |= msk;
}

/*
 * see if the state is involved in the transition
 *
 */

teststate(state,t)
int state;
Trans t;
{
  int idx,msk;
  idx = state/8;
  msk = 0x80 >> (state % 8);
  return(t->states[idx] & msk);
}


/*
 * read input from here...
 *
 */

Trans
rdinput(infp,outfp)
FILE *infp,*outfp;
{
  Trans x,rdrules();
  lines = 1;				/* line counter */
  nstates = 0;				/* no states */
  nacts = 0;				/* no actions yet */
  fprintf(outfp,"\n\
/* WARNING -- This C source program generated by Wart preprocessor. */\n");
  fprintf(outfp,"\
/* Do not edit this file; edit the Wart-format source file instead, */\n");
  fprintf(outfp,"\
/* and then run it through Wart to produce a new C source file.     */\n\n");
  initial(infp,outfp);			/* read state names, initial defs */
  prolog(outfp);			/* write out our initial code */
  x = rdrules(infp,outfp);		/* read rules */
  epilogue(outfp);			/* write out epilogue code */
  return(x);
}

/*
 * initial - read initial definitions and state names.  Returns
 * on EOF or %%.
 *
 */

initial(infp,outfp)
FILE *infp,*outfp;
{
  int c;
  char wordbuf[MAXWORD];
  while ((c = getc(infp)) != EOF) {
	if (c == '%') {
			rdword(infp,wordbuf);
			if (strcmp(wordbuf,"states") == 0)
			    rdstates(infp,outfp);
			else if (strcmp(wordbuf,"%") == 0) return;
			else fprintf(outfp,"%%%s",wordbuf);
		      }
	else putc(c,outfp);
	if (c == '\n') lines++;
     }
}

/*
 * boolean function to tell if the given character can be part of
 * a word.
 *
 */
isin(s,c) char *s; int c; {
   for (; *s != '\0'; s++)
      if (*s == c) return(1);
   return(0);
}
isword(c)
int c;
{
  static char special[] = ".%_-$@";	/* these are allowable */
  return(isalnum(c) || isin(special,c));
}

/*
 * read the next word into the given buffer.
 *
 */
rdword(fp,buf)
FILE *fp;
char *buf;
{
  int len = 0,c;
  while (isword(c = getc(fp)) && ++len < MAXWORD) *buf++ = c;
  *buf++ = '\0';			/* tie off word */
  ungetc(c,fp);				/* put break char back */
}

/*
 * read state names, up to a newline.
 *
 */

rdstates(fp,ofp)
FILE *fp,*ofp;
{
  int c;
  char wordbuf[MAXWORD];
  while ((c = getc(fp)) != EOF && c != '\n')
  {
	if (isspace(c)) continue;	/* skip whitespace */
	ungetc(c,fp);			/* put char back */
	rdword(fp,wordbuf);		/* read the whole word */
	enter(wordbuf,++nstates);	/* put into symbol tbl */
	fprintf(ofp,"#define %s %d\n",wordbuf,nstates);
  }
  lines++;
}
		
/*
 * allocate a new, empty transition node
 *
 */

Trans
newtrans()
{
  Trans new;
  int i;
  new = (Trans) malloc(sizeof (struct trans));
  for (i=0; i<SBYTES; i++) new->states[i] = 0;
  new->anyst = 0;
  new->nxt = NULL;
  return(new);
}

/*
 * read all the rules.
 *
 */

Trans
rdrules(fp,out)
FILE *fp,*out;
{
  Trans head,cur,prev;
  int curtok,i;
  head = cur = NULL;
  while ((curtok = gettoken(fp)) != SEP) 

	switch(curtok) {
		case LBRACK: if (cur == NULL) cur = newtrans();
		    	     else fatal("duplicate state list");
			     statelist(fp,cur);/* set states */
			     continue;	/* prepare to read char */

		case WORD:   if (strlen(tokval) != 1)
					fatal("multiple chars in state");
			     if (cur == NULL) {
				cur = newtrans();
				cur->anyst = 1;
				}
			     cur->actno = ++nacts;
			     cur->inchr = tokval[0];
			     if (head == NULL) head = cur;
			     else prev->nxt = cur;
			     prev = cur;
			     cur = NULL;
			     copyact(fp,out,nacts);
			     break; 
		 default: fatal("bad input format");
	     }
	
   return(head);
}

/*
 * read a list of (comma-separated) states, set them in the
 * given transition.
 *
 */
statelist(fp,t)
FILE *fp;
Trans t;
{
  int curtok,sval;
  curtok = COMMA;
  while (curtok != RBRACK) {
	if (curtok != COMMA) fatal("missing comma");
	if ((curtok = gettoken(fp)) != WORD) fatal("missing state name");
        if ((sval = lkup(tokval)) == -1) {
		fprintf(stderr,"state %s undefined\n",tokval);
		fatal("undefined state");
	   }
        setstate(sval,t);
	curtok = gettoken(fp);
   }
}

/*
 * copy an action from the input to the output file
 *
 */
copyact(inp,outp,actno)
FILE *inp,*outp;
int actno;
{
  int c,bcnt;
  fprintf(outp,"case %d:\n",actno);
  while ((c = getc(inp)) != '\n' && isspace(c));	/* skip whitespace */
  if (c == '{') {
     bcnt = 1;
     putc(c,outp);
     while (bcnt > 0 && (c = getc(inp)) != EOF) {
	if (c == '{') bcnt++;
	else if (c == '}') bcnt--;
	else if (c == '\n') lines++;
	putc(c,outp);
      }
     if (bcnt > 0) fatal("action doesn't end");
    }
   else {
	  while (c != '\n' && c != EOF) {
		putc(c,outp);
		c = getc(inp);
	    }
	  lines++;
	}
   fprintf(outp,"\nbreak;\n");
}

/*
 * find the action associated with a given character and state.
 * returns -1 if one can't be found.
 *
 */
faction(hd,state,chr)
Trans hd;
int state,chr;
{
  while (hd != NULL) {
    if (hd->anyst || teststate(state,hd))
      if (hd->inchr == '.' || hd->inchr == chr) return(hd->actno);
    hd = hd->nxt;
    }
  return(-1);
}


/*
 * empty the table...
 *
 */
emptytbl()
{
  int i;
  for (i=0; i<nstates*128; i++) tbl[i] = -1;
}

/*
 * add the specified action to the output for the given state and chr.
 *
 */

addaction(act,state,chr)
int act,state,chr;
{
 tbl[state*128 + chr] = act;
}

writetbl(fp)
FILE *fp;
{
  warray(fp,"tbl",tbl,128*(nstates+1));
}

/*
 * write an array to the output file, given its name and size.
 *
 */
warray(fp,nam,cont,siz)
FILE *fp;
char *nam;
int cont[],siz;
{
  int i;
  fprintf(fp,"int %s[] = {\n",nam);
  for (i = 0; i < siz; i++) {
	fprintf(fp,"%d, ",cont[i]);
	if ((i % 20) == 0) putc('\n',fp);
	}
  fprintf(fp,"};\n");
}

main(argc,argv)
int argc;
char *argv[];
{
  Trans head;
  int state,c;
  FILE *infile,*outfile;

  if (argc > 1) {
    if ((infile = fopen(argv[1],"r")) == NULL) {
    	fprintf(stderr,"Can't open %s\n",argv[1]);
	fatal("unreadable input file"); } }
  else infile = stdin;

  if (argc > 2) {
    if ((outfile = fopen(argv[2],"w")) == NULL) {
    	fprintf(stderr,"Can't write to %s\n",argv[2]);
	fatal("bad output file"); } }
  else outfile = stdout;

  clrhash();				/* empty hash table */
  head = rdinput(infile,outfile);	/* read input file */
  emptytbl();				/* empty our tables */
  for (state = 0; state <= nstates; state++)
    for (c = 1; c < 128; c++)
     addaction(faction(head,state,c),state,c);	/* find actions, add to tbl */
  writetbl(outfile);
  copyrest(infile,outfile);
  printf("%d states, %d actions\n",nstates,nacts);
#ifdef undef
  for (state = 1; state <= nstates; state ++)
    for (c = 1; c < 128; c++)
       if (tbl[state*128 + c] != -1) printf("state %d, chr %d, act %d\n",
       	state,c,tbl[state*128 + c]);
#endif
  exit(0);
}

/*
 * fatal error handler
 *
 */

fatal(msg)
char *msg;
{
  fprintf(stderr,"error in line %d: %s\n",lines,msg);
  exit(1);
}

prolog(outfp)
FILE *outfp;
{
  int c;
  while ((c = *txt1++) != '\0')  putc(c,outfp);
  while ((c = *fname++) != '\0') putc(c,outfp);
  while ((c = *txt2++) != '\0')  putc(c,outfp);
}

epilogue(outfp)
FILE *outfp;
{
  int c;
  while ((c = *txt3++) != '\0') putc(c,outfp);
}

copyrest(in,out)
FILE *in,*out;
{
  int c;
  while ((c = getc(in)) != EOF) putc(c,out);
}

/*
 * gettoken - returns token type of next token, sets tokval
 * to the string value of the token if appropriate.
 *
 */

gettoken(fp)
FILE *fp;
{
  int c;
  while (1) {				/* loop if reading comments... */
    do {
	  c = getc(fp);
	  if (c == '\n') lines++;
       } while (isspace(c));		/* skip whitespace */
    switch(c) {
	  case EOF: return(SEP);
	  case '%': if ((c = getc(fp)) == '%') return(SEP);
		    tokval[0] = '%';
		    tokval[1] = c;
		    rdword(fp,tokval+2);
		    return(WORD);
	  case '<': return(LBRACK);
	  case '>': return(RBRACK);
	  case ',': return(COMMA);
	  case '/': if ((c = getc(fp)) == '*') {
	    	      rdcmnt(fp);	/* skip over the comment */
		      continue; }	/* and keep looping */
		    else {
			ungetc(c);	/* put this back into input */
			c = '/'; }	/* put character back, fall thru */

	  default: if (isword(c)) {
			  ungetc(c,fp);
			  rdword(fp,tokval);
			  return(WORD);
		      	}
		   else fatal("Invalid character in input");
	     }
  }
}

/*
 * skip over a comment
 *
 */

rdcmnt(fp)
FILE *fp;
{
  int c,star,prcnt;
  prcnt = star = 0;			/* no star seen yet */
  while (!((c = getc(fp)) == '/' && star)) {
    if (c == EOF || (prcnt && c == '%')) fatal("Unterminated comment");
    prcnt = (c == '%');
    star = (c == '*');
    if (c == '\n') lines++; }
}


/*
 * symbol table management for wart
 *
 * entry points:
 *   clrhash - empty hash table.
 *   enter - enter a name into the symbol table
 *   lkup - find a name's value in the symbol table.
 *
 */

#define HASHSIZE 101			/* # of entries in hash table */

struct sym { char *name;		/* symbol name */
	     int val;			/* value */
	     struct sym *hnxt; }	/* next on collision chain */
    *htab[HASHSIZE];			/* the hash table */


/*
 * empty the hash table before using it...
 *
 */
clrhash()
{
  int i;
  for (i=0; i<HASHSIZE; i++) htab[i] = NULL;
}

/*
 * compute the value of the hash for a symbol
 *
 */
hash(name)
char *name;
{
  int sum;
  for (sum = 0; *name != '\0'; name++) sum += (sum + *name);
  sum %= HASHSIZE;			/* take sum mod hashsize */
  if (sum < 0) sum += HASHSIZE;		/* disallow negative hash value */
  return(sum);
}

/*
 * make a private copy of a string...
 *
 */
char *
copy(s)
char *s;
{
  char *new;
  new = (char *) malloc(strlen(s) + 1);
  strcpy(new,s);
  return(new);
}

/*
 * enter state name into the hash table
 *
 */
enter(name,svalue)
char *name;
int svalue;
{
  int h;
  struct sym *cur;
  if (lkup(name) != -1) {
	fprintf(stderr,"state %s appears twice...\n");
	exit(1); }
  h = hash(name);
  cur = (struct sym *)malloc(sizeof (struct sym));
  cur->name = copy(name);
  cur->val = svalue;
  cur->hnxt = htab[h];
  htab[h] = cur;
}

/*
 * find name in the symbol table, return its value.  Returns -1
 * if not found.
 *
 */
lkup(name)
char *name;
{
  struct sym *cur;
  for (cur = htab[hash(name)]; cur != NULL; cur = cur->hnxt)
	if (strcmp(cur->name,name) == 0) return(cur->val);
  return(-1);
}
@//E*O*F ckwart.c//
chmod u=rw,g=r,o=r ckwart.c
 
echo x - ckwart.doc
sed 's/^@//' > "ckwart.doc" <<'@//E*O*F ckwart.doc//'
WART

Wart is a program that implements a small subset of the Unix 'lex' lexical
analyzer generator.  Unlike lex, wart may be distributed without requirement
for a Unix license.  Wart was written by Jeff Damens at the Columbia University
Center of Computing Activities to facilitate development of Unix Kermit.

Wart is intended for production of state table switchers.  It allows a set of
states to be defined, along with a function for getting input, and a table of
state transitions.  A C program is generated which performs actions and
switches states based on the current state and the input.

The following short program demonstrates some of the capabilities and
limitations of Wart.  The program accepts from the command line a binary
number, preceded by an optional minus sign, and optionally containing a
fractional part.  It prints the decimal equivalent.

#include <stdio.h>

int state, s = 1, m = 0, d;
float f;
char *b;

%states sign mantissa fraction		    /* Declare wart states */

%%					    /* Begin state table */
<sign>-      { s = -1; BEGIN mantissa; }    /* Look for sign */
<sign>0      { m = 0;  BEGIN mantissa; }    /* Got digit, start mantissa */
<sign>1      { m = 1;  BEGIN mantissa; }
<sign>.      { fatal("bad input"); }	    /* Detect bad format */
<mantissa>0  { m *= 2; }		    /* Accumulate mantissa */
<mantissa>1  { m = 2 * m + 1; }
<mantissa>$  { printf("%d\n", s * m); return; }
<mantissa>.  { f = 0.0; d = 1; BEGIN fraction; }    /* Start fraction */
<fraction>0  { d *= 2; }		    	    /* Accumulate fraction */
<fraction>1  { d *= 2; f += 1.0 / d; }
<fraction>$  { printf("%f\n", s * (m + f) ); return; }
<fraction>.  { fatal("bad input"); }
%%

input() {				    /* Define input() function */
    int x;
    return(((x = *b++) == '\0') ? '$' : x );
}

fatal(s) char *s; {			    /* Error exit */
    fprintf(stderr,"fatal - %s\n",s);
    exit(1);
}

main(argc,argv) int argc; char **argv; {    /* Main program */
    if (argc < 1) exit(1);
    b = *++argv;
    state = sign;			    /* Initialize state */
    wart();				    /* Invoke state switcher */
    exit(0);				    /* Done */
}

The wart program accepts as input a C program containing lines that start
with "%" or sections delimited by "%%".  The directive "%states" declares
the program's states.  The section enclosed by "%%" markers is the state
table, with entries of the form

  <state>X { action }

which is read as "if in state <state> with input X perform { action }"

The optional <state> field tells the current state or states the program must
be in to perform the indicated action.  If no state is specified, then it
means the action will be performed regardless of the current state.  If more
than one state is specifed, then the action will be performed in any of the
listed states.  Multiple states are separated by commas.

The required input field consists of a single literal character.  When in
the indicated state, if the input is the specified character, then the
associated action will be performed.  The character '.' matches any input
character.  No pattern matching or range notation is provided.  The input
character is obtained from the input() function, which you must define.  It
should be alphanumeric, or else one of the characters ".% -$@" (quotes not
included).  Note that the program above recognize the binary point '.'
through a ruse.

The action is a series of zero or more C language statements, enclosed in
curly braces.

The BEGIN macro is defined simply to be "state = ", as in lex.

The wart() function is generated by the wart program based on the state
declarations and the state transition table.  It loops through calls to
input(), using the result to index into a big case statement it has created
from the state table.

Wart is invoked as follows:

	wart          (Input from stdin, output to stdout)

	wart fn1      (Input from fn1, output to stdout)

	wart fn1 fn2  (Input from fn1, output to fn2.  Example:  wart a.w a.c)

Wart programs have the conventional filetype '.w'.
@//E*O*F ckwart.doc//
chmod u=rw,g=r,o=r ckwart.doc
 
echo x - ckwho.txt
sed 's/^@//' > "ckwho.txt" <<'@//E*O*F ckwho.txt//'
(18 Feb 85)

The following people have tentatively volunteered to supply support in
C-Kermit 4.0 for the following systems:


What                             Who

DEC Pro-350/380, Venix           SY.FDC at CU20B (Frank da Cruz)

IBM 370-series, Ahmdah UTS       SY.FDC at CU20B (Frank da Cruz)

Apple Macintosh                  SY.WBC3 at CU20B (Bill Catchings)

Masscomp RTU 2.2                 sob at RICE (Stan Barber)

Coherent                         vortex!lauren at RAND-UNIX (Lauren Weinstein) 

Callan UniStar 300 with
 Unisoft 68000 System V Unix     EBM at MIT-XX (Eliot Moss)

ATT 3Bx, System V                Chris at COLUMBIA-20 (Chris Maio)

IBM PC, etc, PC/IX               HFISCHER at USC-ECLB (Herm Fischer)

IBM PC, etc, Xenix               HFISCHER at USC-ECLB (Herm Fischer)

VAX,PDP-11 with IS3, Interactive
 Systems version of System III   HFISCHER at USC-ECLB (Herm Fischer)

Os9                              BLARSON at USC-ECL (Bob Larson)

Version 7                        vasoll%okstate.csnet at CSNET-RELAY (Mark Vasoll)

4.2 UUCP Line Locking            hipl!tony at NYU-CMCL2 (Tony Movshon)

HP9000 Series 200 (HP9836)
 with HP-UX System III           b-davis at utah-cs (Brad Davis)

CP/M (Small C or BDS C)          bdale at cmu-cs-g (Bdale Garbee)

Honeywell GCOS3/8                Carlin%HIS-PHOENIX-MULTICS at MIT-MULTICS

68000 Xenix                      ED at MIT-MC (Ed Schwalenberg)

VAX, 2.0 BSD                     nsc!jon at DECWRL (Jon Ryshpan)

CP/M-86, De Smet C               nsc!jon at DECWRL (Jon Ryshpan)

Login scripts, raw upload        poulton%hplabs.csnet at CSNET-RELAY (Ken Poulton)

Apple II, Aztec C                Saline at MIT-MULTICS (Steven Saline)
@//E*O*F ckwho.txt//
chmod u=rw,g=r,o=r ckwho.txt
 
echo x - ckxbsd.bwr
sed 's/^@//' > "ckxbsd.bwr" <<'@//E*O*F ckxbsd.bwr//'
ttxin()

1.  11 Feb 85: changed
      x = read(ttyfd,buf,&n);
    to
      x = read(ttyfd,buf,n);

2.  11 Feb 85: in for loop, check & pass along return code from read():
      if ((y = read(ttyfd,&c,1)) < 1) return(y);


3.  11 Feb 85: declare c to be unsigned char, rather than int, to allow
    read() to work on machines that store chars in MSB rather LSB.
    Make sure char is not sign-extended upon return.

ttinc() -

1. 11 Feb 85: declare ch to be unsigned char, rather than int, as in ttinl().
@//E*O*F ckxbsd.bwr//
chmod u=rw,g=r,o=r ckxbsd.bwr
 
echo x - ckxbsd.c
sed 's/^@//' > "ckxbsd.c" <<'@//E*O*F ckxbsd.c//'
char *ckxv = "4.2BSD Terminal I/O, 4.0(013), 12 Feb 85";

/* Interrupt, terminal control & i/o functions for 4.2BSD */

/*
 Note - KERLD is the Berknet line driver, modified to pass through all 8
 bits, and to allow an arbitrary break character to be set.  Don't define
 this symbol unless you have made this modification to your 4.2BSD kernel!
*/ 
#define KERLD 0

/*
 Variables:

   dftty  -- Pointer to default tty name string, like "/dev/tty".
   dfloc  -- 0 if dftty is console, 1 if external line.
   dfprty -- Default parity
   dfflow -- Default flow control
   ckxech -- Flag for who echoes console typein:
     1 - The program (system echo is turned off)
     0 - The system (or front end, or terminal).
   functions that want to do their own echoing should check this flag
   before doing so.

 Functions for assigned communication line (either external or console tty):

   ttopen(ttname)          -- Open the named tty for exclusive access.
   ttclos()                -- Close & reset the tty, releasing any access lock.
   ttpkt(speed,flow)       -- Put the tty in packet mode and set the speed.
   ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
   ttinl(dest,max,timo)    -- Timed read line from the tty.
   ttinc(timo)             -- Timed read character from tty.
   ttchk()                 -- See how many characters in tty input buffer.
   ttxin(n,buf)            -- Read n characters from tty (untimed).
   ttol(string,length)     -- Write a string to the tty.
   ttoc(c)                 -- Write a character to the tty.
   ttflui()                -- Flush tty input buffer.

Functions for console terminal:

   congm()   -- Get console terminal modes.
   concb()   -- Put the console in single-character wakeup mode with no echo.
   conbin()  -- Put the console in binary (raw) mode.
   conres()  -- Restore the console to mode obtained by congm().
   conoc(c)  -- Unbuffered output, one character to console.
   conol(s)  -- Unbuffered output, null-terminated string to the console.
   conxo(n,s) -- Unbuffered output, n characters to the console.
   conchk()  -- Check if characters available at console.
   coninc()  -- Get a character from the console.
   conint()  -- Enable terminal interrupts on the console.
   connoi()  -- Disable terminal interrupts on the console.

Time functions

   msleep(m) -- Millisecond sleep
   ztime(&s) -- Return pointer to date/time string
*/

#include <stdio.h>			/* Unix Standard i/o */
#include <sgtty.h>			/* Set/Get tty modes */
#include <signal.h>			/* Interrupts */
#include <setjmp.h>			/* Longjumps */
#include <sys/file.h>			/* File information */
#include <sys/time.h>			/* Clock info */
#include "ckdebu.h"			/* Formats for debug() */


/* dftty is the device name of the default device for file transfer */

char *dftty = "/dev/tty";


/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */

int dfloc = 0;

/* Other defaults */

int dfprty = 0;				/* No parity */
int dfflow = 1;				/* Xon/Xoff flow control */

/* ckxech is 0 if system normally echoes console characters, 1 otherwise */

int ckxech = 0;


/* Declarations of variables global to this module */

jmp_buf sjbuf;				/* Longjump buffer */

static int lkf = 0,			/* Line lock flag */
    conif = 0,				/* Console interrupts on/off flag */
    cgmf = 0,				/* Flag that console modes saved */
    kerld = KERLD,			/* Flag for using line discipline */
    ttyfd = -1;				/* TTY file descriptor */

static struct timeval tv;		/* For getting time, from sys/time.h */
static struct timezone tz;

static struct sgttyb 			/* sgtty info... */
    ttold, ttraw, tttvt,		/* for communication line */
    ccold, ccraw, cccbrk;		/* and for console */

#if KERLD
  struct tchars oldc, newc;		/* Special characters */
  int ld = NETLDISC;			/* Special Kermit line discipline */
  int oldld;				/* Old discipline */
#endif

/*  T T O P E N  --  Open a tty for exclusive access.  */

/*  Returns 0 on success, -1 on failure.  */

/***
 Do something better about exclusive access later, like the uucp
 lock file hair...
***/

ttopen(ttname) char *ttname; {

    if (ttyfd > -1) ttclos();		/* If old one open, close it. */

    ttyfd = open(ttname,2);		/* Open a tty for read/write */
    if (ttyfd < 0) return(-1);
    lkf = 0;

    if (flock(ttyfd,(LOCK_EX|LOCK_NB)) < 0) {
    	fprintf(stderr,"Warning - Access to %s not exclusive\n",ttname);
    } else lkf = 1;

    gtty(ttyfd,&ttold);			/* Get sgtty info */
    gtty(ttyfd,&ttraw);			/* And a copy of it for packets*/
    gtty(ttyfd,&tttvt);			/* And one for virtual tty service */
    return(0);
}


/*  T T C L O S  --  Close the TTY, releasing any lock.  */

ttclos() {
    if (ttyfd < 0) return(0);		/* Wasn't open. */
    if (lkf) flock(ttyfd,LOCK_UN);	/* Unlock it first. */
    ttres();				/* Reset modes. */
    close(ttyfd);			/* Close it. */
    ttyfd = -1;				/* Mark it as closed. */
    return(0);
}

/*  T T P K T  --  Condition the communication line for packets. */

/*  If called with speed > -1, also set the speed.  */

/*  Returns 0 on success, -1 on failure.  */

ttpkt(speed,flow) int speed, flow; {
    int s;
    if (ttyfd < 0) return(-1);		/* Not open. */

#if KERLD
    if (kerld) {
	ioctl(ttyfd,TIOCGETD,&oldld);	/* Get line discipline */
	ioctl(ttyfd,TIOCGETC,&oldc);	/* Get special chars */
	newc = oldc;			/* Copy special chars */
	newc.t_brkc = '\r';		/* Set CR to be break character */
	if(ioctl(ttyfd,TIOCSETC,&newc) < 0) return(-1);
    }
#endif

/* Note, KERLD ignores the TANDEM, ECHO, and CRMOD bits */

    if (flow == 1) ttraw.sg_flags |= TANDEM; /* XON/XOFF if selected */
    if (flow == 0) ttraw.sg_flags &= ~TANDEM;
    ttraw.sg_flags |= RAW;		/* Go into raw mode */
    ttraw.sg_flags &= ~(ECHO|CRMOD);	/* Use CR for break character */
    
    s = ttsspd(speed);			/* Check the speed */
    if (s > -1) ttraw.sg_ispeed = ttraw.sg_ospeed = s; /* Do the speed */

    if (stty(ttyfd,&ttraw) < 0) return(-1);	/* Set the new modes. */

#if KERLD
    if (kerld) {
	if (ioctl(ttyfd,TIOCSETD,&ld) < 0)
	    return(-1); /* Set line discpline. */
    }
#endif

    ttflui();				/* Flush any pending input */
    return(0);
}

/*  T T V T -- Condition communication line for use as virtual terminal  */

ttvt(speed,flow) int speed, flow; {
    int s;
    if (ttyfd < 0) return(-1);		/* Not open. */

    if (flow == 1) tttvt.sg_flags |= TANDEM; /* XON/XOFF if selected */
    if (flow == 0) tttvt.sg_flags &= ~TANDEM;
    tttvt.sg_flags |= RAW;		/* Raw mode */
    tttvt.sg_flags &= ~ECHO;		/* No echo */
    
    s = ttsspd(speed);			/* Check the speed */
    if (s > -1) tttvt.sg_ispeed = tttvt.sg_ospeed = s; /* Do the speed */

    return(stty(ttyfd,&tttvt));
}


/*  T T S S P D  --  Return the internal baud rate code for 'speed'.  */

ttsspd(speed) {
    int s, spdok;

    if (speed < 0) return(-1);
	spdok = 1;			/* Assume arg ok */
	switch (speed) {
	    case 0:    s = B0;    break;	/* Just the common ones. */
	    case 110:  s = B110;  break;	/* The others from ttydev.h */
	    case 150:  s = B150;  break;	/* could also be included if */
	    case 300:  s = B300;  break;	/* necessary... */
	    case 600:  s = B600;  break;
	    case 1200: s = B1200; break;
	    case 1800: s = B1800; break;
	    case 2400: s = B2400; break;
	    case 4800: s = B4800; break;
	    case 9600: s = B9600; break;
	    default:
	    	spdok = 0;
		fprintf(stderr,"Unsupported line speed - %d\n",speed);
		fprintf(stderr,"Current speed not changed\n");
		break;
	}	    
	if (spdok) return(s); else return(-1);
 }

/*  T T F L U I  --  Flush tty input buffer */

ttflui() {
    long int n;
    if (ttyfd < 0) return(-1);		/* Not open. */
    n = FREAD;				/* Specify read queue. */
    if (ioctl(ttyfd,TIOCFLUSH,&n) < 0) perror("flush failed");
    return(0);
}



/*  T T R E S  --  Restore terminal to "normal" mode.  */

ttres() {				/* Restore the tty to normal. */
    if (ttyfd < 0) return(-1);		/* Not open. */
    sleep(1);				/* Wait for pending i/o to finish. */
#if KERLD
    if (kerld) ioctl(ttyfd,TIOCSETD,&oldld); /* Restore old line discipline. */
#endif
    if (stty(ttyfd,&ttold) < 0) return(-1); /* Restore sgtty stuff */
#if KERLD
    if (kerld) ioctl(ttyfd,TIOCSETC,&oldc); /* Restore old special chars. */
#endif
    return(0);
}

/* Interrupt Functions */


/* Timeout handler for communication line input functions */

timerh() {
    longjmp(sjbuf,1);
}

 
/* Set up terminal interrupts on console terminal */


conint(f) int (*f)(); {			/* Set an interrupt trap. */
    if (conif) return;			/* Nothing to do if already on. */
    signal(SIGINT,f);			/* Function to trap to. */
    conif = 1;
}


/* Reset console terminal interrupts */

connoi() {
    signal(SIGINT,SIG_DFL);
    conif = 0;
}


/*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */

ttchk() {
    int n, x;
    x = ioctl(ttyfd, FIONREAD, &n);
    return((x < 0) ? 0 : n);
}


/*  T T X I N  --  Get n characters from tty input buffer  */

ttxin(n,buf) int n; char *buf; {
    int x;
    x = read(ttyfd,buf,n);
    if (x > 0) buf[x] = '\0';
    return(x);
}

/*  T T O L  --  Similar to "ttinl", but for writing.  */

ttol(s,n) int n; char *s; {
    int x;
    if (ttyfd < 0) return(-1);		/* Not open. */
    x = write(ttyfd,s,n);
    debug(F111,"ttol",s,n);
    if (x < 0) debug(F101,"ttol failed","",x);
    return(x);
}


/*  T T O C  --  Output a character to the communication line  */

ttoc(c) char c; {
    if (ttyfd < 0) return(-1);		/* Not open. */
    return(write(ttyfd,&c,1));
}


/*  T T I N L  --  Read a record (up to break character) from comm line.  */
/*
  If no break character encountered within "max", return "max" characters,
  with disposition of any remaining characters undefined.  Otherwise, return
  the characters that were read, including the break character, in "dest" and
  the number of characters read as the value of function, or 0 upon end of
  file, or -1 if an error occurred.  Times out & returns error if not completed
  within "timo" seconds.
*/

ttinl(dest,max,timo,eol) int max,timo; char *dest; {
    int x, y, z;
    unsigned char c;
    if (ttyfd < 0) return(-1);		/* Not open. */
    if (timo <= 0) {			/* Untimed. */
	x = read(ttyfd,dest,max);	/* Try to read. */
	return(x);			/* Return the count. */
    }
    signal(SIGALRM,timerh);		/* Timed, set up timeout action. */
    alarm(timo);			/* Set the timer. */
    if (setjmp(sjbuf)) return(-1);	/* Do this if timer went off. */
    if (kerld) {
	x = read(ttyfd,dest,max);	/* Try to read. */
    } else {
	for (x = c = 0; (x < max) && (c != eol); x++) {
	     if ((y = read(ttyfd,&c,1)) < 1) return(y);
	     dest[x] = c;
	}
	x++;
    }
    alarm(0);				/* Success, turn off timer, */
    signal(SIGALRM,SIG_DFL);		/* and associated interrupt. */
    return(x);				/* Return the count. */
}

/*  T T I N C --  Read a character from the communication line  */

ttinc(timo) int timo; {
    int n;
    unsigned char ch;		       

    if (ttyfd < 0) return(-1);		/* Not open. */
    if (timo <= 0) {			/* Untimed. */
	n = read(ttyfd,&ch,1);		/* Wait for a character. */
	return( (n > 0) ? (ch & 0377) : n );
    }

    signal(SIGALRM,timerh);		/* Timed, set up timer. */
    alarm(timo);
    if (setjmp(sjbuf)) return(-1);
    n = read(ttyfd,&ch,1);		/* Read a character. */
    alarm(0);				/* Turn off timer, */
    signal(SIGALRM,SIG_DFL);		/* and interrupt. */
    if (n > 0) return(ch & 0377); else return(n); /* Return char or -1. */
}

/*  T T S N D B  --  Send a BREAK signal  */

ttsndb() {
    int x;

    if (ttyfd < 0) return(-1);		/* Not open. */
    if (ioctl(ttyfd,TIOCSBRK,(char *)0) < 0) {	/* Turn on BREAK */
    	conol("Can't send BREAK");
	return(-1);
    }
    x = msleep(275);			/* Sleep for so many milliseconds */
    if (ioctl(ttyfd,TIOCCBRK,(char *)0) < 0) {	/* Turn off BREAK */
	conol("BREAK stuck!!!");
	doexit();			/* Get out, closing the line. */
    }
    return(x);
}


/*  M S L E E P  --  Millisecond version of sleep().  */

/*
 Intended only for small intervals.  For big ones, just use sleep().
 This is a very expensive way to do interval timing.  It just loops,
 looking at the system clock.  If anyone can figure out a better way,
 e.g. with setitimer(), then do it that way.
*/

msleep(m) int m; {
    int t1, t3, t4;

    if (gettimeofday(&tv, &tz) < 0) return(-1); /* Get current time. */
    t1 = tv.tv_sec;			/* Seconds */

    while (1) {
	gettimeofday(&tv, &tz);
	t3 = tv.tv_sec - t1;
	t4 = (tv.tv_usec + 1000000 * t3) / 1000;
	if (t4 > m) return(t4);
    }
}


/*  Z T I M E  --  Return date/time string  */

ztime(s) char **s; {
    char *asctime();
    struct tm *localtime();
    struct tm *tp;

    gettimeofday(&tv, &tz);
    time(&tv.tv_sec);
    tp = localtime(&tv.tv_sec);
    *s = asctime(tp);
}

/*  C O N G M  --  Get console terminal modes.  */

/*
 Saves current console mode, and establishes variables for switching between 
 current (presumably normal) mode and other modes.
*/

congm() {
     gtty(0,&ccold);			/* Structure for restoring */
     gtty(0,&cccbrk);			/* For setting CBREAK mode */
     gtty(0,&ccraw);			/* For setting RAW mode */
     cgmf = 1;				/* Flag that we got them. */
}


/*  C O N C B --  Put console in cbreak mode.  */

/*  Returns 0 if ok, -1 if not  */

concb() {
    if (cgmf == 0) congm();		/* Get modes if necessary. */
    cccbrk.sg_flags |= CBREAK;		/* Set to character wakeup, */
    cccbrk.sg_flags &= ~ECHO;		/* no echo. */
    ckxech = 1;				/* Program can echo characters */
    return(stty(0,&cccbrk));
}


/*  C O N B I N  --  Put console in binary mode  */

/*  Returns 0 if ok, -1 if not  */

conbin() {
    if (cgmf == 0) congm();		/* Get modes if necessary. */
    ccraw.sg_flags |= (RAW|TANDEM);   	/* Set rawmode, XON/XOFF */
    ccraw.sg_flags &= ~(ECHO|CRMOD);  	/* Set char wakeup, no echo */
    ckxech = 1;				/* Program can echo characters */
    return(stty(0,&ccraw));
}


/*  C O N R E S  --  Restore the console terminal  */

conres() {
    if (cgmf == 0) return(0);		/* Don't do anything if modes */
    sleep(1);				/*  not known! */
    ckxech = 0;				/* System should echo chars */
    return(stty(0,&ccold));		/* Restore controlling tty */
}

/*  C O N O C  --  Output a character to the console terminal  */

conoc(c) char c; {
    write(1,&c,1);
}

/*  C O N X O  --  Write x characters to the console terminal  */

conxo(x,s) char *s; int x; {
    write(1,s,x);
}

/*  C O N O L  --  Write a line to the console terminal  */

conol(s) char *s; {
    int len;
    len = strlen(s);
    write(1,s,len);
}

/*  C O N O L L  --  Output a string followed by CRLF  */

conoll(s) char *s; {
    conol(s);
    write(1,"\r\n",2);
}


/*  C O N C H K  --  Check if characters available at console  */

conchk() {
    int n, x;
    x = ioctl(0, FIONREAD, &n);
    return((x < 0) ? 0 : n);
}


/*  C O N I N C  --  Get a character from the console  */

coninc() {
    int n; char ch;
    n = read(0, &ch, 1);		/* Read a character. */
    ch &= 0377;
    if (n > 0) return(ch); else return(-1);  /* Return the char, or -1. */
}
@//E*O*F ckxbsd.c//
chmod u=rw,g=r,o=r ckxbsd.c
 
echo x - ckzbsd.c
sed 's/^@//' > "ckzbsd.c" <<'@//E*O*F ckzbsd.c//'
char *ckzv = "4.2BSD file support, 4.0(011) 30 Jan 85";

/* C K Z B S D  --  Kermit file system support for 4.2BSD */

/* Definitions of some Unix system commands */

char *DIRCMD = "ls -l ";		/* For directory listing */
char *DELCMD = "rm -f ";		/* For file deletion */
char *TYPCMD = "cat ";			/* For typing a file */
char *SPACMD = "pwd ; quota ; df .";	/* Space/quota of current directory */
char *SPACM2 = "df ";			/* For space in specified directory */
char *WHOCMD = "finger ";		/* For seeing who's logged in */
/*
  Functions (n is one of the predefined file numbers from ckermi.h):

   zopeni(n,name)   -- Opens an existing file for input.
   zopeno(n,name)   -- Opens a new file for output.
   zclose(n)        -- Closes a file.
   zchin(n)         -- Gets the next character from an input file.
   zsout(n,s)       -- Write a null-terminated string to output file, buffered.
   zsoutl(n,s)      -- Like zsout, but appends a line terminator.
   zsoutx(n,s,x)    -- Write x characters to output file, unbuffered.
   zchout(n,c)      -- Add a character to an output file, unbuffered.
   zchki(name)      -- Check if named file exists and is readable, return size.
   zchko(name)      -- Check if named file can be created.
   znewn(name,s)    -- Make a new unique file name based on the given name.
   zdelet(name)     -- Delete the named file.
   zxpand(string)   -- Expands the given wildcard string into a list of files.
   znext(string)    -- Returns the next file from the list in "string".
   zxcmd(cmd)       -- Execute the command in a lower fork.
   zclosf()         -- Close input file associated with zxcmd()'s lower fork.
   zrtol(n1,n2)     -- Convert remote filename into local form.
   zltor(n1,n2)     -- Convert local filename into remote form.
   zchdir(dirnam)   -- Change working directory.
   zhome()          -- Return pointer to home directory name string.
 */
#include <stdio.h>			/* Standard Unix i/o */
#include <ctype.h>			/* Character types */
#include <sys/types.h>			/* Data types */
#include <sys/dir.h>			/* Directory structure */
#include <sys/stat.h>			/* File status */
#include <sys/file.h>			/* File access */
#include <sys/time.h>
#include "ckermi.h"			/* Kermit definitions */

FILE *fp[ZNFILS] = { 			/* File pointers */
    NULL, NULL, NULL, NULL, NULL, NULL, NULL };

#define MAXWLD 500			/* Maximum wildcard filenames */

static int pid;	    			/* pid of child fork */
static int fcount;			/* Number of files in wild group */
char *getenv(), *strcpy();		/* For finding home directory */
extern errno;				/* System error code */

static char *mtchs[MAXWLD],		/* Matches found for filename */
     **mtchptr;				/* Pointer to current match */

/*  Z O P E N I  --  Open an existing file for input. */

zopeni(n,name) int n; char *name; {
    debug(F111," zopeni",name,n);
    debug(F101,"  fp","",(int) fp[n]);
    if (chkfn(n) != 0) return(0);
    if (n == ZSTDIO) {			/* Standard input? */
	if (isatty(0)) {
	    fprintf(stderr,"?Terminal input not allowed\n");
	    debug(F110,"zopeni: attempts input from unredirected stdin","",0);
	    return(0);
	}
	fp[ZIFILE] = stdin;
	return(1);
    }
    fp[n] = fopen(name,"r");		/* Real file. */
    debug(F111," zopeni", name, (int) fp[n]);
    if (fp[n] == NULL) perror("zopeni");
    return((fp[n] != NULL) ? 1 : 0);
}

/*  Z O P E N O  --  Open a new file for output.  */

zopeno(n,name) int n; char *name; {
    debug(F111," zopeno",name,n);
    if (chkfn(n) != 0) return(0);
    if ((n == ZCTERM) || (n == ZSTDIO)) {   /* Terminal or standard output */
	fp[ZOFILE] = stdout;
	debug(F101," fp[]=stdout", "", (int) fp[n]);
	return(1);
    }
    fp[n] = fopen(name,"w");		/* A real file */
    if (fp[n] == NULL) perror("zopeno");
    if (n == ZDFILE) setbuf(fp[n],NULL); /* Make debugging file unbuffered */
    debug(F101, " fp[n]", "", (int) fp[n]);
    return((fp[n] != NULL) ? 1 : 0);
}

/*  Z C L O S E  --  Close the given file.  */

zclose(n) int n; {
    if (chkfn(n) < 1) return(0);
    if ((fp[n] != stdout) && (fp[n] != stdin)) fclose(fp[n]);
    fp[n] = NULL;
    return(1);
}

/*  Z C H I N  --  Get a character from the input file.  */

zchin(n) int n; {
    int a;
    if (chkfn(n) < 1) return(-1);
    a = getc(fp[n]);
    return((a == EOF) ? -1 : a & 0377);
}

/*  Z S O U T  --  Write a string to the given file, buffered.  */

zsout(n,s) int n; char *s; {
    if (chkfn(n) < 1) return(-1);
    fprintf(fp[n],s);
    return(0);
}

/*  Z S O U T L  --  Write string to file, with line terminator, buffered  */

zsoutl(n,s) int n; char *s; {
    if (chkfn(n) < 1) return(-1);
    fprintf(fp[n],"%s\n",s);
    return(0);
}

/*  Z S O U T X  --  Write x characters to file, unbuffered.  */

zsoutx(n,s,x) int n, x; char *s; {
    if (chkfn(n) < 1) return(-1);
    return(write(fp[n]->_file,s,x));
}


/*  Z C H O U T  --  Add a character to the given file.  */

zchout(n,c) int n; char c; {
    if (chkfn(n) < 1) return(-1);
    if (n == ZSFILE)
    	return(write(fp[n]->_file,&c,1)); /* Use unbuffered for session log */
    else {
    	putc(c,fp[n]);			/* Buffered for everything else */
	return(0);
    }
}

/*  C H K F N  --  Internal function to verify file number is ok  */

/*
 Returns:
  -1: File number n is out of range
   0: n is in range, but file is not open
   1: n in range and file is open
*/
chkfn(n) int n; {
    switch (n) {
	case ZCTERM:
	case ZSTDIO:
	case ZIFILE:
	case ZOFILE:
	case ZDFILE:
	case ZTFILE:
	case ZPFILE:
	case ZSFILE: break;
	default:
	    debug(F101,"chkfn: file number out of range","",n);
	    fprintf(stderr,"?File number out of range - %d\n",n);
	    return(-1);
    }
    return( (fp[n] == NULL) ? 0 : 1 );
}

/*  Z C H K I  --  Check if input file exists and is readable  */

/*
  Returns:
   >= 0 if the file can be read (returns the size).
     -1 if file doesn't exist or can't be accessed,
     -2 if file exists but is not readable (e.g. a directory file).
     -3 if file exists but protected against read access.
*/
/*
 For Berkeley Unix, a file must be of type "regular" to be readable.
 Directory files, special files, and symbolic links are not readable.
*/
zchki(name) char *name; {
    struct stat buf;
    int x;

    x = stat(name,&buf);
    if (x < 0) {
	debug(F111,"zchki stat fails",name,errno);
	return(-1);
    }
    x = buf.st_mode & S_IFMT;		/* Isolate file format field */
    if (x != S_IFREG) {
	debug(F111,"zchki skipping:",name,x);
	return(-2);
    }
    debug(F111,"zchki stat ok:",name,x);

    if ((x = access(name,R_OK)) < 0) { 	/* Is the file accessible? */
	debug(F111," access failed:",name,x); /* No */
    	return(-3);			
    } else {
	x = buf.st_size;
	debug(F111," access ok:",name,x); /* Yes */
	return( (x > -1) ? x : 0 );
    }
}

/*  Z C H K O  --  Check if output file can be created  */

/*
 Returns -1 if write permission for the file would be denied, 0 otherwise.
*/
zchko(name) char *name; {
    int i, x;
    char s[50], *sp;	

    sp = s;				/* Make a copy, get length */
    x = 0;
    while ((*sp++ = *name++) != '\0')
    	x++;
    if (x == 0) return(-1);		/* If no filename, fail. */

    debug(F101," length","",x);
    for (i = x; i > 0; i--)		/* Strip filename. */
	if (s[i-1] == '/') break;
    
    debug(F101," i","",i);
    if (i == 0)				/* If no path, use current directory */
    	strcpy(s,"./");			
    else				/* Otherwise, use given one. */
        s[i] = '\0';

    x = access(s,W_OK);			/* Check access of path. */
    if (x < 0) {
	debug(F111,"zchko access failed:",s,errno);
	return(-1);
    } else {
	debug(F111,"zchko access ok:",s,x);
	return(0);
    }
}

/*  Z D E L E T  --  Delete the named file.  */

zdelet(name) char *name; {
    unlink(name);
}


/*  Z R T O L  --  Convert remote filename into local form  */

/*  For UNIX, this means changing uppercase letters to lowercase.  */

zrtol(name,name2) char *name, *name2; {
    for ( ; *name != '\0'; name++) {
    	*name2++ = isupper(*name) ? tolower(*name) : *name;
    }
    *name2 = '\0';
}


/*  Z L T O R  --  Convert filename from local format to common form.   */

zltor(name,name2) char *name, *name2; {
    char work[100], *cp, *pp;
    int dc = 0;

    strcpy(work,name);
    for (cp = pp = work; *cp != '\0'; cp++) {	/* strip path name */
    	if (*cp == '/') {
	    pp = cp;
	    pp++;
	}
	else if (islower(*cp)) *cp = toupper(*cp); /* Uppercase letters */
	else if (*cp == '~') *cp = 'X';	/* Change tilde to 'X' */
	else if ((*cp == '.') && (++dc > 1)) *cp = 'X'; /* & extra dots */
    }
    cp = name2;				/* If nothing before dot, */
    if (*pp == '.') *cp++ = 'X';	/* insert 'X' */
    strcpy(cp,pp);
}    


/*  Z C H D I R  --  Change directory  */

zchdir(dirnam) char *dirnam; {
    char *hd;
    if (*dirnam == '\0') hd = getenv("HOME");
    else hd = dirnam;
    return((chdir(hd) == 0) ? 1 : 0);
}


/*  Z H O M E  --  Return pointer to user's home directory  */

char *
zhome() {
    char *getenv();
    return(getenv("HOME"));
}

/*  Z X C M D -- Run a system command so its output can be read like a file */

zxcmd(comand) char *comand; {
    int pipes[2];
    if (pipe(pipes) != 0) return(0);	/* can't make pipe, fail */
    if ((pid = fork()) == 0) {		/* child */
	close(pipes[0]);		/* close input side of pipe */
	close(0);			/* close stdin */
	if (open("/dev/null",0) < 0) return(0);	/* replace input by null */
	dup2(pipes[1],1);		/* replace stdout & stderr */
	dup2(pipes[1],2);		/* by the pipe */
	close(pipes[1]);		/* get rid of this copy of the pipe */
	execl("/bin/sh","sh","-c",comand,0); /* use shell to do it */
	exit(0); }			/* just punt if it didn't work */
    close(pipes[1]);			/* don't need the output side */
    fp[ZIFILE] = fdopen(pipes[0],"r");	/* open a stream for it */
    return(1);
}


/*  Z C L O S F  - wait for the child fork to terminate and close the pipe. */

zclosf() {
    int wstat;
    fclose(fp[ZIFILE]);
    fp[ZIFILE] = NULL;
    while ((wstat = wait(0)) != pid && wstat != -1) ;
}

/*  Z X P A N D  --  Expand a wildcard string into an array of strings  */
/*
  Returns the number of files that match fn1, with data structures set up
  so that first file (if any) will be returned by the next znext() call.
*/
zxpand(fn) char *fn; {
    fcount = fgen(fn,mtchs,MAXWLD);	/* Look up the file. */
    if (fcount > 0) {
	mtchptr = mtchs;		/* Save pointer for next. */
    }
    debug(F111,"zxpand",mtchs[0],fcount);
    return(fcount);
}


/*  Z N E X T  --  Get name of next file from list created by zxpand(). */
/*
 Returns >0 if there's another file, with its name copied into the arg string,
 or 0 if no more files in list.
*/
znext(fn) char *fn; {
    if (fcount-- > 0) strcpy(fn,*mtchptr++);
    else *fn = '\0';
    debug(F111,"znext",fn,fcount+1);
    return(fcount+1);
}


/*  Z N E W N  --  Make a new name for the given file  */

znewn(fn,s) char *fn, **s; {
    static char buf[100];
    char *bp, *xp;
    int len = 0, n = 0, d = 0, t;

    bp = buf;
    while (*fn) {
	*bp++ = *fn++;
	len++;
    }
    *bp++ = '*';			/* Put a star on the end */
    *bp-- = '\0';

    n = zxpand(buf);			/* Expand the resulting wild name */
    
    while (n-- > 0) {			/* Find any existing name~d files */
	xp = *mtchptr++;
	xp += len;
	if (*xp == '~') {
	    t = atoi(xp+1);
	    if (t > d) d = t;		/* Get maximum d */
	}
    }
    sprintf(bp,"~%d",d+1);		/* Make and return name~(d+1) */
    *s = buf;
}

/* Directory Functions for 4.2BSD, written by Jeff Damens, CUCCA, 1984. */


/*
 * The path structure is used to represent the name to match.
 * Each slash-separated segment of the name is kept in one
 * such structure, and they are linked together, to make
 * traversing the name easier.
 */

struct path {
              char npart[MAXNAMLEN];	/* name part of path segment */
              struct path *fwd;		/* forward ptr */
            };

#define SSPACE 2000			/* size of string-generating buffer */
static char sspace[SSPACE];             /* buffer to generate names in */
static char *freeptr,**resptr;         	/* copies of caller's arguments */
static int remlen;                      /* remaining length in caller's array*/
static int numfnd;                      /* number of matches found */

/*
 * splitpath:
 *  takes a string and splits the slash-separated portions into
 *  a list of path structures.  Returns the head of the list.  The
 *  structures are allocated by malloc, so they must be freed.
 *  Splitpath is used internally by the filename generator.
 *
 * Input: A string.
 * Returns: A linked list of the slash-separated segments of the input.
 */

struct path *
splitpath(p)
char *p;
{
 struct path *head,*cur,*prv;
 int i;
 head = prv = NULL;
 if (*p == '/') p++;            /* skip leading slash */
 while (*p != '\0')
 {
   cur = (struct path *) malloc(sizeof (struct path));
   cur -> fwd = NULL;
   if (head == NULL) head = cur;
   else prv -> fwd = cur;       /* link into chain */
   prv = cur;
   for (i=0; i < MAXNAMLEN && *p != '/' && *p != '\0'; i++)
     cur -> npart[i] = *p++;
   cur -> npart[i] = '\0';      /* end this segment */
   if (i >= MAXNAMLEN) while (*p != '/' && *p != '\0') p++;
   if (*p == '/') p++;
 }
 return(head);
}

/*
 * fgen:
 *  This is the actual name generator.  It is passed a string,
 *  possibly containing wildcards, and an array of character pointers.
 *  It finds all the matching filenames and stores them into the array.
 *  The returned strings are allocated from a static buffer local to
 *  this module (so the caller doesn't have to worry about deallocating
 *  them); this means that successive calls to fgen will wipe out
 *  the results of previous calls.  This isn't a problem here
 *  because we process one wildcard string at a time.
 *
 * Input: a wildcard string, an array to write names to, the
 *        length of the array.
 * Returns: the number of matches.  The array is filled with filenames
 *          that matched the pattern.  If there wasn't enough room in the
 *	    array, -1 is returned.
 * By: Jeff Damens, CUCCA, 1984.
 */

fgen(pat,resarry,len)
char *pat,*resarry[];
int len;
{
 struct path *head;
 char scratch[100],*sptr;
 head = splitpath(pat);
 if (*pat == '/')
 {
  scratch[0] = '/';
  sptr = scratch+1;
 }
 else
 {
  strcpy(scratch,"./");
  sptr = scratch+2;
 }					/* init buffer correctly */
 numfnd = 0;                            /* none found yet */
 freeptr = sspace;			/* this is where matches are copied */
 resptr = resarry;			/* static copies of these so*/
 remlen = len;				/* recursive calls can alter them */
 traverse(head,scratch,sptr);		/* go walk the directory tree */
 for (; head != NULL; head = head -> fwd)
   free(head);				/* return the path segments */
 return(numfnd);			/* and return the number of matches */
}

/*
 * traverse:
 *  Walks the directory tree looking for matches to its arguments.
 *  The algorithm is, briefly:
 *   If the current pattern segment contains no wildcards, that
 *   segment is added to what we already have.  If the name so far
 *   exists, we call ourselves recursively with the next segment
 *   in the pattern string; otherwise, we just return.
 *
 *   If the current pattern segment contains wildcards, we
 *   open the name we've accumulated so far (assuming it is
 *   really a directory), then read each filename in it, and, if
 *   it matches the wildcard pattern segment, add that filename
 *   to what we have so far and call ourselves recursively on the
 *   next segment.
 *
 *   Finally, when no more pattern segments remain, we add what
 *   we've accumulated so far to the result array and increment
 *   the number of matches.
 *
 * Input: a pattern path list (as generated by splitpath), a string
 *	  pointer that points to what we've traversed so far (this
 *	  can be initialized to "/" to start the search at the root
 *	  directory, or to "./" to start the search at the current
 *	  directory), and a string pointer to the end of the string
 *	  in the previous argument.
 * Returns: nothing.
 */

traverse(pl,sofar,endcur)
struct path *pl;
char *sofar,*endcur;
{
 DIR *fd;
 struct direct *dirbuf;
 struct stat statbuf;
 if (pl == NULL)
 {
  *--endcur = '\0';                    /* end string, overwrite trailing / */
  addresult(sofar);
  return;
 }
 if (!iswild(pl -> npart))
 {
  strcpy(endcur,pl -> npart);
  endcur += strlen(pl -> npart);
  *endcur = '\0';                     	/* end current string */
  if (stat(sofar,&statbuf) == 0)	/* if current piece exists */
  {
      *endcur++ = '/';                  /* add slash to end */
      *endcur = '\0';			/* and end the string */
      traverse(pl -> fwd,sofar,endcur);
  }
  return;
 }
/* cont'd... */

/*...traverse, cont'd */

/* segment contains wildcards, have to search directory */
 *endcur = '\0';                        	/* end current string */
 if (stat(sofar,&statbuf) == -1) return;   	/* doesn't exist, forget it */
 if ((statbuf.st_mode & S_IFDIR) == 0) return;  /* not a directory, skip */
 if ((fd = opendir(sofar)) == NULL) return;  	/* can't open, forget it */
 while (dirbuf = readdir(fd))
  if (dirbuf->d_ino != 0 && match(pl -> npart,dirbuf->d_name)) {
    char *eos;
    strcpy(endcur,dirbuf->d_name);
    eos = endcur + strlen(dirbuf->d_name);
    *eos = '/';                    /* end this segment */
    traverse(pl -> fwd,sofar,eos+1);
  }
 closedir(fd);
}

/*
 * addresult:
 *  Adds a result string to the result array.  Increments the number
 *  of matches found, copies the found string into our string
 *  buffer, and puts a pointer to the buffer into the caller's result
 *  array.  Our free buffer pointer is updated.  If there is no
 *  more room in the caller's array, the number of matches is set to -1.
 * Input: a result string.
 * Returns: nothing.
 */

addresult(str)
char *str;
{
 int l;
 if (strncmp(str,"./",2) == 0) str += 2;
 if (--remlen < 0) {
  numfnd = -1;
  return;
 }
 l = strlen(str) + 1;			/* size this will take up */
 if ((freeptr + l) > &sspace[SSPACE]) {
    numfnd = -1;			/* do not record if not enough space */
    return;
  }
 strcpy(freeptr,str);
 *resptr++ = freeptr;
 freeptr += l;
 numfnd++;
}

iswild(str)
char *str;
{
 char c;
 while ((c = *str++) != '\0')
   if (c == '*' || c == '?') return(1);
 return(0);
}

/*
 * match:
 *  pattern matcher.  Takes a string and a pattern possibly containing
 *  the wildcard characters '*' and '?'.  Returns true if the pattern
 *  matches the string, false otherwise.
 * by: Jeff Damens, CUCCA
 *
 * Input: a string and a wildcard pattern.
 * Returns: 1 if match, 0 if no match.
 */

match(pattern,string) char *pattern,*string; {
    char *psave,*ssave;			/* back up pointers for failure */
    psave = ssave = NULL;
    while (1) {
	for (; *pattern == *string; pattern++,string++)  /* skip first */
	    if (*string == '\0') return(1);	/* end of strings, succeed */
	if (*string != '\0' && *pattern == '?') {
	    pattern++;			/* '?', let it match */
	    string++;
	} else if (*pattern == '*') {	/* '*' ... */
	    psave = ++pattern;		/* remember where we saw it */
	    ssave = string;		/* let it match 0 chars */
	} else if (ssave != NULL && *ssave != '\0') {	/* if not at end  */
  					/* ...have seen a star */
	    string = ++ssave;		/* skip 1 char from string */
	    pattern = psave;		/* and back up pattern */
	} else return(0);		/* otherwise just fail */
    }
}
@//E*O*F ckzbsd.c//
chmod u=rw,g=r,o=r ckzbsd.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     616    1914   12117 ckwart.c
     101     685    4018 ckwart.doc
      52     167    1648 ckwho.txt
      18      88     504 ckxbsd.bwr
     536    2494   15108 ckxbsd.c
     642    3225   19296 ckzbsd.c
    1965    8573   52691 total
!!!
wc  ckwart.c ckwart.doc ckwho.txt ckxbsd.bwr ckxbsd.c ckzbsd.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0



More information about the Comp.sources.unix mailing list