[comp.sources.misc] v18i022: perl - The perl programming language, Part04/36

lwall@netlabs.com (Larry Wall) (04/15/91)

Submitted-by: Larry Wall <lwall@netlabs.com>
Posting-number: Volume 18, Issue 22
Archive-name: perl/part04

[There are 36 kits for perl version 4.0.]

#! /bin/sh

# Make a new directory for the perl sources, cd to it, and run kits 1
# thru 36 through sh.  When all 36 kits have been run, read README.

echo "This is perl 4.0 kit 4 (of 36).  If kit 4 is complete, the line"
echo '"'"End of kit 4 (of 36)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir  2>/dev/null
echo Extracting toke.c:AA
sed >toke.c:AA <<'!STUFFY!FUNK!' -e 's/X//'
X/* $RCSfile: toke.c,v $$Revision: 4.0.1.1 $$Date: 91/04/12 09:18:18 $
X *
X *    Copyright (c) 1989, Larry Wall
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the perl 3.0 kit.
X *
X * $Log:	toke.c,v $
X * Revision 4.0.1.1  91/04/12  09:18:18  lwall
X * patch1: perl -de "print" wouldn't stop at the first statement
X * 
X * Revision 4.0  91/03/20  01:42:14  lwall
X * 4.0 baseline.
X * 
X */
X
X#include "EXTERN.h"
X#include "perl.h"
X#include "perly.h"
X
X#ifdef I_FCNTL
X#include <fcntl.h>
X#endif
X#ifdef I_SYS_FILE
X#include <sys/file.h>
X#endif
X
X/* which backslash sequences to keep in m// or s// */
X
Xstatic char *patleave = "\\.^$@dDwWsSbB+*?|()-nrtf0123456789[{]}";
X
Xchar *reparse;		/* if non-null, scanident found ${foo[$bar]} */
X
Xvoid checkcomma();
X
X#ifdef CLINE
X#undef CLINE
X#endif
X#define CLINE (cmdline = (curcmd->c_line < cmdline ? curcmd->c_line : cmdline))
X
X#define META(c) ((c) | 128)
X
X#define RETURN(retval) return (bufptr = s,(int)retval)
X#define OPERATOR(retval) return (expectterm = TRUE,bufptr = s,(int)retval)
X#define TERM(retval) return (CLINE, expectterm = FALSE,bufptr = s,(int)retval)
X#define LOOPX(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)LOOPEX)
X#define FTST(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)FILETEST)
X#define FUN0(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC0)
X#define FUN1(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC1)
X#define FUN2(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC2)
X#define FUN2x(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC2x)
X#define FUN3(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC3)
X#define FUN4(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC4)
X#define FUN5(f) return(yylval.ival = f,expectterm = FALSE,bufptr = s,(int)FUNC5)
X#define FL(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FLIST)
X#define FL2(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FLIST2)
X#define HFUN(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)HSHFUN)
X#define HFUN3(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)HSHFUN3)
X#define LFUN(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LVALFUN)
X#define AOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)ADDOP)
X#define MOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)MULOP)
X#define EOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)EQOP)
X#define ROP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)RELOP)
X#define FOP(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP)
X#define FOP2(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP2)
X#define FOP3(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP3)
X#define FOP4(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP4)
X#define FOP22(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP22)
X#define FOP25(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP25)
X
X/* This bit of chicanery makes a unary function followed by
X * a parenthesis into a function with one argument, highest precedence.
X */
X#define UNI(f) return(yylval.ival = f,expectterm = TRUE,bufptr = s, \
X	(*s == '(' || (s = skipspace(s), *s == '(') ? (int)FUNC1 : (int)UNIOP) )
X
X/* This does similarly for list operators, merely by pretending that the
X * paren came before the listop rather than after.
X */
X#define LOP(f) return(CLINE, *s == '(' || (s = skipspace(s), *s == '(') ? \
X	(*s = META('('), bufptr = oldbufptr, '(') : \
X	(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LISTOP))
X/* grandfather return to old style */
X#define OLDLOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)LISTOP)
X
Xchar *
Xskipspace(s)
Xregister char *s;
X{
X    while (s < bufend && isascii(*s) && isspace(*s))
X	s++;
X    return s;
X}
X
X#ifdef CRIPPLED_CC
X
X#undef UNI
X#undef LOP
X#define UNI(f) return uni(f,s)
X#define LOP(f) return lop(f,s)
X
Xint
Xuni(f,s)
Xint f;
Xchar *s;
X{
X    yylval.ival = f;
X    expectterm = TRUE;
X    bufptr = s;
X    if (*s == '(')
X	return FUNC1;
X    s = skipspace(s);
X    if (*s == '(')
X	return FUNC1;
X    else
X	return UNIOP;
X}
X
Xint
Xlop(f,s)
Xint f;
Xchar *s;
X{
X    CLINE;
X    if (*s != '(')
X	s = skipspace(s);
X    if (*s == '(') {
X	*s = META('(');
X	bufptr = oldbufptr;
X	return '(';
X    }
X    else {
X	yylval.ival=f;
X	expectterm = TRUE;
X	bufptr = s;
X	return LISTOP;
X    }
X}
X
X#endif /* CRIPPLED_CC */
X
Xyylex()
X{
X    register char *s = bufptr;
X    register char *d;
X    register int tmp;
X    static bool in_format = FALSE;
X    static bool firstline = TRUE;
X    extern int yychar;		/* last token */
X
X    oldoldbufptr = oldbufptr;
X    oldbufptr = s;
X
X  retry:
X#ifdef YYDEBUG
X    if (debug & 1)
X	if (index(s,'\n'))
X	    fprintf(stderr,"Tokener at %s",s);
X	else
X	    fprintf(stderr,"Tokener at %s\n",s);
X#endif
X#ifdef BADSWITCH
X    if (*s & 128) {
X	if ((*s & 127) == '(')
X	    *s++ = '(';
X	else
X	    warn("Unrecognized character \\%03o ignored", *s++ & 255);
X	goto retry;
X    }
X#endif
X    switch (*s) {
X    default:
X	if ((*s & 127) == '(')
X	    *s++ = '(';
X	else
X	    warn("Unrecognized character \\%03o ignored", *s++ & 255);
X	goto retry;
X    case 4:
X    case 26:
X	goto fake_eof;			/* emulate EOF on ^D or ^Z */
X    case 0:
X	if (!rsfp)
X	    RETURN(0);
X	if (s++ < bufend)
X	    goto retry;			/* ignore stray nulls */
X	if (firstline) {
X	    firstline = FALSE;
X	    if (minus_n || minus_p || perldb) {
X		str_set(linestr,"");
X		if (perldb) {
X		    char *getenv();
X		    char *pdb = getenv("PERLDB");
X
X		    str_cat(linestr, pdb ? pdb : "require 'perldb.pl'");
X		    str_cat(linestr, ";");
X		}
X		if (minus_n || minus_p) {
X		    str_cat(linestr,"line: while (<>) {");
X		    if (minus_l)
X			str_cat(linestr,"chop;");
X		    if (minus_a)
X			str_cat(linestr,"@F=split(' ');");
X		}
X		oldoldbufptr = oldbufptr = s = str_get(linestr);
X		bufend = linestr->str_ptr + linestr->str_cur;
X		goto retry;
X	    }
X	}
X	if (in_format) {
X	    bufptr = bufend;
X	    yylval.formval = load_format();
X	    in_format = FALSE;
X	    oldoldbufptr = oldbufptr = s = str_get(linestr) + 1;
X	    bufend = linestr->str_ptr + linestr->str_cur;
X	    OPERATOR(FORMLIST);
X	}
X	curcmd->c_line++;
X#ifdef CRYPTSCRIPT
X	cryptswitch();
X#endif /* CRYPTSCRIPT */
X	do {
X	    if ((s = str_gets(linestr, rsfp, 0)) == Nullch) {
X	      fake_eof:
X		if (rsfp) {
X		    if (preprocess)
X			(void)mypclose(rsfp);
X		    else if (rsfp == stdin)
X			clearerr(stdin);
X		    else
X			(void)fclose(rsfp);
X		    rsfp = Nullfp;
X		}
X		if (minus_n || minus_p) {
X		    str_set(linestr,minus_p ? ";}continue{print" : "");
X		    str_cat(linestr,";}");
X		    oldoldbufptr = oldbufptr = s = str_get(linestr);
X		    bufend = linestr->str_ptr + linestr->str_cur;
X		    minus_n = minus_p = 0;
X		    goto retry;
X		}
X		oldoldbufptr = oldbufptr = s = str_get(linestr);
X		str_set(linestr,"");
X		RETURN(';');	/* not infinite loop because rsfp is NULL now */
X	    }
X	    if (doextract && *linestr->str_ptr == '#')
X		doextract = FALSE;
X	} while (doextract);
X	oldoldbufptr = oldbufptr = bufptr = s;
X	if (perldb) {
X	    STR *str = Str_new(85,0);
X
X	    str_sset(str,linestr);
X	    astore(stab_xarray(curcmd->c_filestab),(int)curcmd->c_line,str);
X	}
X#ifdef DEBUG
X	if (firstline) {
X	    char *showinput();
X	    s = showinput();
X	}
X#endif
X	bufend = linestr->str_ptr + linestr->str_cur;
X	if (curcmd->c_line == 1) {
X	    if (*s == '#' && s[1] == '!') {
X		if (!in_eval && !instr(s,"perl") && instr(origargv[0],"perl")) {
X		    char **newargv;
X		    char *cmd;
X
X		    s += 2;
X		    if (*s == ' ')
X			s++;
X		    cmd = s;
X		    while (s < bufend && !isspace(*s))
X			s++;
X		    *s++ = '\0';
X		    while (s < bufend && isspace(*s))
X			s++;
X		    if (s < bufend) {
X			Newz(899,newargv,origargc+3,char*);
X			newargv[1] = s;
X			while (s < bufend && !isspace(*s))
X			    s++;
X			*s = '\0';
X			Copy(origargv+1, newargv+2, origargc+1, char*);
X		    }
X		    else
X			newargv = origargv;
X		    newargv[0] = cmd;
X		    execv(cmd,newargv);
X		    fatal("Can't exec %s", cmd);
X		}
X	    }
X	    else {
X		while (s < bufend && isspace(*s))
X		    s++;
X		if (*s == ':')	/* for csh's that have to exec sh scripts */
X		    s++;
X	    }
X	}
X	goto retry;
X    case ' ': case '\t': case '\f': case '\r': case 013:
X	s++;
X	goto retry;
X    case '#':
X	if (preprocess && s == str_get(linestr) &&
X	       s[1] == ' ' && isdigit(s[2])) {
X	    curcmd->c_line = atoi(s+2)-1;
X	    for (s += 2; isdigit(*s); s++) ;
X	    d = bufend;
X	    while (s < d && isspace(*s)) s++;
X	    s[strlen(s)-1] = '\0';	/* wipe out newline */
X	    if (*s == '"') {
X		s++;
X		s[strlen(s)-1] = '\0';	/* wipe out trailing quote */
X	    }
X	    if (*s)
X		curcmd->c_filestab = fstab(s);
X	    else
X		curcmd->c_filestab = fstab(origfilename);
X	    oldoldbufptr = oldbufptr = s = str_get(linestr);
X	}
X	/* FALL THROUGH */
X    case '\n':
X	if (in_eval && !rsfp) {
X	    d = bufend;
X	    while (s < d && *s != '\n')
X		s++;
X	    if (s < d)
X		s++;
X	    if (perldb) {
X		STR *str = Str_new(85,0);
X
X		str_nset(str,linestr->str_ptr, s - linestr->str_ptr);
X		astore(stab_xarray(curcmd->c_filestab),(int)curcmd->c_line,str);
X		str_chop(linestr, s);
X	    }
X	    if (in_format) {
X		bufptr = s;
X		yylval.formval = load_format();
X		in_format = FALSE;
X		oldoldbufptr = oldbufptr = s = bufptr + 1;
X		TERM(FORMLIST);
X	    }
X	    curcmd->c_line++;
X	}
X	else {
X	    *s = '\0';
X	    bufend = s;
X	}
X	goto retry;
X    case '-':
X	if (s[1] && isalpha(s[1]) && !isalpha(s[2])) {
X	    s++;
X	    switch (*s++) {
X	    case 'r': FTST(O_FTEREAD);
X	    case 'w': FTST(O_FTEWRITE);
X	    case 'x': FTST(O_FTEEXEC);
X	    case 'o': FTST(O_FTEOWNED);
X	    case 'R': FTST(O_FTRREAD);
X	    case 'W': FTST(O_FTRWRITE);
X	    case 'X': FTST(O_FTREXEC);
X	    case 'O': FTST(O_FTROWNED);
X	    case 'e': FTST(O_FTIS);
X	    case 'z': FTST(O_FTZERO);
X	    case 's': FTST(O_FTSIZE);
X	    case 'f': FTST(O_FTFILE);
X	    case 'd': FTST(O_FTDIR);
X	    case 'l': FTST(O_FTLINK);
X	    case 'p': FTST(O_FTPIPE);
X	    case 'S': FTST(O_FTSOCK);
X	    case 'u': FTST(O_FTSUID);
X	    case 'g': FTST(O_FTSGID);
X	    case 'k': FTST(O_FTSVTX);
X	    case 'b': FTST(O_FTBLK);
X	    case 'c': FTST(O_FTCHR);
X	    case 't': FTST(O_FTTTY);
X	    case 'T': FTST(O_FTTEXT);
X	    case 'B': FTST(O_FTBINARY);
X	    case 'M': stabent("\024",TRUE); FTST(O_FTMTIME);
X	    case 'A': stabent("\024",TRUE); FTST(O_FTATIME);
X	    case 'C': stabent("\024",TRUE); FTST(O_FTCTIME);
X	    default:
X		s -= 2;
X		break;
X	    }
X	}
X	tmp = *s++;
X	if (*s == tmp) {
X	    s++;
X	    RETURN(DEC);
X	}
X	if (expectterm)
X	    OPERATOR('-');
X	else
X	    AOP(O_SUBTRACT);
X    case '+':
X	tmp = *s++;
X	if (*s == tmp) {
X	    s++;
X	    RETURN(INC);
X	}
X	if (expectterm)
X	    OPERATOR('+');
X	else
X	    AOP(O_ADD);
X
X    case '*':
X	if (expectterm) {
X	    s = scanident(s,bufend,tokenbuf);
X	    yylval.stabval = stabent(tokenbuf,TRUE);
X	    TERM(STAR);
X	}
X	tmp = *s++;
X	if (*s == tmp) {
X	    s++;
X	    OPERATOR(POW);
X	}
X	MOP(O_MULTIPLY);
X    case '%':
X	if (expectterm) {
X	    s = scanident(s,bufend,tokenbuf);
X	    yylval.stabval = hadd(stabent(tokenbuf,TRUE));
X	    TERM(HSH);
X	}
X	s++;
X	MOP(O_MODULO);
X
X    case '^':
X    case '~':
X    case '(':
X    case ',':
X    case ':':
X    case '[':
X	tmp = *s++;
X	OPERATOR(tmp);
X    case '{':
X	tmp = *s++;
X	if (isspace(*s) || *s == '#')
X	    cmdline = NOLINE;   /* invalidate current command line number */
X	OPERATOR(tmp);
X    case ';':
X	if (curcmd->c_line < cmdline)
X	    cmdline = curcmd->c_line;
X	tmp = *s++;
X	OPERATOR(tmp);
X    case ')':
X    case ']':
X	tmp = *s++;
X	TERM(tmp);
X    case '}':
X	tmp = *s++;
X	RETURN(tmp);
X    case '&':
X	s++;
X	tmp = *s++;
X	if (tmp == '&')
X	    OPERATOR(ANDAND);
X	s--;
X	if (expectterm) {
X	    d = bufend;
X	    while (s < d && isspace(*s))
X		s++;
X	    if (isalpha(*s) || *s == '_' || *s == '\'')
X		*(--s) = '\\';	/* force next ident to WORD */
X	    OPERATOR(AMPER);
X	}
X	OPERATOR('&');
X    case '|':
X	s++;
X	tmp = *s++;
X	if (tmp == '|')
X	    OPERATOR(OROR);
X	s--;
X	OPERATOR('|');
X    case '=':
X	s++;
X	tmp = *s++;
X	if (tmp == '=')
X	    EOP(O_EQ);
X	if (tmp == '~')
X	    OPERATOR(MATCH);
X	s--;
X	OPERATOR('=');
X    case '!':
X	s++;
X	tmp = *s++;
X	if (tmp == '=')
X	    EOP(O_NE);
X	if (tmp == '~')
X	    OPERATOR(NMATCH);
X	s--;
X	OPERATOR('!');
X    case '<':
X	if (expectterm) {
X	    s = scanstr(s);
X	    TERM(RSTRING);
X	}
X	s++;
X	tmp = *s++;
X	if (tmp == '<')
X	    OPERATOR(LS);
X	if (tmp == '=') {
X	    tmp = *s++;
X	    if (tmp == '>')
X		EOP(O_NCMP);
X	    s--;
X	    ROP(O_LE);
X	}
X	s--;
X	ROP(O_LT);
X    case '>':
X	s++;
X	tmp = *s++;
X	if (tmp == '>')
X	    OPERATOR(RS);
X	if (tmp == '=')
X	    ROP(O_GE);
X	s--;
X	ROP(O_GT);
X
X#define SNARFWORD \
X	d = tokenbuf; \
X	while (isascii(*s) && \
X	  (isalpha(*s) || isdigit(*s) || *s == '_' || *s == '\'')) \
X	    *d++ = *s++; \
X	while (d[-1] == '\'') \
X	    d--,s--; \
X	*d = '\0'; \
X	d = tokenbuf;
X
X    case '$':
X	if (s[1] == '#' && (isalpha(s[2]) || s[2] == '_')) {
X	    s++;
X	    s = scanident(s,bufend,tokenbuf);
X	    yylval.stabval = aadd(stabent(tokenbuf,TRUE));
X	    TERM(ARYLEN);
X	}
X	d = s;
X	s = scanident(s,bufend,tokenbuf);
X	if (reparse) {		/* turn ${foo[bar]} into ($foo[bar]) */
X	  do_reparse:
X	    s[-1] = ')';
X	    s = d;
X	    s[1] = s[0];
X	    s[0] = '(';
X	    goto retry;
X	}
X	yylval.stabval = stabent(tokenbuf,TRUE);
X	TERM(REG);
X
X    case '@':
X	d = s;
X	s = scanident(s,bufend,tokenbuf);
X	if (reparse)
X	    goto do_reparse;
X	yylval.stabval = aadd(stabent(tokenbuf,TRUE));
X	TERM(ARY);
X
X    case '/':			/* may either be division or pattern */
X    case '?':			/* may either be conditional or pattern */
X	if (expectterm) {
X	    s = scanpat(s);
X	    TERM(PATTERN);
X	}
X	tmp = *s++;
X	if (tmp == '/')
X	    MOP(O_DIVIDE);
X	OPERATOR(tmp);
X
X    case '.':
X	if (!expectterm || !isdigit(s[1])) {
X	    tmp = *s++;
X	    if (*s == tmp) {
X		s++;
X		OPERATOR(DOTDOT);
X	    }
X	    AOP(O_CONCAT);
X	}
X	/* FALL THROUGH */
X    case '0': case '1': case '2': case '3': case '4':
X    case '5': case '6': case '7': case '8': case '9':
X    case '\'': case '"': case '`':
X	s = scanstr(s);
X	TERM(RSTRING);
X
X    case '\\':	/* some magic to force next word to be a WORD */
X	s++;	/* used by do and sub to force a separate namespace */
X	/* FALL THROUGH */
X    case '_':
X	SNARFWORD;
X	if (d[1] == '_') {
X	    if (strEQ(d,"__LINE__") || strEQ(d,"__FILE__")) {
X		ARG *arg = op_new(1);
X
X		yylval.arg = arg;
X		arg->arg_type = O_ITEM;
X		if (d[2] == 'L')
X		    (void)sprintf(tokenbuf,"%ld",(long)curcmd->c_line);
X		else
X		    strcpy(tokenbuf, stab_val(curcmd->c_filestab)->str_ptr);
X		arg[1].arg_type = A_SINGLE;
X		arg[1].arg_ptr.arg_str = str_make(tokenbuf,strlen(tokenbuf));
X		TERM(RSTRING);
X	    }
X	    else if (strEQ(d,"__END__")) {
X#ifndef TAINT
X		STAB *stab;
X		int fd;
X
X		if (stab = stabent("DATA",FALSE)) {
X		    stab->str_pok |= SP_MULTI;
X		    stab_io(stab) = stio_new();
X		    stab_io(stab)->ifp = rsfp;
X#if defined(HAS_FCNTL) && defined(F_SETFD)
X		    fd = fileno(rsfp);
X		    fcntl(fd,F_SETFD,fd >= 3);
X#endif
X		    if (preprocess)
X			stab_io(stab)->type = '|';
X		    else if (rsfp == stdin)
X			stab_io(stab)->type = '-';
X		    else
X			stab_io(stab)->type = '<';
X		    rsfp = Nullfp;
X		}
X#endif
X		goto fake_eof;
X	    }
X	}
X	break;
X    case 'a': case 'A':
X	SNARFWORD;
X	if (strEQ(d,"alarm"))
X	    UNI(O_ALARM);
X	if (strEQ(d,"accept"))
X	    FOP22(O_ACCEPT);
X	if (strEQ(d,"atan2"))
X	    FUN2(O_ATAN2);
X	break;
X    case 'b': case 'B':
X	SNARFWORD;
X	if (strEQ(d,"bind"))
X	    FOP2(O_BIND);
X	if (strEQ(d,"binmode"))
X	    FOP(O_BINMODE);
X	break;
X    case 'c': case 'C':
X	SNARFWORD;
X	if (strEQ(d,"chop"))
X	    LFUN(O_CHOP);
X	if (strEQ(d,"continue"))
X	    OPERATOR(CONTINUE);
X	if (strEQ(d,"chdir")) {
X	    (void)stabent("ENV",TRUE);	/* may use HOME */
X	    UNI(O_CHDIR);
X	}
X	if (strEQ(d,"close"))
X	    FOP(O_CLOSE);
X	if (strEQ(d,"closedir"))
X	    FOP(O_CLOSEDIR);
X	if (strEQ(d,"cmp"))
X	    EOP(O_SCMP);
X	if (strEQ(d,"caller"))
X	    UNI(O_CALLER);
X	if (strEQ(d,"crypt")) {
X#ifdef FCRYPT
X	    init_des();
X#endif
X	    FUN2(O_CRYPT);
X	}
X	if (strEQ(d,"chmod"))
X	    LOP(O_CHMOD);
X	if (strEQ(d,"chown"))
X	    LOP(O_CHOWN);
X	if (strEQ(d,"connect"))
X	    FOP2(O_CONNECT);
X	if (strEQ(d,"cos"))
X	    UNI(O_COS);
X	if (strEQ(d,"chroot"))
X	    UNI(O_CHROOT);
X	break;
X    case 'd': case 'D':
X	SNARFWORD;
X	if (strEQ(d,"do")) {
X	    d = bufend;
X	    while (s < d && isspace(*s))
X		s++;
X	    if (isalpha(*s) || *s == '_')
X		*(--s) = '\\';	/* force next ident to WORD */
X	    OPERATOR(DO);
X	}
X	if (strEQ(d,"die"))
X	    LOP(O_DIE);
X	if (strEQ(d,"defined"))
X	    LFUN(O_DEFINED);
X	if (strEQ(d,"delete"))
X	    OPERATOR(DELETE);
X	if (strEQ(d,"dbmopen"))
X	    HFUN3(O_DBMOPEN);
X	if (strEQ(d,"dbmclose"))
X	    HFUN(O_DBMCLOSE);
X	if (strEQ(d,"dump"))
X	    LOOPX(O_DUMP);
X	break;
X    case 'e': case 'E':
X	SNARFWORD;
X	if (strEQ(d,"else"))
X	    OPERATOR(ELSE);
X	if (strEQ(d,"elsif")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(ELSIF);
X	}
X	if (strEQ(d,"eq") || strEQ(d,"EQ"))
X	    EOP(O_SEQ);
X	if (strEQ(d,"exit"))
X	    UNI(O_EXIT);
X	if (strEQ(d,"eval")) {
X	    allstabs = TRUE;		/* must initialize everything since */
X	    UNI(O_EVAL);		/* we don't know what will be used */
X	}
X	if (strEQ(d,"eof"))
X	    FOP(O_EOF);
X	if (strEQ(d,"exp"))
X	    UNI(O_EXP);
X	if (strEQ(d,"each"))
X	    HFUN(O_EACH);
X	if (strEQ(d,"exec")) {
X	    set_csh();
X	    LOP(O_EXEC_OP);
X	}
X	if (strEQ(d,"endhostent"))
X	    FUN0(O_EHOSTENT);
X	if (strEQ(d,"endnetent"))
X	    FUN0(O_ENETENT);
X	if (strEQ(d,"endservent"))
X	    FUN0(O_ESERVENT);
X	if (strEQ(d,"endprotoent"))
X	    FUN0(O_EPROTOENT);
X	if (strEQ(d,"endpwent"))
X	    FUN0(O_EPWENT);
X	if (strEQ(d,"endgrent"))
X	    FUN0(O_EGRENT);
X	break;
X    case 'f': case 'F':
X	SNARFWORD;
X	if (strEQ(d,"for") || strEQ(d,"foreach")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(FOR);
X	}
X	if (strEQ(d,"format")) {
X	    d = bufend;
X	    while (s < d && isspace(*s))
X		s++;
X	    if (isalpha(*s) || *s == '_')
X		*(--s) = '\\';	/* force next ident to WORD */
X	    in_format = TRUE;
X	    allstabs = TRUE;		/* must initialize everything since */
X	    OPERATOR(FORMAT);		/* we don't know what will be used */
X	}
X	if (strEQ(d,"fork"))
X	    FUN0(O_FORK);
X	if (strEQ(d,"fcntl"))
X	    FOP3(O_FCNTL);
X	if (strEQ(d,"fileno"))
X	    FOP(O_FILENO);
X	if (strEQ(d,"flock"))
X	    FOP2(O_FLOCK);
X	break;
X    case 'g': case 'G':
X	SNARFWORD;
X	if (strEQ(d,"gt") || strEQ(d,"GT"))
X	    ROP(O_SGT);
X	if (strEQ(d,"ge") || strEQ(d,"GE"))
X	    ROP(O_SGE);
X	if (strEQ(d,"grep"))
X	    FL2(O_GREP);
X	if (strEQ(d,"goto"))
X	    LOOPX(O_GOTO);
X	if (strEQ(d,"gmtime"))
X	    UNI(O_GMTIME);
X	if (strEQ(d,"getc"))
X	    FOP(O_GETC);
X	if (strnEQ(d,"get",3)) {
X	    d += 3;
X	    if (*d == 'p') {
X		if (strEQ(d,"ppid"))
X		    FUN0(O_GETPPID);
X		if (strEQ(d,"pgrp"))
X		    UNI(O_GETPGRP);
X		if (strEQ(d,"priority"))
X		    FUN2(O_GETPRIORITY);
X		if (strEQ(d,"protobyname"))
X		    UNI(O_GPBYNAME);
X		if (strEQ(d,"protobynumber"))
X		    FUN1(O_GPBYNUMBER);
X		if (strEQ(d,"protoent"))
X		    FUN0(O_GPROTOENT);
X		if (strEQ(d,"pwent"))
X		    FUN0(O_GPWENT);
X		if (strEQ(d,"pwnam"))
X		    FUN1(O_GPWNAM);
X		if (strEQ(d,"pwuid"))
X		    FUN1(O_GPWUID);
X		if (strEQ(d,"peername"))
X		    FOP(O_GETPEERNAME);
X	    }
X	    else if (*d == 'h') {
X		if (strEQ(d,"hostbyname"))
X		    UNI(O_GHBYNAME);
X		if (strEQ(d,"hostbyaddr"))
X		    FUN2(O_GHBYADDR);
X		if (strEQ(d,"hostent"))
X		    FUN0(O_GHOSTENT);
X	    }
X	    else if (*d == 'n') {
X		if (strEQ(d,"netbyname"))
X		    UNI(O_GNBYNAME);
X		if (strEQ(d,"netbyaddr"))
X		    FUN2(O_GNBYADDR);
X		if (strEQ(d,"netent"))
X		    FUN0(O_GNETENT);
X	    }
X	    else if (*d == 's') {
X		if (strEQ(d,"servbyname"))
X		    FUN2(O_GSBYNAME);
X		if (strEQ(d,"servbyport"))
X		    FUN2(O_GSBYPORT);
X		if (strEQ(d,"servent"))
X		    FUN0(O_GSERVENT);
X		if (strEQ(d,"sockname"))
X		    FOP(O_GETSOCKNAME);
X		if (strEQ(d,"sockopt"))
X		    FOP3(O_GSOCKOPT);
X	    }
X	    else if (*d == 'g') {
X		if (strEQ(d,"grent"))
X		    FUN0(O_GGRENT);
X		if (strEQ(d,"grnam"))
X		    FUN1(O_GGRNAM);
X		if (strEQ(d,"grgid"))
X		    FUN1(O_GGRGID);
X	    }
X	    else if (*d == 'l') {
X		if (strEQ(d,"login"))
X		    FUN0(O_GETLOGIN);
X	    }
X	    d -= 3;
X	}
X	break;
X    case 'h': case 'H':
X	SNARFWORD;
X	if (strEQ(d,"hex"))
X	    UNI(O_HEX);
X	break;
X    case 'i': case 'I':
X	SNARFWORD;
X	if (strEQ(d,"if")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(IF);
X	}
X	if (strEQ(d,"index"))
X	    FUN2x(O_INDEX);
X	if (strEQ(d,"int"))
X	    UNI(O_INT);
X	if (strEQ(d,"ioctl"))
X	    FOP3(O_IOCTL);
X	break;
X    case 'j': case 'J':
X	SNARFWORD;
X	if (strEQ(d,"join"))
X	    FL2(O_JOIN);
X	break;
X    case 'k': case 'K':
X	SNARFWORD;
X	if (strEQ(d,"keys"))
X	    HFUN(O_KEYS);
X	if (strEQ(d,"kill"))
X	    LOP(O_KILL);
X	break;
X    case 'l': case 'L':
X	SNARFWORD;
X	if (strEQ(d,"last"))
X	    LOOPX(O_LAST);
X	if (strEQ(d,"local"))
X	    OPERATOR(LOCAL);
X	if (strEQ(d,"length"))
X	    UNI(O_LENGTH);
X	if (strEQ(d,"lt") || strEQ(d,"LT"))
X	    ROP(O_SLT);
X	if (strEQ(d,"le") || strEQ(d,"LE"))
X	    ROP(O_SLE);
X	if (strEQ(d,"localtime"))
X	    UNI(O_LOCALTIME);
X	if (strEQ(d,"log"))
X	    UNI(O_LOG);
X	if (strEQ(d,"link"))
X	    FUN2(O_LINK);
X	if (strEQ(d,"listen"))
X	    FOP2(O_LISTEN);
X	if (strEQ(d,"lstat"))
X	    FOP(O_LSTAT);
X	break;
X    case 'm': case 'M':
X	if (s[1] == '\'') {
X	    d = "m";
X	    s++;
X	}
X	else {
X	    SNARFWORD;
X	}
X	if (strEQ(d,"m")) {
X	    s = scanpat(s-1);
X	    if (yylval.arg)
X		TERM(PATTERN);
X	    else
X		RETURN(1);	/* force error */
X	}
X	switch (d[1]) {
X	case 'k':
X	    if (strEQ(d,"mkdir"))
X		FUN2(O_MKDIR);
X	    break;
X	case 's':
X	    if (strEQ(d,"msgctl"))
X		FUN3(O_MSGCTL);
X	    if (strEQ(d,"msgget"))
X		FUN2(O_MSGGET);
X	    if (strEQ(d,"msgrcv"))
X		FUN5(O_MSGRCV);
X	    if (strEQ(d,"msgsnd"))
X		FUN3(O_MSGSND);
X	    break;
X	}
X	break;
X    case 'n': case 'N':
X	SNARFWORD;
X	if (strEQ(d,"next"))
X	    LOOPX(O_NEXT);
X	if (strEQ(d,"ne") || strEQ(d,"NE"))
X	    EOP(O_SNE);
X	break;
X    case 'o': case 'O':
X	SNARFWORD;
X	if (strEQ(d,"open"))
X	    OPERATOR(OPEN);
X	if (strEQ(d,"ord"))
X	    UNI(O_ORD);
X	if (strEQ(d,"oct"))
X	    UNI(O_OCT);
X	if (strEQ(d,"opendir"))
X	    FOP2(O_OPENDIR);
X	break;
X    case 'p': case 'P':
X	SNARFWORD;
X	if (strEQ(d,"print")) {
X	    checkcomma(s,"filehandle");
X	    LOP(O_PRINT);
X	}
X	if (strEQ(d,"printf")) {
X	    checkcomma(s,"filehandle");
X	    LOP(O_PRTF);
X	}
X	if (strEQ(d,"push")) {
X	    yylval.ival = O_PUSH;
X	    OPERATOR(PUSH);
X	}
X	if (strEQ(d,"pop"))
X	    OPERATOR(POP);
X	if (strEQ(d,"pack"))
X	    FL2(O_PACK);
X	if (strEQ(d,"package"))
X	    OPERATOR(PACKAGE);
X	if (strEQ(d,"pipe"))
X	    FOP22(O_PIPE);
X	break;
X    case 'q': case 'Q':
X	SNARFWORD;
X	if (strEQ(d,"q")) {
X	    s = scanstr(s-1);
X	    TERM(RSTRING);
X	}
X	if (strEQ(d,"qq")) {
X	    s = scanstr(s-2);
X	    TERM(RSTRING);
X	}
X	if (strEQ(d,"qx")) {
X	    s = scanstr(s-2);
X	    TERM(RSTRING);
X	}
X	break;
X    case 'r': case 'R':
X	SNARFWORD;
X	if (strEQ(d,"return"))
X	    OLDLOP(O_RETURN);
X	if (strEQ(d,"require")) {
X	    allstabs = TRUE;		/* must initialize everything since */
X	    UNI(O_REQUIRE);		/* we don't know what will be used */
X	}
X	if (strEQ(d,"reset"))
X	    UNI(O_RESET);
X	if (strEQ(d,"redo"))
X	    LOOPX(O_REDO);
X	if (strEQ(d,"rename"))
X	    FUN2(O_RENAME);
X	if (strEQ(d,"rand"))
X	    UNI(O_RAND);
X	if (strEQ(d,"rmdir"))
X	    UNI(O_RMDIR);
X	if (strEQ(d,"rindex"))
X	    FUN2x(O_RINDEX);
X	if (strEQ(d,"read"))
X	    FOP3(O_READ);
X	if (strEQ(d,"readdir"))
X	    FOP(O_READDIR);
X	if (strEQ(d,"rewinddir"))
X	    FOP(O_REWINDDIR);
X	if (strEQ(d,"recv"))
X	    FOP4(O_RECV);
X	if (strEQ(d,"reverse"))
X	    LOP(O_REVERSE);
X	if (strEQ(d,"readlink"))
X	    UNI(O_READLINK);
X	break;
X    case 's': case 'S':
X	if (s[1] == '\'') {
X	    d = "s";
X	    s++;
X	}
X	else {
X	    SNARFWORD;
X	}
X	if (strEQ(d,"s")) {
X	    s = scansubst(s);
X	    if (yylval.arg)
X		TERM(SUBST);
X	    else
X		RETURN(1);	/* force error */
X	}
X	switch (d[1]) {
X	case 'a':
X	case 'b':
X	    break;
X	case 'c':
X	    if (strEQ(d,"scalar"))
X		UNI(O_SCALAR);
X	    break;
X	case 'd':
X	    break;
X	case 'e':
X	    if (strEQ(d,"select"))
X		OPERATOR(SSELECT);
X	    if (strEQ(d,"seek"))
X		FOP3(O_SEEK);
X	    if (strEQ(d,"semctl"))
X		FUN4(O_SEMCTL);
X	    if (strEQ(d,"semget"))
X		FUN3(O_SEMGET);
X	    if (strEQ(d,"semop"))
X		FUN2(O_SEMOP);
X	    if (strEQ(d,"send"))
X		FOP3(O_SEND);
X	    if (strEQ(d,"setpgrp"))
X		FUN2(O_SETPGRP);
X	    if (strEQ(d,"setpriority"))
X		FUN3(O_SETPRIORITY);
X	    if (strEQ(d,"sethostent"))
X		FUN1(O_SHOSTENT);
X	    if (strEQ(d,"setnetent"))
X		FUN1(O_SNETENT);
X	    if (strEQ(d,"setservent"))
X		FUN1(O_SSERVENT);
X	    if (strEQ(d,"setprotoent"))
X		FUN1(O_SPROTOENT);
X	    if (strEQ(d,"setpwent"))
X		FUN0(O_SPWENT);
X	    if (strEQ(d,"setgrent"))
X		FUN0(O_SGRENT);
X	    if (strEQ(d,"seekdir"))
X		FOP2(O_SEEKDIR);
X	    if (strEQ(d,"setsockopt"))
X		FOP4(O_SSOCKOPT);
X	    break;
X	case 'f':
X	case 'g':
X	    break;
X	case 'h':
X	    if (strEQ(d,"shift"))
X		TERM(SHIFT);
X	    if (strEQ(d,"shmctl"))
X		FUN3(O_SHMCTL);
X	    if (strEQ(d,"shmget"))
X		FUN3(O_SHMGET);
X	    if (strEQ(d,"shmread"))
X		FUN4(O_SHMREAD);
X	    if (strEQ(d,"shmwrite"))
X		FUN4(O_SHMWRITE);
X	    if (strEQ(d,"shutdown"))
X		FOP2(O_SHUTDOWN);
X	    break;
X	case 'i':
X	    if (strEQ(d,"sin"))
X		UNI(O_SIN);
X	    break;
X	case 'j':
X	case 'k':
X	    break;
X	case 'l':
X	    if (strEQ(d,"sleep"))
X		UNI(O_SLEEP);
X	    break;
X	case 'm':
X	case 'n':
X	    break;
X	case 'o':
X	    if (strEQ(d,"socket"))
X		FOP4(O_SOCKET);
X	    if (strEQ(d,"socketpair"))
X		FOP25(O_SOCKPAIR);
X	    if (strEQ(d,"sort")) {
X		checkcomma(s,"subroutine name");
X		d = bufend;
X		while (s < d && isascii(*s) && isspace(*s)) s++;
X		if (*s == ';' || *s == ')')		/* probably a close */
X		    fatal("sort is now a reserved word");
X		if (isascii(*s) && (isalpha(*s) || *s == '_')) {
X		    for (d = s; isalpha(*d) || isdigit(*d) || *d == '_'; d++) ;
X		    strncpy(tokenbuf,s,d-s);
X		    if (strNE(tokenbuf,"keys") &&
X			strNE(tokenbuf,"values") &&
X			strNE(tokenbuf,"split") &&
X			strNE(tokenbuf,"grep") &&
X			strNE(tokenbuf,"readdir") &&
X			strNE(tokenbuf,"unpack") &&
X			strNE(tokenbuf,"do") &&
X			(d >= bufend || isspace(*d)) )
X			*(--s) = '\\';	/* force next ident to WORD */
X		}
X		LOP(O_SORT);
X	    }
X	    break;
X	case 'p':
X	    if (strEQ(d,"split"))
X		TERM(SPLIT);
X	    if (strEQ(d,"sprintf"))
X		FL(O_SPRINTF);
X	    if (strEQ(d,"splice")) {
X		yylval.ival = O_SPLICE;
X		OPERATOR(PUSH);
X	    }
X	    break;
X	case 'q':
X	    if (strEQ(d,"sqrt"))
X		UNI(O_SQRT);
X	    break;
X	case 'r':
X	    if (strEQ(d,"srand"))
X		UNI(O_SRAND);
X	    break;
X	case 's':
X	    break;
X	case 't':
X	    if (strEQ(d,"stat"))
X		FOP(O_STAT);
X	    if (strEQ(d,"study")) {
X		sawstudy++;
X		LFUN(O_STUDY);
X	    }
X	    break;
X	case 'u':
X	    if (strEQ(d,"substr"))
X		FUN2x(O_SUBSTR);
X	    if (strEQ(d,"sub")) {
X		subline = curcmd->c_line;
X		d = bufend;
X		while (s < d && isspace(*s))
X		    s++;
X		if (isalpha(*s) || *s == '_' || *s == '\'') {
X		    if (perldb) {
X			str_sset(subname,curstname);
X			str_ncat(subname,"'",1);
X			for (d = s+1;
X			  isalpha(*d) || isdigit(*d) || *d == '_' || *d == '\'';
X			  d++);
X			if (d[-1] == '\'')
X			    d--;
X			str_ncat(subname,s,d-s);
X		    }
X		    *(--s) = '\\';	/* force next ident to WORD */
X		}
X		else if (perldb)
X		    str_set(subname,"?");
X		OPERATOR(SUB);
X	    }
X	    break;
X	case 'v':
X	case 'w':
X	case 'x':
X	    break;
X	case 'y':
X	    if (strEQ(d,"system")) {
X		set_csh();
X		LOP(O_SYSTEM);
X	    }
X	    if (strEQ(d,"symlink"))
X		FUN2(O_SYMLINK);
X	    if (strEQ(d,"syscall"))
X		LOP(O_SYSCALL);
X	    if (strEQ(d,"sysread"))
X		FOP3(O_SYSREAD);
X	    if (strEQ(d,"syswrite"))
X		FOP3(O_SYSWRITE);
X	    break;
X	case 'z':
X	    break;
X	}
X	break;
X    case 't': case 'T':
X	SNARFWORD;
X	if (strEQ(d,"tr")) {
X	    s = scantrans(s);
X	    if (yylval.arg)
X		TERM(TRANS);
X	    else
X		RETURN(1);	/* force error */
X	}
X	if (strEQ(d,"tell"))
X	    FOP(O_TELL);
X	if (strEQ(d,"telldir"))
X	    FOP(O_TELLDIR);
X	if (strEQ(d,"time"))
X	    FUN0(O_TIME);
X	if (strEQ(d,"times"))
X	    FUN0(O_TMS);
X	if (strEQ(d,"truncate"))
X	    FOP2(O_TRUNCATE);
X	break;
X    case 'u': case 'U':
X	SNARFWORD;
X	if (strEQ(d,"using"))
X	    OPERATOR(USING);
X	if (strEQ(d,"until")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(UNTIL);
X	}
X	if (strEQ(d,"unless")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(UNLESS);
X	}
X	if (strEQ(d,"unlink"))
X	    LOP(O_UNLINK);
X	if (strEQ(d,"undef"))
X	    LFUN(O_UNDEF);
X	if (strEQ(d,"unpack"))
X	    FUN2(O_UNPACK);
X	if (strEQ(d,"utime"))
X	    LOP(O_UTIME);
X	if (strEQ(d,"umask"))
X	    UNI(O_UMASK);
X	if (strEQ(d,"unshift")) {
X	    yylval.ival = O_UNSHIFT;
X	    OPERATOR(PUSH);
X	}
X	break;
X    case 'v': case 'V':
X	SNARFWORD;
X	if (strEQ(d,"values"))
X	    HFUN(O_VALUES);
X	if (strEQ(d,"vec")) {
X	    sawvec = TRUE;
X	    FUN3(O_VEC);
X	}
X	break;
X    case 'w': case 'W':
X	SNARFWORD;
X	if (strEQ(d,"while")) {
X	    yylval.ival = curcmd->c_line;
X	    OPERATOR(WHILE);
X	}
X	if (strEQ(d,"warn"))
X	    LOP(O_WARN);
X	if (strEQ(d,"wait"))
X	    FUN0(O_WAIT);
X	if (strEQ(d,"waitpid"))
X	    FUN2(O_WAITPID);
X	if (strEQ(d,"wantarray")) {
X	    yylval.arg = op_new(1);
X	    yylval.arg->arg_type = O_ITEM;
X	    yylval.arg[1].arg_type = A_WANTARRAY;
X	    TERM(RSTRING);
X	}
X	if (strEQ(d,"write"))
X	    FOP(O_WRITE);
X	break;
X    case 'x': case 'X':
X	SNARFWORD;
X	if (!expectterm && strEQ(d,"x"))
X	    MOP(O_REPEAT);
X	break;
X    case 'y': case 'Y':
X	if (s[1] == '\'') {
X	    d = "y";
X	    s++;
X	}
X	else {
X	    SNARFWORD;
X	}
X	if (strEQ(d,"y")) {
X	    s = scantrans(s);
X	    TERM(TRANS);
X	}
X	break;
X    case 'z': case 'Z':
X	SNARFWORD;
X	break;
X    }
X    yylval.cval = savestr(d);
X    expectterm = FALSE;
X    if (oldoldbufptr && oldoldbufptr < bufptr) {
X	while (isspace(*oldoldbufptr))
X	    oldoldbufptr++;
X	if (*oldoldbufptr == 'p' && strnEQ(oldoldbufptr,"print",5))
X	    expectterm = TRUE;
X	else if (*oldoldbufptr == 's' && strnEQ(oldoldbufptr,"sort",4))
X	    expectterm = TRUE;
X    }
X    return (CLINE, bufptr = s, (int)WORD);
X}
X
Xvoid
Xcheckcomma(s,what)
Xregister char *s;
Xchar *what;
X{
X    char *someword;
X
X    if (*s == '(')
X	s++;
X    while (s < bufend && isascii(*s) && isspace(*s))
X	s++;
X    if (isascii(*s) && (isalpha(*s) || *s == '_')) {
X	someword = s++;
X	while (isalpha(*s) || isdigit(*s) || *s == '_')
X	    s++;
X	while (s < bufend && isspace(*s))
X	    s++;
X	if (*s == ',') {
X	    *s = '\0';
X	    someword = instr(
X	      "tell eof times getlogin wait length shift umask getppid \
X	      cos exp int log rand sin sqrt ord wantarray",
X	      someword);
X	    *s = ',';
X	    if (someword)
X		return;
X	    fatal("No comma allowed after %s", what);
X	}
X    }
X}
X
Xchar *
Xscanident(s,send,dest)
Xregister char *s;
Xregister char *send;
Xchar *dest;
X{
X    register char *d;
X    int brackets = 0;
X
X    reparse = Nullch;
X    s++;
X    d = dest;
X    if (isdigit(*s)) {
X	while (isdigit(*s))
X	    *d++ = *s++;
X    }
X    else {
X	while (isalpha(*s) || isdigit(*s) || *s == '_' || *s == '\'')
X	    *d++ = *s++;
X    }
X    while (d > dest+1 && d[-1] == '\'')
X	d--,s--;
X    *d = '\0';
X    d = dest;
X    if (!*d) {
X	*d = *s++;
X	if (*d == '{' /* } */ ) {
X	    d = dest;
X	    brackets++;
X	    while (s < send && brackets) {
X		if (!reparse && (d == dest || (*s && isascii(*s) &&
X		  (isalpha(*s) || isdigit(*s) || *s == '_') ))) {
X		    *d++ = *s++;
X		    continue;
X		}
X		else if (!reparse)
X		    reparse = s;
X		switch (*s++) {
X		/* { */
X		case '}':
X		    brackets--;
X		    if (reparse && reparse == s - 1)
X			reparse = Nullch;
X		    break;
X		case '{':   /* } */
X		    brackets++;
X		    break;
X		}
X	    }
X	    *d = '\0';
X	    d = dest;
X	}
X	else
X	    d[1] = '\0';
X    }
X    if (*d == '^' && (isupper(*s) || index("[\\]^_?",*s)))
X	*d = *s++ ^ 64;
X    return s;
X}
X
XSTR *
Xscanconst(string,len)
Xchar *string;
Xint len;
X{
X    register STR *retstr;
X    register char *t;
X    register char *d;
X    register char *e;
X
X    if (index(string,'|')) {
X	return Nullstr;
X    }
X    retstr = Str_new(86,len);
X    str_nset(retstr,string,len);
X    t = str_get(retstr);
X    e = t + len;
X    retstr->str_u.str_useful = 100;
X    for (d=t; d < e; ) {
X	switch (*d) {
X	case '{':
X	    if (isdigit(d[1]))
X		e = d;
X	    else
X		goto defchar;
X	    break;
X	case '.': case '[': case '$': case '(': case ')': case '|': case '+':
X	    e = d;
X	    break;
X	case '\\':
X	    if (d[1] && index("wWbB0123456789sSdDlLuUE",d[1])) {
X		e = d;
X		break;
X	    }
X	    (void)bcopy(d+1,d,e-d);
X	    e--;
X	    switch(*d) {
X	    case 'n':
X		*d = '\n';
X		break;
X	    case 't':
X		*d = '\t';
X		break;
X	    case 'f':
X		*d = '\f';
X		break;
X	    case 'r':
X		*d = '\r';
X		break;
X	    case 'e':
X		*d = '\033';
X		break;
X	    case 'a':
X		*d = '\007';
X		break;
X	    }
X	    /* FALL THROUGH */
X	default:
X	  defchar:
X	    if (d[1] == '*' || (d[1] == '{' && d[2] == '0') || d[1] == '?') {
X		e = d;
X		break;
X	    }
X	    d++;
X	}
X    }
X    if (d == t) {
X	str_free(retstr);
X	return Nullstr;
X    }
X    *d = '\0';
X    retstr->str_cur = d - t;
X    return retstr;
X}
X
Xchar *
Xscanpat(s)
Xregister char *s;
X{
X    register SPAT *spat;
X    register char *d;
X    register char *e;
X    int len;
X    SPAT savespat;
X    STR *str = Str_new(93,0);
X
X    Newz(801,spat,1,SPAT);
X    spat->spat_next = curstash->tbl_spatroot;	/* link into spat list */
X    curstash->tbl_spatroot = spat;
X
X    switch (*s++) {
X    case 'm':
X	s++;
X	break;
X    case '/':
X	break;
X    case '?':
X	spat->spat_flags |= SPAT_ONCE;
X	break;
X    default:
X	fatal("panic: scanpat");
X    }
X    s = str_append_till(str,s,bufend,s[-1],patleave);
X    if (s >= bufend) {
X	str_free(str);
X	yyerror("Search pattern not terminated");
X	yylval.arg = Nullarg;
X	return s;
X    }
X    s++;
X    while (*s == 'i' || *s == 'o') {
X	if (*s == 'i') {
X	    s++;
X	    sawi = TRUE;
X	    spat->spat_flags |= SPAT_FOLD;
X	}
X	if (*s == 'o') {
X	    s++;
X	    spat->spat_flags |= SPAT_KEEP;
X	}
X    }
X    len = str->str_cur;
X    e = str->str_ptr + len;
X    for (d = str->str_ptr; d < e; d++) {
X	if (*d == '\\')
X	    d++;
X	else if ((*d == '$' && d[1] && d[1] != '|' && d[1] != ')') ||
X		 (*d == '@')) {
X	    register ARG *arg;
X
X	    spat->spat_runtime = arg = op_new(1);
X	    arg->arg_type = O_ITEM;
X	    arg[1].arg_type = A_DOUBLE;
X	    arg[1].arg_ptr.arg_str = str_smake(str);
X	    d = scanident(d,bufend,buf);
X	    (void)stabent(buf,TRUE);		/* make sure it's created */
X	    for (; d < e; d++) {
X		if (*d == '\\')
X		    d++;
X		else if (*d == '$' && d[1] && d[1] != '|' && d[1] != ')') {
X		    d = scanident(d,bufend,buf);
X		    (void)stabent(buf,TRUE);
X		}
X		else if (*d == '@') {
X		    d = scanident(d,bufend,buf);
X		    if (strEQ(buf,"ARGV") || strEQ(buf,"ENV") ||
X		      strEQ(buf,"SIG") || strEQ(buf,"INC"))
X			(void)stabent(buf,TRUE);
X		}
X	    }
X	    goto got_pat;		/* skip compiling for now */
X	}
X    }
X    if (spat->spat_flags & SPAT_FOLD)
X#ifdef STRUCTCOPY
X	savespat = *spat;
X#else
X	(void)bcopy((char *)spat, (char *)&savespat, sizeof(SPAT));
X#endif
X    if (*str->str_ptr == '^') {
X	spat->spat_short = scanconst(str->str_ptr+1,len-1);
X	if (spat->spat_short) {
X	    spat->spat_slen = spat->spat_short->str_cur;
X	    if (spat->spat_slen == len - 1)
X		spat->spat_flags |= SPAT_ALL;
X	}
X    }
X    else {
X	spat->spat_flags |= SPAT_SCANFIRST;
X	spat->spat_short = scanconst(str->str_ptr,len);
X	if (spat->spat_short) {
X	    spat->spat_slen = spat->spat_short->str_cur;
X	    if (spat->spat_slen == len)
X		spat->spat_flags |= SPAT_ALL;
X	}
X    }	
X    if ((spat->spat_flags & SPAT_ALL) && (spat->spat_flags & SPAT_SCANFIRST)) {
X	fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
X	spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
X	    spat->spat_flags & SPAT_FOLD);
X		/* Note that this regexp can still be used if someone says
X		 * something like /a/ && s//b/;  so we can't delete it.
X		 */
X    }
X    else {
X	if (spat->spat_flags & SPAT_FOLD)
X#ifdef STRUCTCOPY
X	    *spat = savespat;
X#else
X	    (void)bcopy((char *)&savespat, (char *)spat, sizeof(SPAT));
X#endif
X	if (spat->spat_short)
X	    fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
X	spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
X	    spat->spat_flags & SPAT_FOLD);
X	hoistmust(spat);
X    }
X  got_pat:
X    str_free(str);
X    yylval.arg = make_match(O_MATCH,stab2arg(A_STAB,defstab),spat);
X    return s;
X}
X
Xchar *
Xscansubst(s)
Xregister char *s;
X{
X    register SPAT *spat;
X    register char *d;
X    register char *e;
X    int len;
X    STR *str = Str_new(93,0);
X
X    Newz(802,spat,1,SPAT);
X    spat->spat_next = curstash->tbl_spatroot;	/* link into spat list */
X    curstash->tbl_spatroot = spat;
X
X    s = str_append_till(str,s+1,bufend,*s,patleave);
X    if (s >= bufend) {
X	str_free(str);
X	yyerror("Substitution pattern not terminated");
X	yylval.arg = Nullarg;
X	return s;
X    }
X    len = str->str_cur;
X    e = str->str_ptr + len;
X    for (d = str->str_ptr; d < e; d++) {
X	if (*d == '\\')
X	    d++;
X	else if ((*d == '$' && d[1] && d[1] != '|' && /*(*/ d[1] != ')') ||
X	    *d == '@' ) {
X	    register ARG *arg;
X
X	    spat->spat_runtime = arg = op_new(1);
X	    arg->arg_type = O_ITEM;
X	    arg[1].arg_type = A_DOUBLE;
X	    arg[1].arg_ptr.arg_str = str_smake(str);
X	    d = scanident(d,bufend,buf);
X	    (void)stabent(buf,TRUE);		/* make sure it's created */
X	    for (; *d; d++) {
X		if (*d == '$' && d[1] && d[-1] != '\\' && d[1] != '|') {
X		    d = scanident(d,bufend,buf);
X		    (void)stabent(buf,TRUE);
X		}
X		else if (*d == '@' && d[-1] != '\\') {
X		    d = scanident(d,bufend,buf);
X		    if (strEQ(buf,"ARGV") || strEQ(buf,"ENV") ||
X		      strEQ(buf,"SIG") || strEQ(buf,"INC"))
X			(void)stabent(buf,TRUE);
X		}
X	    }
X	    goto get_repl;		/* skip compiling for now */
X	}
X    }
X    if (*str->str_ptr == '^') {
X	spat->spat_short = scanconst(str->str_ptr+1,len-1);
X	if (spat->spat_short)
X	    spat->spat_slen = spat->spat_short->str_cur;
X    }
X    else {
X	spat->spat_flags |= SPAT_SCANFIRST;
X	spat->spat_short = scanconst(str->str_ptr,len);
X	if (spat->spat_short)
X	    spat->spat_slen = spat->spat_short->str_cur;
X    }
Xget_repl:
X    s = scanstr(s);
X    if (s >= bufend) {
X	str_free(str);
X	yyerror("Substitution replacement not terminated");
X	yylval.arg = Nullarg;
X	return s;
X    }
X    spat->spat_repl = yylval.arg;
X    spat->spat_flags |= SPAT_ONCE;
X    if ((spat->spat_repl[1].arg_type & A_MASK) == A_SINGLE)
X	spat->spat_flags |= SPAT_CONST;
X    else if ((spat->spat_repl[1].arg_type & A_MASK) == A_DOUBLE) {
X	STR *tmpstr;
X	register char *t;
X
X	spat->spat_flags |= SPAT_CONST;
X	tmpstr = spat->spat_repl[1].arg_ptr.arg_str;
X	e = tmpstr->str_ptr + tmpstr->str_cur;
X	for (t = tmpstr->str_ptr; t < e; t++) {
X	    if (*t == '$' && t[1] && (index("`'&+0123456789",t[1]) ||
X	      (t[1] == '{' /*}*/ && isdigit(t[2])) ))
X		spat->spat_flags &= ~SPAT_CONST;
X	}
X    }
X    while (*s == 'g' || *s == 'i' || *s == 'e' || *s == 'o') {
X	if (*s == 'e') {
X	    s++;
X	    if ((spat->spat_repl[1].arg_type & A_MASK) == A_DOUBLE)
X		spat->spat_repl[1].arg_type = A_SINGLE;
X	    spat->spat_repl = make_op(O_EVAL,2,
X		spat->spat_repl,
X		Nullarg,
X		Nullarg);
X	    spat->spat_flags &= ~SPAT_CONST;
X	}
X	if (*s == 'g') {
X	    s++;
X	    spat->spat_flags &= ~SPAT_ONCE;
X	}
X	if (*s == 'i') {
X	    s++;
X	    sawi = TRUE;
X	    spat->spat_flags |= SPAT_FOLD;
X	    if (!(spat->spat_flags & SPAT_SCANFIRST)) {
X		str_free(spat->spat_short);	/* anchored opt doesn't do */
X		spat->spat_short = Nullstr;	/* case insensitive match */
X		spat->spat_slen = 0;
X	    }
X	}
X	if (*s == 'o') {
X	    s++;
X	    spat->spat_flags |= SPAT_KEEP;
X	}
X    }
X    if (spat->spat_short && (spat->spat_flags & SPAT_SCANFIRST))
X	fbmcompile(spat->spat_short, spat->spat_flags & SPAT_FOLD);
X    if (!spat->spat_runtime) {
X	spat->spat_regexp = regcomp(str->str_ptr,str->str_ptr+len,
X	  spat->spat_flags & SPAT_FOLD);
X	hoistmust(spat);
X    }
X    yylval.arg = make_match(O_SUBST,stab2arg(A_STAB,defstab),spat);
X    str_free(str);
X    return s;
X}
X
Xhoistmust(spat)
Xregister SPAT *spat;
X{
X    if (spat->spat_regexp->regmust) {	/* is there a better short-circuit? */
X	if (spat->spat_short &&
X	  str_eq(spat->spat_short,spat->spat_regexp->regmust))
X	{
X	    if (spat->spat_flags & SPAT_SCANFIRST) {
X		str_free(spat->spat_short);
X		spat->spat_short = Nullstr;
X	    }
X	    else {
X		str_free(spat->spat_regexp->regmust);
X		spat->spat_regexp->regmust = Nullstr;
X		return;
X	    }
X	}
X	if (!spat->spat_short ||	/* promote the better string */
X	  ((spat->spat_flags & SPAT_SCANFIRST) &&
X	   (spat->spat_short->str_cur < spat->spat_regexp->regmust->str_cur) )){
X	    str_free(spat->spat_short);		/* ok if null */
X	    spat->spat_short = spat->spat_regexp->regmust;
X	    spat->spat_regexp->regmust = Nullstr;
X	    spat->spat_flags |= SPAT_SCANFIRST;
X	}
X    }
X}
X
Xchar *
Xexpand_charset(s,len,retlen)
Xregister char *s;
Xint len;
Xint *retlen;
X{
X    char t[520];
X    register char *d = t;
X    register int i;
X    register char *send = s + len;
X
X    while (s < send && d - t <= 256) {
X	if (s[1] == '-' && s+2 < send) {
X	    for (i = (s[0] & 0377); i <= (s[2] & 0377); i++)
X		*d++ = i;
X	    s += 3;
X	}
X	else
X	    *d++ = *s++;
X    }
X    *d = '\0';
X    *retlen = d - t;
X    return nsavestr(t,d-t);
X}
X
Xchar *
Xscantrans(s)
Xregister char *s;
X{
X    ARG *arg =
X	l(make_op(O_TRANS,2,stab2arg(A_STAB,defstab),Nullarg,Nullarg));
X    register char *t;
X    register char *r;
X    register short *tbl;
X    register int i;
X    register int j;
X    int tlen, rlen;
X    int squash;
X    int delete;
X    int complement;
X
X    New(803,tbl,256,short);
X    arg[2].arg_type = A_NULL;
X    arg[2].arg_ptr.arg_cval = (char*) tbl;
X    s = scanstr(s);
X    if (s >= bufend) {
X	yyerror("Translation pattern not terminated");
X	yylval.arg = Nullarg;
X	return s;
X    }
X    t = expand_charset(yylval.arg[1].arg_ptr.arg_str->str_ptr,
X	yylval.arg[1].arg_ptr.arg_str->str_cur,&tlen);
X    arg_free(yylval.arg);
X    s = scanstr(s-1);
X    if (s >= bufend) {
X	yyerror("Translation replacement not terminated");
X	yylval.arg = Nullarg;
X	return s;
X    }
X    complement = delete = squash = 0;
X    while (*s == 'c' || *s == 'd' || *s == 's') {
X	if (*s == 'c')
X	    complement = 1;
X	else if (*s == 'd')
X	    delete = 2;
X	else
X	    squash = 1;
X	s++;
X    }
X    r = expand_charset(yylval.arg[1].arg_ptr.arg_str->str_ptr,
X	yylval.arg[1].arg_ptr.arg_str->str_cur,&rlen);
X    arg_free(yylval.arg);
X    arg[2].arg_len = delete|squash;
X    yylval.arg = arg;
X    if (!rlen && !delete) {
X	Safefree(r);
X	r = t; rlen = tlen;
X    }
X    if (complement) {
X	Zero(tbl, 256, short);
X	for (i = 0; i < tlen; i++)
X	    tbl[t[i] & 0377] = -1;
X	for (i = 0, j = 0; i < 256; i++) {
X	    if (!tbl[i]) {
X		if (j >= rlen) {
X		    if (delete)
X			tbl[i] = -2;
X		    else
X			tbl[i] = r[j-1];
X		}
X		else
X		    tbl[i] = r[j++];
X	    }
X	}
X    }
X    else {
X	for (i = 0; i < 256; i++)
X	    tbl[i] = -1;
X	for (i = 0, j = 0; i < tlen; i++,j++) {
X	    if (j >= rlen) {
X		if (delete) {
X		    if (tbl[t[i] & 0377] == -1)
X			tbl[t[i] & 0377] = -2;
X		    continue;
X		}
X		--j;
X	    }
X	    if (tbl[t[i] & 0377] == -1)
X		tbl[t[i] & 0377] = r[j] & 0377;
X	}
X    }
X    if (r != t)
X	Safefree(r);
X    Safefree(t);
X    return s;
X}
X
Xchar *
Xscanstr(s)
Xregister char *s;
X{
X    register char term;
X    register char *d;
X    register ARG *arg;
X    register char *send;
X    register bool makesingle = FALSE;
X    register STAB *stab;
X    bool alwaysdollar = FALSE;
X    bool hereis = FALSE;
X    STR *herewas;
X    STR *str;
X    char *leave = "\\$@nrtfbeacx0123456789[{]}lLuUE"; /* which backslash sequences to keep */
X    int len;
X
X    arg = op_new(1);
X    yylval.arg = arg;
X    arg->arg_type = O_ITEM;
X
X    switch (*s) {
X    default:			/* a substitution replacement */
X	arg[1].arg_type = A_DOUBLE;
X	makesingle = TRUE;	/* maybe disable runtime scanning */
X	term = *s;
X	if (term == '\'')
X	    leave = Nullch;
X	goto snarf_it;
X    case '0':
X	{
X	    unsigned long i;
X	    int shift;
X
X	    arg[1].arg_type = A_SINGLE;
X	    if (s[1] == 'x') {
X		shift = 4;
X		s += 2;
X	    }
X	    else if (s[1] == '.')
X		goto decimal;
X	    else
X		shift = 3;
X	    i = 0;
X	    for (;;) {
X		switch (*s) {
X		default:
X		    goto out;
X		case '8': case '9':
X		    if (shift != 4)
X			yyerror("Illegal octal digit");
X		    /* FALL THROUGH */
X		case '0': case '1': case '2': case '3': case '4':
X		case '5': case '6': case '7':
X		    i <<= shift;
X		    i += *s++ & 15;
X		    break;
X		case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
X		case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
X		    if (shift != 4)
X			goto out;
X		    i <<= 4;
X		    i += (*s++ & 7) + 9;
X		    break;
X		}
X	    }
X	  out:
X	    str = Str_new(92,0);
X	    str_numset(str,(double)i);
X	    if (str->str_ptr) {
X		Safefree(str->str_ptr);
X		str->str_ptr = Nullch;
X		str->str_len = str->str_cur = 0;
X	    }
X	    arg[1].arg_ptr.arg_str = str;
X	}
X	break;
X    case '1': case '2': case '3': case '4': case '5':
X    case '6': case '7': case '8': case '9': case '.':
X      decimal:
X	arg[1].arg_type = A_SINGLE;
X	d = tokenbuf;
X	while (isdigit(*s) || *s == '_') {
X	    if (*s == '_')
X		s++;
X	    else
X		*d++ = *s++;
X	}
X	if (*s == '.' && s[1] && index("0123456789eE ;",s[1])) {
X	    *d++ = *s++;
X	    while (isdigit(*s) || *s == '_') {
X		if (*s == '_')
X		    s++;
X		else
X		    *d++ = *s++;
X	    }
X	}
X	if (*s && index("eE",*s) && index("+-0123456789",s[1])) {
X	    *d++ = *s++;
X	    if (*s == '+' || *s == '-')
X		*d++ = *s++;
X	    while (isdigit(*s))
X		*d++ = *s++;
X	}
X	*d = '\0';
X	str = Str_new(92,0);
X	str_numset(str,atof(tokenbuf));
X	if (str->str_ptr) {
X	    Safefree(str->str_ptr);
X	    str->str_ptr = Nullch;
X	    str->str_len = str->str_cur = 0;
X	}
X	arg[1].arg_ptr.arg_str = str;
X	break;
X    case '<':
X	if (*++s == '<') {
X	    hereis = TRUE;
X	    d = tokenbuf;
X	    if (!rsfp)
X		*d++ = '\n';
X	    if (*++s && index("`'\"",*s)) {
X		term = *s++;
X		s = cpytill(d,s,bufend,term,&len);
X		if (s < bufend)
X		    s++;
X		d += len;
X	    }
X	    else {
X		if (*s == '\\')
X		    s++, term = '\'';
X		else
X		    term = '"';
X		while (isascii(*s) && (isalpha(*s) || isdigit(*s) || *s == '_'))
X		    *d++ = *s++;
X	    }				/* assuming tokenbuf won't clobber */
X	    *d++ = '\n';
X	    *d = '\0';
X	    len = d - tokenbuf;
X	    d = "\n";
X	    if (rsfp || !(d=ninstr(s,bufend,d,d+1)))
X		herewas = str_make(s,bufend-s);
X	    else
X		s--, herewas = str_make(s,d-s);
X	    s += herewas->str_cur;
X	    if (term == '\'')
X		goto do_single;
X	    if (term == '`')
X		goto do_back;
X	    goto do_double;
X	}
X	d = tokenbuf;
X	s = cpytill(d,s,bufend,'>',&len);
X	if (s < bufend)
X	    s++;
X	if (*d == '$') d++;
X	while (*d &&
X	  (isalpha(*d) || isdigit(*d) || *d == '_' || *d == '\''))
X	    d++;
X	if (d - tokenbuf != len) {
X	    d = tokenbuf;
X	    arg[1].arg_type = A_GLOB;
X	    d = nsavestr(d,len);
X	    arg[1].arg_ptr.arg_stab = stab = genstab();
X	    stab_io(stab) = stio_new();
X	    stab_val(stab) = str_make(d,len);
X	    Safefree(d);
X	    set_csh();
X	}
X	else {
X	    d = tokenbuf;
X	    if (!len)
X		(void)strcpy(d,"ARGV");
X	    if (*d == '$') {
X		arg[1].arg_type = A_INDREAD;
X		arg[1].arg_ptr.arg_stab = stabent(d+1,TRUE);
X	    }
X	    else {
X		arg[1].arg_type = A_READ;
X		arg[1].arg_ptr.arg_stab = stabent(d,TRUE);
X		if (!stab_io(arg[1].arg_ptr.arg_stab))
X		    stab_io(arg[1].arg_ptr.arg_stab) = stio_new();
X		if (strEQ(d,"ARGV")) {
X		    (void)aadd(arg[1].arg_ptr.arg_stab);
X		    stab_io(arg[1].arg_ptr.arg_stab)->flags |=
X		      IOF_ARGV|IOF_START;
X		}
X	    }
X	}
X	break;
X
X    case 'q':
X	s++;
X	if (*s == 'q') {
X	    s++;
X	    goto do_double;
X	}
X	if (*s == 'x') {
X	    s++;
X	    goto do_back;
X	}
X	/* FALL THROUGH */
X    case '\'':
X      do_single:
X	term = *s;
X	arg[1].arg_type = A_SINGLE;
X	leave = Nullch;
X	goto snarf_it;
X
X    case '"': 
X      do_double:
X	term = *s;
X	arg[1].arg_type = A_DOUBLE;
X	makesingle = TRUE;	/* maybe disable runtime scanning */
X	alwaysdollar = TRUE;	/* treat $) and $| as variables */
X	goto snarf_it;
X    case '`':
X      do_back:
X	term = *s;
X	arg[1].arg_type = A_BACKTICK;
X	set_csh();
X	alwaysdollar = TRUE;	/* treat $) and $| as variables */
X      snarf_it:
X	{
X	    STR *tmpstr;
X	    char *tmps;
X
X	    multi_start = curcmd->c_line;
X	    if (hereis)
X		multi_open = multi_close = '<';
X	    else {
X		multi_open = term;
X		if (term && (tmps = index("([{< )]}> )]}>",term)))
X		    term = tmps[5];
X		multi_close = term;
X	    }
X	    tmpstr = Str_new(87,80);
X	    if (hereis) {
X		term = *tokenbuf;
X		if (!rsfp) {
X		    d = s;
X		    while (s < bufend &&
X		      (*s != term || bcmp(s,tokenbuf,len) != 0) ) {
X			if (*s++ == '\n')
X			    curcmd->c_line++;
X		    }
X		    if (s >= bufend) {
X			curcmd->c_line = multi_start;
X			fatal("EOF in string");
X		    }
X		    str_nset(tmpstr,d+1,s-d);
X		    s += len - 1;
X		    str_ncat(herewas,s,bufend-s);
X		    str_replace(linestr,herewas);
!STUFFY!FUNK!
echo " "
echo "End of kit 4 (of 36)"
cat /dev/null >kit4isdone
run=''
config=''
for iskit in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36; do
    if test -f kit${iskit}isdone; then
	run="$run $iskit"
    else
	todo="$todo $iskit"
    fi
done
case $todo in
    '')
	echo "You have run all your kits.  Please read README and then type Configure."
	for combo in *:AA; do
	    if test -f "$combo"; then
		realfile=`basename $combo :AA`
		cat $realfile:[A-Z][A-Z] >$realfile
		rm -rf $realfile:[A-Z][A-Z]
	    fi
	done
	rm -rf kit*isdone
	chmod 755 Configure
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.