[comp.sources.unix] v15i093: Perl, release 2, Part04/15

rsalz@uunet.uu.net (Rich Salz) (07/08/88)

Submitted-by: Larry Wall <lwall@jpl-devvax.jpl.nasa.gov>
Posting-number: Volume 15, Issue 93
Archive-name: perl2/part04

#! /bin/sh

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

echo "This is perl 2.0 kit 4 (of 15).  If kit 4 is complete, the line"
echo '"'"End of kit 4 (of 15)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir x2p 2>/dev/null
echo Extracting perly.c
sed >perly.c <<'!STUFFY!FUNK!' -e 's/X//'
Xchar rcsid[] = "$Header: perly.c,v 2.0 88/06/05 00:09:56 root Exp $";
X/*
X * $Log:	perly.c,v $
X * Revision 2.0  88/06/05  00:09:56  root
X * Baseline version 2.0.
X * 
X */
X
X#include "EXTERN.h"
X#include "perl.h"
X#include "perly.h"
X
Xextern char *tokename[];
Xextern int yychar;
X
Xstatic int cmd_tosave();
Xstatic int arg_tosave();
Xstatic int spat_tosave();
X
Xmain(argc,argv,env)
Xregister int argc;
Xregister char **argv;
Xregister char **env;
X{
X    register STR *str;
X    register char *s;
X    char *index(), *strcpy(), *getenv();
X    bool dosearch = FALSE;
X
X    uid = (int)getuid();
X    euid = (int)geteuid();
X    linestr = str_new(80);
X    str_nset(linestr,"",0);
X    str = str_make("");		/* first used for -I flags */
X    incstab = aadd(stabent("INC",TRUE));
X    for (argc--,argv++; argc; argc--,argv++) {
X	if (argv[0][0] != '-' || !argv[0][1])
X	    break;
X      reswitch:
X	switch (argv[0][1]) {
X	case 'a':
X	    minus_a = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X#ifdef DEBUGGING
X	case 'D':
X	    debug = atoi(argv[0]+2);
X#ifdef YYDEBUG
X	    yydebug = (debug & 1);
X#endif
X	    break;
X#endif
X	case 'e':
X	    if (!e_fp) {
X	        e_tmpname = strcpy(safemalloc(sizeof(TMPPATH)),TMPPATH);
X		mktemp(e_tmpname);
X		e_fp = fopen(e_tmpname,"w");
X	    }
X	    if (argv[1])
X		fputs(argv[1],e_fp);
X	    putc('\n', e_fp);
X	    argc--,argv++;
X	    break;
X	case 'i':
X	    inplace = savestr(argv[0]+2);
X	    argvoutstab = stabent("ARGVOUT",TRUE);
X	    break;
X	case 'I':
X	    str_cat(str,argv[0]);
X	    str_cat(str," ");
X	    if (argv[0][2]) {
X		apush(incstab->stab_array,str_make(argv[0]+2));
X	    }
X	    else {
X		apush(incstab->stab_array,str_make(argv[1]));
X		str_cat(str,argv[1]);
X		argc--,argv++;
X		str_cat(str," ");
X	    }
X	    break;
X	case 'n':
X	    minus_n = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 'p':
X	    minus_p = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 'P':
X	    preprocess = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 's':
X	    doswitches = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 'S':
X	    dosearch = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 'U':
X	    unsafe = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case 'v':
X	    version();
X	    exit(0);
X	case 'w':
X	    dowarn = TRUE;
X	    strcpy(argv[0], argv[0]+1);
X	    goto reswitch;
X	case '-':
X	    argc--,argv++;
X	    goto switch_end;
X	case 0:
X	    break;
X	default:
X	    fatal("Unrecognized switch: %s",argv[0]);
X	}
X    }
X  switch_end:
X    if (e_fp) {
X	fclose(e_fp);
X	argc++,argv--;
X	argv[0] = e_tmpname;
X    }
X#ifndef PRIVLIB
X#define PRIVLIB "/usr/local/lib/perl"
X#endif
X    apush(incstab->stab_array,str_make(PRIVLIB));
X
X    str_set(&str_no,No);
X    str_set(&str_yes,Yes);
X    init_eval();
X
X    /* open script */
X
X    if (argv[0] == Nullch)
X	argv[0] = "-";
X    if (dosearch && argv[0][0] != '/' && (s = getenv("PATH"))) {
X	char *xfound = Nullch, *xfailed = Nullch;
X
X	while (*s) {
X	    s = cpytill(tokenbuf,s,':');
X	    if (*s)
X		s++;
X	    if (tokenbuf[0])
X		strcat(tokenbuf,"/");
X	    strcat(tokenbuf,argv[0]);
X#ifdef DEBUGGING
X	    if (debug & 1)
X		fprintf(stderr,"Looking for %s\n",tokenbuf);
X#endif
X	    if (stat(tokenbuf,&statbuf) < 0)		/* not there? */
X		continue;
X	    if ((statbuf.st_mode & S_IFMT) == S_IFREG
X	     && cando(S_IREAD,TRUE) && cando(S_IEXEC,TRUE)) {
X		xfound = tokenbuf;              /* bingo! */
X		break;
X	    }
X	    if (!xfailed)
X		xfailed = savestr(tokenbuf);
X	}
X	if (!xfound)
X	    fatal("Can't execute %s", xfailed);
X	if (xfailed)
X	    safefree(xfailed);
X	argv[0] = savestr(xfound);
X    }
X    filename = savestr(argv[0]);
X    origfilename = savestr(filename);
X    if (strEQ(filename,"-"))
X	argv[0] = "";
X    if (preprocess) {
X	str_cat(str,"-I");
X	str_cat(str,PRIVLIB);
X	sprintf(buf, "\
X/bin/sed -e '/^[^#]/b' \
X -e '/^#[ 	]*include[ 	]/b' \
X -e '/^#[ 	]*define[ 	]/b' \
X -e '/^#[ 	]*if[ 	]/b' \
X -e '/^#[ 	]*ifdef[ 	]/b' \
X -e '/^#[ 	]*ifndef[ 	]/b' \
X -e '/^#[ 	]*else/b' \
X -e '/^#[ 	]*endif/b' \
X -e 's/^#.*//' \
X %s | %s -C %s %s",
X	  argv[0], CPPSTDIN, str_get(str), CPPMINUS);
X	rsfp = popen(buf,"r");
X    }
X    else if (!*argv[0])
X	rsfp = stdin;
X    else
X	rsfp = fopen(argv[0],"r");
X    if (rsfp == Nullfp)
X	fatal("Perl script \"%s\" doesn't seem to exist",filename);
X    str_free(str);		/* free -I directories */
X
X    defstab = stabent("_",TRUE);
X
X    /* init tokener */
X
X    bufptr = str_get(linestr);
X
X    /* now parse the report spec */
X
X    if (yyparse())
X	fatal("Execution aborted due to compilation errors.\n");
X
X    if (dowarn) {
X	stab_check('A','Z');
X	stab_check('a','z');
X    }
X
X    preprocess = FALSE;
X    if (e_fp) {
X	e_fp = Nullfp;
X	UNLINK(e_tmpname);
X    }
X    argc--,argv++;	/* skip name of script */
X    if (doswitches) {
X	for (; argc > 0 && **argv == '-'; argc--,argv++) {
X	    if (argv[0][1] == '-') {
X		argc--,argv++;
X		break;
X	    }
X	    str_numset(stabent(argv[0]+1,TRUE)->stab_val,(double)1.0);
X	}
X    }
X    if (argvstab = stabent("ARGV",allstabs)) {
X	aadd(argvstab);
X	for (; argc > 0; argc--,argv++) {
X	    apush(argvstab->stab_array,str_make(argv[0]));
X	}
X    }
X    if (envstab = stabent("ENV",allstabs)) {
X	hadd(envstab);
X	for (; *env; env++) {
X	    if (!(s = index(*env,'=')))
X		continue;
X	    *s++ = '\0';
X	    str = str_make(s);
X	    str->str_link.str_magic = envstab;
X	    hstore(envstab->stab_hash,*env,str);
X	    *--s = '=';
X	}
X    }
X    if (sigstab = stabent("SIG",allstabs))
X	hadd(sigstab);
X
X    magicalize("!#?^~=-%0123456789.+&*()<>,\\/[|");
X
X    sawampersand = (stabent("&",FALSE) != Nullstab);
X    if (tmpstab = stabent("0",allstabs))
X	str_set(STAB_STR(tmpstab),origfilename);
X    if (tmpstab = stabent("$",allstabs))
X	str_numset(STAB_STR(tmpstab),(double)getpid());
X
X    tmpstab = stabent("stdin",TRUE);
X    tmpstab->stab_io = stio_new();
X    tmpstab->stab_io->fp = stdin;
X
X    tmpstab = stabent("stdout",TRUE);
X    tmpstab->stab_io = stio_new();
X    tmpstab->stab_io->fp = stdout;
X    defoutstab = tmpstab;
X    curoutstab = tmpstab;
X
X    tmpstab = stabent("stderr",TRUE);
X    tmpstab->stab_io = stio_new();
X    tmpstab->stab_io->fp = stderr;
X
X    savestack = anew(Nullstab);		/* for saving non-local values */
X
X    setjmp(top_env);	/* sets goto_targ on longjump */
X
X#ifdef DEBUGGING
X    if (debug & 1024)
X	dump_cmd(main_root,Nullcmd);
X    if (debug)
X	fprintf(stderr,"\nEXECUTING...\n\n");
X#endif
X
X    /* do it */
X
X    (void) cmd_exec(main_root);
X
X    if (goto_targ)
X	fatal("Can't find label \"%s\"--aborting",goto_targ);
X    exit(0);
X    /* NOTREACHED */
X}
X
Xmagicalize(list)
Xregister char *list;
X{
X    register STAB *stab;
X    char sym[2];
X
X    sym[1] = '\0';
X    while (*sym = *list++) {
X	if (stab = stabent(sym,allstabs)) {
X	    stab->stab_flags = SF_VMAGIC;
X	    stab->stab_val->str_link.str_magic = stab;
X	}
X    }
X}
X
XARG *
Xmake_split(stab,arg)
Xregister STAB *stab;
Xregister ARG *arg;
X{
X    register SPAT *spat = (SPAT *) safemalloc(sizeof (SPAT));
X
X    if (arg->arg_type != O_MATCH) {
X	spat = (SPAT *) safemalloc(sizeof (SPAT));
X	bzero((char *)spat, sizeof(SPAT));
X	spat->spat_next = spat_root;	/* link into spat list */
X	spat_root = spat;
X
X	spat->spat_runtime = arg;
X	arg = make_match(O_MATCH,stab2arg(A_STAB,defstab),spat);
X    }
X    arg->arg_type = O_SPLIT;
X    spat = arg[2].arg_ptr.arg_spat;
X    spat->spat_repl = stab2arg(A_STAB,aadd(stab));
X    if (spat->spat_short) {	/* exact match can bypass regexec() */
X	if (!((spat->spat_flags & SPAT_SCANFIRST) &&
X	    (spat->spat_flags & SPAT_ALL) )) {
X	    str_free(spat->spat_short);
X	    spat->spat_short = Nullstr;
X	}
X    }
X    return arg;
X}
X
XSUBR *
Xmake_sub(name,cmd)
Xchar *name;
XCMD *cmd;
X{
X    register SUBR *sub = (SUBR *) safemalloc(sizeof (SUBR));
X    STAB *stab = stabent(name,TRUE);
X
X    if (stab->stab_sub) {
X	if (dowarn) {
X	    line_t oldline = line;
X
X	    if (cmd)
X		line = cmd->c_line;
X	    warn("Subroutine %s redefined",name);
X	    line = oldline;
X	}
X	cmd_free(stab->stab_sub->cmd);
X	afree(stab->stab_sub->tosave);
X	safefree((char*)stab->stab_sub);
X    }
X    bzero((char *)sub, sizeof(SUBR));
X    sub->cmd = cmd;
X    sub->filename = filename;
X    tosave = anew(Nullstab);
X    tosave->ary_fill = 0;	/* make 1 based */
X    cmd_tosave(cmd);		/* this builds the tosave array */
X    sub->tosave = tosave;
X    stab->stab_sub = sub;
X}
X
XCMD *
Xblock_head(tail)
Xregister CMD *tail;
X{
X    if (tail == Nullcmd) {
X	return tail;
X    }
X    return tail->c_head;
X}
X
XCMD *
Xappend_line(head,tail)
Xregister CMD *head;
Xregister CMD *tail;
X{
X    if (tail == Nullcmd)
X	return head;
X    if (!tail->c_head)			/* make sure tail is well formed */
X	tail->c_head = tail;
X    if (head != Nullcmd) {
X	tail = tail->c_head;		/* get to start of tail list */
X	if (!head->c_head)
X	    head->c_head = head;	/* start a new head list */
X	while (head->c_next) {
X	    head->c_next->c_head = head->c_head;
X	    head = head->c_next;	/* get to end of head list */
X	}
X	head->c_next = tail;		/* link to end of old list */
X	tail->c_head = head->c_head;	/* propagate head pointer */
X    }
X    while (tail->c_next) {
X	tail->c_next->c_head = tail->c_head;
X	tail = tail->c_next;
X    }
X    return tail;
X}
X
XCMD *
Xmake_acmd(type,stab,cond,arg)
Xint type;
XSTAB *stab;
XARG *cond;
XARG *arg;
X{
X    register CMD *cmd = (CMD *) safemalloc(sizeof (CMD));
X
X    bzero((char *)cmd, sizeof(CMD));
X    cmd->c_type = type;
X    cmd->ucmd.acmd.ac_stab = stab;
X    cmd->ucmd.acmd.ac_expr = arg;
X    cmd->c_expr = cond;
X    if (cond) {
X	opt_arg(cmd,1,1);
X	cmd->c_flags |= CF_COND;
X    }
X    if (cmdline != NOLINE) {
X	cmd->c_line = cmdline;
X	cmdline = NOLINE;
X    }
X    cmd->c_file = filename;
X    return cmd;
X}
X
XCMD *
Xmake_ccmd(type,arg,cblock)
Xint type;
Xregister ARG *arg;
Xstruct compcmd cblock;
X{
X    register CMD *cmd = (CMD *) safemalloc(sizeof (CMD));
X
X    bzero((char *)cmd, sizeof(CMD));
X    cmd->c_type = type;
X    cmd->c_expr = arg;
X    cmd->ucmd.ccmd.cc_true = cblock.comp_true;
X    cmd->ucmd.ccmd.cc_alt = cblock.comp_alt;
X    if (arg) {
X	opt_arg(cmd,1,0);
X	cmd->c_flags |= CF_COND;
X    }
X    if (cmdline != NOLINE) {
X	cmd->c_line = cmdline;
X	cmdline = NOLINE;
X    }
X    return cmd;
X}
X
Xvoid
Xopt_arg(cmd,fliporflop,acmd)
Xregister CMD *cmd;
Xint fliporflop;
Xint acmd;
X{
X    register ARG *arg;
X    int opt = CFT_EVAL;
X    int sure = 0;
X    ARG *arg2;
X    char *tmps;	/* for True macro */
X    int context = 0;	/* 0 = normal, 1 = before &&, 2 = before || */
X    int flp = fliporflop;
X
X    if (!cmd)
X	return;
X    arg = cmd->c_expr;
X
X    /* Can we turn && and || into if and unless? */
X
X    if (acmd && !cmd->ucmd.acmd.ac_expr && 
X      (arg->arg_type == O_AND || arg->arg_type == O_OR) ) {
X	dehoist(arg,1);
X	dehoist(arg,2);
X	cmd->ucmd.acmd.ac_expr = arg[2].arg_ptr.arg_arg;
X	cmd->c_expr = arg[1].arg_ptr.arg_arg;
X	if (arg->arg_type == O_OR)
X	    cmd->c_flags ^= CF_INVERT;		/* || is like unless */
X	arg->arg_len = 0;
X	arg_free(arg);
X	arg = cmd->c_expr;
X    }
X
X    /* Turn "if (!expr)" into "unless (expr)" */
X
X    while (arg->arg_type == O_NOT) {
X	dehoist(arg,1);
X	cmd->c_flags ^= CF_INVERT;		/* flip sense of cmd */
X	cmd->c_expr = arg[1].arg_ptr.arg_arg;	/* hoist the rest of expr */
X	free_arg(arg);
X	arg = cmd->c_expr;			/* here we go again */
X    }
X
X    if (!arg->arg_len) {		/* sanity check */
X	cmd->c_flags |= opt;
X	return;
X    }
X
X    /* for "cond .. cond" we set up for the initial check */
X
X    if (arg->arg_type == O_FLIP)
X	context |= 4;
X
X    /* for "cond && expr" and "cond || expr" we can ignore expr, sort of */
X
X    if (arg->arg_type == O_AND)
X	context |= 1;
X    else if (arg->arg_type == O_OR)
X	context |= 2;
X    if (context && arg[flp].arg_type == A_EXPR) {
X	arg = arg[flp].arg_ptr.arg_arg;
X	flp = 1;
X    }
X
X    if (arg[flp].arg_flags & (AF_PRE|AF_POST)) {
X	cmd->c_flags |= opt;
X	return;				/* side effect, can't optimize */
X    }
X
X    if (arg->arg_type == O_ITEM || arg->arg_type == O_FLIP ||
X      arg->arg_type == O_AND || arg->arg_type == O_OR) {
X	if (arg[flp].arg_type == A_SINGLE) {
X	    opt = (str_true(arg[flp].arg_ptr.arg_str) ? CFT_TRUE : CFT_FALSE);
X	    cmd->c_short = arg[flp].arg_ptr.arg_str;
X	    goto literal;
X	}
X	else if (arg[flp].arg_type == A_STAB || arg[flp].arg_type == A_LVAL) {
X	    cmd->c_stab  = arg[flp].arg_ptr.arg_stab;
X	    opt = CFT_REG;
X	  literal:
X	    if (!context) {	/* no && or ||? */
X		free_arg(arg);
X		cmd->c_expr = Nullarg;
X	    }
X	    if (!(context & 1))
X		cmd->c_flags |= CF_EQSURE;
X	    if (!(context & 2))
X		cmd->c_flags |= CF_NESURE;
X	}
X    }
X    else if (arg->arg_type == O_MATCH || arg->arg_type == O_SUBST ||
X	     arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST) {
X	if ((arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
X		arg[2].arg_type == A_SPAT &&
X		arg[2].arg_ptr.arg_spat->spat_short ) {
X	    cmd->c_stab  = arg[1].arg_ptr.arg_stab;
X	    cmd->c_short = arg[2].arg_ptr.arg_spat->spat_short;
X	    cmd->c_slen  = arg[2].arg_ptr.arg_spat->spat_slen;
X	    if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ALL &&
X		!(arg[2].arg_ptr.arg_spat->spat_flags & SPAT_ONCE) &&
X		(arg->arg_type == O_MATCH || arg->arg_type == O_NMATCH) )
X		sure |= CF_EQSURE;		/* (SUBST must be forced even */
X						/* if we know it will work.) */
X	    arg[2].arg_ptr.arg_spat->spat_short = Nullstr;
X	    arg[2].arg_ptr.arg_spat->spat_slen = 0; /* only one chk */
X	    sure |= CF_NESURE;		/* normally only sure if it fails */
X	    if (arg->arg_type == O_NMATCH || arg->arg_type == O_NSUBST)
X		cmd->c_flags |= CF_FIRSTNEG;
X	    if (context & 1) {		/* only sure if thing is false */
X		if (cmd->c_flags & CF_FIRSTNEG)
X		    sure &= ~CF_NESURE;
X		else
X		    sure &= ~CF_EQSURE;
X	    }
X	    else if (context & 2) {	/* only sure if thing is true */
X		if (cmd->c_flags & CF_FIRSTNEG)
X		    sure &= ~CF_EQSURE;
X		else
X		    sure &= ~CF_NESURE;
X	    }
X	    if (sure & (CF_EQSURE|CF_NESURE)) {	/* if we know anything*/
X		if (arg[2].arg_ptr.arg_spat->spat_flags & SPAT_SCANFIRST)
X		    opt = CFT_SCAN;
X		else
X		    opt = CFT_ANCHOR;
X		if (sure == (CF_EQSURE|CF_NESURE)	/* really sure? */
X		    && arg->arg_type == O_MATCH
X		    && context & 4
X		    && fliporflop == 1) {
X		    spat_free(arg[2].arg_ptr.arg_spat);
X		    arg[2].arg_ptr.arg_spat = Nullspat;	/* don't do twice */
X		}
X		cmd->c_flags |= sure;
X	    }
X	}
X    }
X    else if (arg->arg_type == O_SEQ || arg->arg_type == O_SNE ||
X	     arg->arg_type == O_SLT || arg->arg_type == O_SGT) {
X	if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
X	    if (arg[2].arg_type == A_SINGLE) {
X		cmd->c_stab  = arg[1].arg_ptr.arg_stab;
X		cmd->c_short = arg[2].arg_ptr.arg_str;
X		cmd->c_slen  = 30000;
X		switch (arg->arg_type) {
X		case O_SLT: case O_SGT:
X		    sure |= CF_EQSURE;
X		    cmd->c_flags |= CF_FIRSTNEG;
X		    break;
X		case O_SNE:
X		    cmd->c_flags |= CF_FIRSTNEG;
X		    /* FALL THROUGH */
X		case O_SEQ:
X		    sure |= CF_NESURE|CF_EQSURE;
X		    break;
X		}
X		if (context & 1) {	/* only sure if thing is false */
X		    if (cmd->c_flags & CF_FIRSTNEG)
X			sure &= ~CF_NESURE;
X		    else
X			sure &= ~CF_EQSURE;
X		}
X		else if (context & 2) { /* only sure if thing is true */
X		    if (cmd->c_flags & CF_FIRSTNEG)
X			sure &= ~CF_EQSURE;
X		    else
X			sure &= ~CF_NESURE;
X		}
X		if (sure & (CF_EQSURE|CF_NESURE)) {
X		    opt = CFT_STROP;
X		    cmd->c_flags |= sure;
X		}
X	    }
X	}
X    }
X    else if (arg->arg_type == O_EQ || arg->arg_type == O_NE ||
X	     arg->arg_type == O_LE || arg->arg_type == O_GE ||
X	     arg->arg_type == O_LT || arg->arg_type == O_GT) {
X	if (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) {
X	    if (arg[2].arg_type == A_SINGLE) {
X		cmd->c_stab  = arg[1].arg_ptr.arg_stab;
X		cmd->c_short = str_nmake(str_gnum(arg[2].arg_ptr.arg_str));
X		cmd->c_slen = arg->arg_type;
X		sure |= CF_NESURE|CF_EQSURE;
X		if (context & 1) {	/* only sure if thing is false */
X		    sure &= ~CF_EQSURE;
X		}
X		else if (context & 2) { /* only sure if thing is true */
X		    sure &= ~CF_NESURE;
X		}
X		if (sure & (CF_EQSURE|CF_NESURE)) {
X		    opt = CFT_NUMOP;
X		    cmd->c_flags |= sure;
X		}
X	    }
X	}
X    }
X    else if (arg->arg_type == O_ASSIGN &&
X	     (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) &&
X	     arg[1].arg_ptr.arg_stab == defstab &&
X	     arg[2].arg_type == A_EXPR ) {
X	arg2 = arg[2].arg_ptr.arg_arg;
X	if (arg2->arg_type == O_ITEM && arg2[1].arg_type == A_READ) {
X	    opt = CFT_GETS;
X	    cmd->c_stab = arg2[1].arg_ptr.arg_stab;
X	    if (!(arg2[1].arg_ptr.arg_stab->stab_io->flags & IOF_ARGV)) {
X		free_arg(arg2);
X		free_arg(arg);
X		cmd->c_expr = Nullarg;
X	    }
X	}
X    }
X    else if (arg->arg_type == O_CHOP &&
X	     (arg[1].arg_type == A_STAB || arg[1].arg_type == A_LVAL) ) {
X	opt = CFT_CHOP;
X	cmd->c_stab = arg[1].arg_ptr.arg_stab;
X	free_arg(arg);
X	cmd->c_expr = Nullarg;
X    }
X    if (context & 4)
X	opt |= CF_FLIP;
X    cmd->c_flags |= opt;
X
X    if (cmd->c_flags & CF_FLIP) {
X	if (fliporflop == 1) {
X	    arg = cmd->c_expr;	/* get back to O_FLIP arg */
X	    arg[3].arg_ptr.arg_cmd = (CMD*)safemalloc(sizeof(CMD));
X	    bcopy((char *)cmd, (char *)arg[3].arg_ptr.arg_cmd, sizeof(CMD));
X	    arg[4].arg_ptr.arg_cmd = (CMD*)safemalloc(sizeof(CMD));
X	    bcopy((char *)cmd, (char *)arg[4].arg_ptr.arg_cmd, sizeof(CMD));
X	    opt_arg(arg[4].arg_ptr.arg_cmd,2,acmd);
X	    arg->arg_len = 2;		/* this is a lie */
X	}
X	else {
X	    if ((opt & CF_OPTIMIZE) == CFT_EVAL)
X		cmd->c_flags = (cmd->c_flags & ~CF_OPTIMIZE) | CFT_UNFLIP;
X	}
X    }
X}
X
XARG *
Xmod_match(type,left,pat)
Xregister ARG *left;
Xregister ARG *pat;
X{
X
X    register SPAT *spat;
X    register ARG *newarg;
X
X    if ((pat->arg_type == O_MATCH ||
X	 pat->arg_type == O_SUBST ||
X	 pat->arg_type == O_TRANS ||
X	 pat->arg_type == O_SPLIT
X	) &&
X	pat[1].arg_ptr.arg_stab == defstab ) {
X	switch (pat->arg_type) {
X	case O_MATCH:
X	    newarg = make_op(type == O_MATCH ? O_MATCH : O_NMATCH,
X		pat->arg_len,
X		left,Nullarg,Nullarg,0);
X	    break;
X	case O_SUBST:
X	    newarg = l(make_op(type == O_MATCH ? O_SUBST : O_NSUBST,
X		pat->arg_len,
X		left,Nullarg,Nullarg,0));
X	    break;
X	case O_TRANS:
X	    newarg = l(make_op(type == O_MATCH ? O_TRANS : O_NTRANS,
X		pat->arg_len,
X		left,Nullarg,Nullarg,0));
X	    break;
X	case O_SPLIT:
X	    newarg = make_op(type == O_MATCH ? O_SPLIT : O_SPLIT,
X		pat->arg_len,
X		left,Nullarg,Nullarg,0);
X	    break;
X	}
X	if (pat->arg_len >= 2) {
X	    newarg[2].arg_type = pat[2].arg_type;
X	    newarg[2].arg_ptr = pat[2].arg_ptr;
X	    newarg[2].arg_flags = pat[2].arg_flags;
X	    if (pat->arg_len >= 3) {
X		newarg[3].arg_type = pat[3].arg_type;
X		newarg[3].arg_ptr = pat[3].arg_ptr;
X		newarg[3].arg_flags = pat[3].arg_flags;
X	    }
X	}
X	safefree((char*)pat);
X    }
X    else {
X	spat = (SPAT *) safemalloc(sizeof (SPAT));
X	bzero((char *)spat, sizeof(SPAT));
X	spat->spat_next = spat_root;	/* link into spat list */
X	spat_root = spat;
X
X	spat->spat_runtime = pat;
X	newarg = make_op(type,2,left,Nullarg,Nullarg,0);
X	newarg[2].arg_type = A_SPAT;
X	newarg[2].arg_ptr.arg_spat = spat;
X	newarg[2].arg_flags = AF_SPECIAL;
X    }
X
X    return newarg;
X}
X
XCMD *
Xadd_label(lbl,cmd)
Xchar *lbl;
Xregister CMD *cmd;
X{
X    if (cmd)
X	cmd->c_label = lbl;
X    return cmd;
X}
X
XCMD *
Xaddcond(cmd, arg)
Xregister CMD *cmd;
Xregister ARG *arg;
X{
X    cmd->c_expr = arg;
X    opt_arg(cmd,1,0);
X    cmd->c_flags |= CF_COND;
X    return cmd;
X}
X
XCMD *
Xaddloop(cmd, arg)
Xregister CMD *cmd;
Xregister ARG *arg;
X{
X    cmd->c_expr = arg;
X    opt_arg(cmd,1,0);
X    cmd->c_flags |= CF_COND|CF_LOOP;
X    if (cmd->c_type == C_BLOCK)
X	cmd->c_flags &= ~CF_COND;
X    else {
X	arg = cmd->ucmd.acmd.ac_expr;
X	if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_CMD)
X	    cmd->c_flags &= ~CF_COND;  /* "do {} while" happens at least once */
X	if (arg && arg->arg_type == O_SUBR)
X	    cmd->c_flags &= ~CF_COND;  /* likewise for "do subr() while" */
X    }
X    return cmd;
X}
X
XCMD *
Xinvert(cmd)
Xregister CMD *cmd;
X{
X    cmd->c_flags ^= CF_INVERT;
X    return cmd;
X}
X
Xyyerror(s)
Xchar *s;
X{
X    char tmpbuf[128];
X    char *tname = tmpbuf;
X
X    if (yychar > 256) {
X	tname = tokename[yychar-256];
X	if (strEQ(tname,"word"))
X	    strcpy(tname,tokenbuf);
X	else if (strEQ(tname,"register"))
X	    sprintf(tname,"$%s",tokenbuf);
X	else if (strEQ(tname,"array_length"))
X	    sprintf(tname,"$#%s",tokenbuf);
X    }
X    else if (!yychar)
X	strcpy(tname,"EOF");
X    else if (yychar < 32)
X	sprintf(tname,"^%c",yychar+64);
X    else if (yychar == 127)
X	strcpy(tname,"^?");
X    else
X	sprintf(tname,"%c",yychar);
X    sprintf(tokenbuf, "%s in file %s at line %d, next token \"%s\"\n",
X      s,filename,line,tname);
X    if (in_eval)
X	str_set(stabent("@",TRUE)->stab_val,tokenbuf);
X    else
X	fputs(tokenbuf,stderr);
X}
X
XARG *
Xmake_op(type,newlen,arg1,arg2,arg3,dolist)
Xint type;
Xint newlen;
XARG *arg1;
XARG *arg2;
XARG *arg3;
Xint dolist;
X{
X    register ARG *arg;
X    register ARG *chld;
X    register int doarg;
X
X    arg = op_new(newlen);
X    arg->arg_type = type;
X    doarg = opargs[type];
X    if (chld = arg1) {
X	if (!(doarg & 1))
X	    arg[1].arg_flags |= AF_SPECIAL;
X	if (doarg & 16)
X	    arg[1].arg_flags |= AF_NUMERIC;
X	if (chld->arg_type == O_ITEM &&
X	    (hoistable[chld[1].arg_type] || chld[1].arg_type == A_LVAL) ) {
X	    arg[1].arg_type = chld[1].arg_type;
X	    arg[1].arg_ptr = chld[1].arg_ptr;
X	    arg[1].arg_flags |= chld[1].arg_flags;
X	    free_arg(chld);
X	}
X	else {
X	    arg[1].arg_type = A_EXPR;
X	    arg[1].arg_ptr.arg_arg = chld;
X	    if (dolist & 1) {
X		if (chld->arg_type == O_LIST) {
X		    if (newlen == 1) {	/* we can hoist entire list */
X			chld->arg_type = type;
X			free_arg(arg);
X			arg = chld;
X		    }
X		    else {
X			arg[1].arg_flags |= AF_SPECIAL;
X		    }
X		}
X		else {
X		    switch (chld->arg_type) {
X		    case O_ARRAY:
X			if (chld->arg_len == 1)
X			    arg[1].arg_flags |= AF_SPECIAL;
X			break;
X		    case O_ITEM:
X			if (chld[1].arg_type == A_READ ||
X			    chld[1].arg_type == A_INDREAD ||
X			    chld[1].arg_type == A_GLOB)
X			    arg[1].arg_flags |= AF_SPECIAL;
X			break;
X		    case O_SPLIT:
X		    case O_TMS:
X		    case O_EACH:
X		    case O_VALUES:
X		    case O_KEYS:
X		    case O_SORT:
X			arg[1].arg_flags |= AF_SPECIAL;
X			break;
X		    }
X		}
X	    }
X	}
X    }
X    if (chld = arg2) {
X	if (!(doarg & 2))
X	    arg[2].arg_flags |= AF_SPECIAL;
X	if (doarg & 32)
X	    arg[2].arg_flags |= AF_NUMERIC;
X	if (chld->arg_type == O_ITEM && 
X	    (hoistable[chld[1].arg_type] || 
X	     (type == O_ASSIGN && 
X	      ((chld[1].arg_type == A_READ && !(arg[1].arg_flags & AF_SPECIAL))
X		||
X	       (chld[1].arg_type == A_INDREAD && !(arg[1].arg_flags & AF_SPECIAL))
X		||
X	       (chld[1].arg_type == A_GLOB && !(arg[1].arg_flags & AF_SPECIAL))
X		||
X	       chld[1].arg_type == A_BACKTICK ) ) ) ) {
X	    arg[2].arg_type = chld[1].arg_type;
X	    arg[2].arg_ptr = chld[1].arg_ptr;
X	    free_arg(chld);
X	}
X	else {
X	    arg[2].arg_type = A_EXPR;
X	    arg[2].arg_ptr.arg_arg = chld;
X	    if ((dolist & 2) &&
X	      (chld->arg_type == O_LIST ||
X	       (chld->arg_type == O_ARRAY && chld->arg_len == 1) ))
X		arg[2].arg_flags |= AF_SPECIAL;
X	}
X    }
X    if (chld = arg3) {
X	if (!(doarg & 4))
X	    arg[3].arg_flags |= AF_SPECIAL;
X	if (doarg & 64)
X	    arg[3].arg_flags |= AF_NUMERIC;
X	if (chld->arg_type == O_ITEM && hoistable[chld[1].arg_type]) {
X	    arg[3].arg_type = chld[1].arg_type;
X	    arg[3].arg_ptr = chld[1].arg_ptr;
X	    free_arg(chld);
X	}
X	else {
X	    arg[3].arg_type = A_EXPR;
X	    arg[3].arg_ptr.arg_arg = chld;
X	    if ((dolist & 4) &&
X	      (chld->arg_type == O_LIST ||
X	       (chld->arg_type == O_ARRAY && chld->arg_len == 1) ))
X		arg[3].arg_flags |= AF_SPECIAL;
X	}
X    }
X#ifdef DEBUGGING
X    if (debug & 16) {
X	fprintf(stderr,"%lx <= make_op(%s",arg,opname[arg->arg_type]);
X	if (arg1)
X	    fprintf(stderr,",%s=%lx",
X		argname[arg[1].arg_type],arg[1].arg_ptr.arg_arg);
X	if (arg2)
X	    fprintf(stderr,",%s=%lx",
X		argname[arg[2].arg_type],arg[2].arg_ptr.arg_arg);
X	if (arg3)
X	    fprintf(stderr,",%s=%lx",
X		argname[arg[3].arg_type],arg[3].arg_ptr.arg_arg);
X	fprintf(stderr,")\n");
X    }
X#endif
X    evalstatic(arg);		/* see if we can consolidate anything */
X    return arg;
X}
X
X/* turn 123 into 123 == $. */
X
XARG *
Xflipflip(arg)
Xregister ARG *arg;
X{
X    if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_SINGLE) {
X	arg = (ARG*)saferealloc((char*)arg,3*sizeof(ARG));
X	arg->arg_type = O_EQ;
X	arg->arg_len = 2;
X	arg[2].arg_type = A_STAB;
X	arg[2].arg_flags = 0;
X	arg[2].arg_ptr.arg_stab = stabent(".",TRUE);
X    }
X    return arg;
X}
X
Xvoid
Xevalstatic(arg)
Xregister ARG *arg;
X{
X    register STR *str;
X    register STR *s1;
X    register STR *s2;
X    double value;		/* must not be register */
X    register char *tmps;
X    int i;
X    unsigned long tmplong;
X    double exp(), log(), sqrt(), modf();
X    char *crypt();
X
X    if (!arg || !arg->arg_len)
X	return;
X
X    if (arg[1].arg_type == A_SINGLE &&
X        (arg->arg_len == 1 || arg[2].arg_type == A_SINGLE) ) {
X	str = str_new(0);
X	s1 = arg[1].arg_ptr.arg_str;
X	if (arg->arg_len > 1)
X	    s2 = arg[2].arg_ptr.arg_str;
X	else
X	    s2 = Nullstr;
X	switch (arg->arg_type) {
X	default:
X	    str_free(str);
X	    str = Nullstr;		/* can't be evaluated yet */
X	    break;
X	case O_CONCAT:
X	    str_sset(str,s1);
X	    str_scat(str,s2);
X	    break;
X	case O_REPEAT:
X	    i = (int)str_gnum(s2);
X	    while (i-- > 0)
X		str_scat(str,s1);
X	    break;
X	case O_MULTIPLY:
X	    value = str_gnum(s1);
X	    str_numset(str,value * str_gnum(s2));
X	    break;
X	case O_DIVIDE:
X	    value = str_gnum(s2);
X	    if (value == 0.0)
X		fatal("Illegal division by constant zero");
X	    str_numset(str,str_gnum(s1) / value);
X	    break;
X	case O_MODULO:
X	    tmplong = (unsigned long)str_gnum(s2);
X	    if (tmplong == 0L)
X		fatal("Illegal modulus of constant zero");
X	    str_numset(str,(double)(((unsigned long)str_gnum(s1)) % tmplong));
X	    break;
X	case O_ADD:
X	    value = str_gnum(s1);
X	    str_numset(str,value + str_gnum(s2));
X	    break;
X	case O_SUBTRACT:
X	    value = str_gnum(s1);
X	    str_numset(str,value - str_gnum(s2));
X	    break;
X	case O_LEFT_SHIFT:
X	    value = str_gnum(s1);
X	    i = (int)str_gnum(s2);
X	    str_numset(str,(double)(((unsigned long)value) << i));
X	    break;
X	case O_RIGHT_SHIFT:
X	    value = str_gnum(s1);
X	    i = (int)str_gnum(s2);
X	    str_numset(str,(double)(((unsigned long)value) >> i));
X	    break;
X	case O_LT:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value < str_gnum(s2)));
X	    break;
X	case O_GT:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value > str_gnum(s2)));
X	    break;
X	case O_LE:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value <= str_gnum(s2)));
X	    break;
X	case O_GE:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value >= str_gnum(s2)));
X	    break;
X	case O_EQ:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value == str_gnum(s2)));
X	    break;
X	case O_NE:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(value != str_gnum(s2)));
X	    break;
X	case O_BIT_AND:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(((unsigned long)value) &
X		((unsigned long)str_gnum(s2))));
X	    break;
X	case O_XOR:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(((unsigned long)value) ^
X		((unsigned long)str_gnum(s2))));
X	    break;
X	case O_BIT_OR:
X	    value = str_gnum(s1);
X	    str_numset(str,(double)(((unsigned long)value) |
X		((unsigned long)str_gnum(s2))));
X	    break;
X	case O_AND:
X	    if (str_true(s1))
X		str = str_make(str_get(s2));
X	    else
X		str = str_make(str_get(s1));
X	    break;
X	case O_OR:
X	    if (str_true(s1))
X		str = str_make(str_get(s1));
X	    else
X		str = str_make(str_get(s2));
X	    break;
X	case O_COND_EXPR:
X	    if (arg[3].arg_type != A_SINGLE) {
X		str_free(str);
X		str = Nullstr;
X	    }
X	    else {
X		str = str_make(str_get(str_true(s1) ? s2 : arg[3].arg_ptr.arg_str));
X		str_free(arg[3].arg_ptr.arg_str);
X	    }
X	    break;
X	case O_NEGATE:
X	    str_numset(str,(double)(-str_gnum(s1)));
X	    break;
X	case O_NOT:
X	    str_numset(str,(double)(!str_true(s1)));
X	    break;
X	case O_COMPLEMENT:
X	    str_numset(str,(double)(~(long)str_gnum(s1)));
X	    break;
X	case O_LENGTH:
X	    str_numset(str, (double)str_len(s1));
X	    break;
X	case O_SUBSTR:
X	    if (arg[3].arg_type != A_SINGLE || stabent("[",allstabs)) {
X		str_free(str);		/* making the fallacious assumption */
X		str = Nullstr;		/* that any $[ occurs before substr()*/
X	    }
X	    else {
X		char *beg;
X		int len = (int)str_gnum(s2);
X		int tmp;
X
X		for (beg = str_get(s1); *beg && len > 0; beg++,len--) ;
X		len = (int)str_gnum(arg[3].arg_ptr.arg_str);
X		str_free(arg[3].arg_ptr.arg_str);
X		if (len > (tmp = strlen(beg)))
X		    len = tmp;
X		str_nset(str,beg,len);
X	    }
X	    break;
X	case O_SLT:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strLT(tmps,str_get(s2))));
X	    break;
X	case O_SGT:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strGT(tmps,str_get(s2))));
X	    break;
X	case O_SLE:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strLE(tmps,str_get(s2))));
X	    break;
X	case O_SGE:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strGE(tmps,str_get(s2))));
X	    break;
X	case O_SEQ:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strEQ(tmps,str_get(s2))));
X	    break;
X	case O_SNE:
X	    tmps = str_get(s1);
X	    str_numset(str,(double)(strNE(tmps,str_get(s2))));
X	    break;
X	case O_CRYPT:
X#ifdef CRYPT
X	    tmps = str_get(s1);
X	    str_set(str,crypt(tmps,str_get(s2)));
X#else
X	    fatal(
X	    "The crypt() function is unimplemented due to excessive paranoia.");
X#endif
X	    break;
X	case O_EXP:
X	    str_numset(str,exp(str_gnum(s1)));
X	    break;
X	case O_LOG:
X	    str_numset(str,log(str_gnum(s1)));
X	    break;
X	case O_SQRT:
X	    str_numset(str,sqrt(str_gnum(s1)));
X	    break;
X	case O_INT:
X	    value = str_gnum(s1);
X	    if (value >= 0.0)
X		modf(value,&value);
X	    else {
X		modf(-value,&value);
X		value = -value;
X	    }
X	    str_numset(str,value);
X	    break;
X	case O_ORD:
X	    str_numset(str,(double)(*str_get(s1)));
X	    break;
X	}
X	if (str) {
X	    arg->arg_type = O_ITEM;	/* note arg1 type is already SINGLE */
X	    str_free(s1);
X	    str_free(s2);
X	    arg[1].arg_ptr.arg_str = str;
X	}
X    }
X}
X
XARG *
Xl(arg)
Xregister ARG *arg;
X{
X    register int i;
X    register ARG *arg1;
X    ARG *tmparg;
X
X    arg->arg_flags |= AF_COMMON;	/* XXX should cross-match */
X					/* this does unnecessary copying */
X
X    if (arg[1].arg_type == A_ARYLEN) {
X	arg[1].arg_type = A_LARYLEN;
X	return arg;
X    }
X
X    /* see if it's an array reference */
X
X    if (arg[1].arg_type == A_EXPR) {
X	arg1 = arg[1].arg_ptr.arg_arg;
X
X	if (arg1->arg_type == O_LIST && arg->arg_type != O_ITEM) {
X						/* assign to list */
X	    arg[1].arg_flags |= AF_SPECIAL;
X	    dehoist(arg,2);
X	    arg[2].arg_flags |= AF_SPECIAL;
X	    for (i = arg1->arg_len; i >= 1; i--) {
X		switch (arg1[i].arg_type) {
X		case A_STAB: case A_LVAL:
X		    arg1[i].arg_type = A_LVAL;
X		    break;
X		case A_EXPR: case A_LEXPR:
X		    arg1[i].arg_type = A_LEXPR;
X		    if (arg1[i].arg_ptr.arg_arg->arg_type == O_ARRAY)
X			arg1[i].arg_ptr.arg_arg->arg_type = O_LARRAY;
X		    else if (arg1[i].arg_ptr.arg_arg->arg_type == O_HASH)
X			arg1[i].arg_ptr.arg_arg->arg_type = O_LHASH;
X		    if (arg1[i].arg_ptr.arg_arg->arg_type == O_LARRAY)
X			break;
X		    if (arg1[i].arg_ptr.arg_arg->arg_type == O_LHASH)
X			break;
X		    /* FALL THROUGH */
X		default:
X		    sprintf(tokenbuf,
X		      "Illegal item (%s) as lvalue",argname[arg1[i].arg_type]);
X		    yyerror(tokenbuf);
X		}
X	    }
X	}
X	else if (arg1->arg_type == O_ARRAY) {
X	    if (arg1->arg_len == 1 && arg->arg_type != O_ITEM) {
X						/* assign to array */
X		arg[1].arg_flags |= AF_SPECIAL;
X		dehoist(arg,2);
X		arg[2].arg_flags |= AF_SPECIAL;
X	    }
X	    else
X		arg1->arg_type = O_LARRAY;	/* assign to array elem */
X	}
X	else if (arg1->arg_type == O_HASH)
X	    arg1->arg_type = O_LHASH;
X	else if (arg1->arg_type != O_ASSIGN) {
X	    sprintf(tokenbuf,
X	      "Illegal expression (%s) as lvalue",opname[arg1->arg_type]);
X	    yyerror(tokenbuf);
X	}
X	arg[1].arg_type = A_LEXPR;
X#ifdef DEBUGGING
X	if (debug & 16)
X	    fprintf(stderr,"lval LEXPR\n");
X#endif
X	return arg;
X    }
X
X    /* not an array reference, should be a register name */
X
X    if (arg[1].arg_type != A_STAB && arg[1].arg_type != A_LVAL) {
X	sprintf(tokenbuf,
X	  "Illegal item (%s) as lvalue",argname[arg[1].arg_type]);
X	yyerror(tokenbuf);
X    }
X    arg[1].arg_type = A_LVAL;
X#ifdef DEBUGGING
X    if (debug & 16)
X	fprintf(stderr,"lval LVAL\n");
X#endif
X    return arg;
X}
X
Xdehoist(arg,i)
XARG *arg;
X{
X    ARG *tmparg;
X
X    if (arg[i].arg_type != A_EXPR) {	/* dehoist */
X	tmparg = make_op(O_ITEM,1,Nullarg,Nullarg,Nullarg,0);
X	tmparg[1] = arg[i];
X	arg[i].arg_ptr.arg_arg = tmparg;
X	arg[i].arg_type = A_EXPR;
X    }
X}
X
XARG *
Xaddflags(i,flags,arg)
Xregister ARG *arg;
X{
X    arg[i].arg_flags |= flags;
X    return arg;
X}
X
XARG *
Xhide_ary(arg)
XARG *arg;
X{
X    if (arg->arg_type == O_ARRAY)
X	return make_op(O_ITEM,1,arg,Nullarg,Nullarg,0);
X    return arg;
X}
X
XARG *
Xmake_list(arg)
Xregister ARG *arg;
X{
X    register int i;
X    register ARG *node;
X    register ARG *nxtnode;
X    register int j;
X    STR *tmpstr;
X
X    if (!arg) {
X	arg = op_new(0);
X	arg->arg_type = O_LIST;
X    }
X    if (arg->arg_type != O_COMMA) {
X	arg->arg_flags |= AF_LISTISH;	/* see listish() below */
X	return arg;
X    }
X    for (i = 2, node = arg; ; i++) {
X	if (node->arg_len < 2)
X	    break;
X        if (node[2].arg_type != A_EXPR)
X	    break;
X	node = node[2].arg_ptr.arg_arg;
X	if (node->arg_type != O_COMMA)
X	    break;
X    }
X    if (i > 2) {
X	node = arg;
X	arg = op_new(i);
X	tmpstr = arg->arg_ptr.arg_str;
X	*arg = *node;		/* copy everything except the STR */
X	arg->arg_ptr.arg_str = tmpstr;
X	for (j = 1; ; ) {
X	    arg[j] = node[1];
X	    ++j;		/* Bug in Xenix compiler */
X	    if (j >= i) {
X		arg[j] = node[2];
X		free_arg(node);
X		break;
X	    }
X	    nxtnode = node[2].arg_ptr.arg_arg;
X	    free_arg(node);
X	    node = nxtnode;
X	}
X    }
X    arg->arg_type = O_LIST;
X    arg->arg_len = i;
X    return arg;
X}
X
X/* turn a single item into a list */
X
XARG *
Xlistish(arg)
XARG *arg;
X{
X    if (arg->arg_flags & AF_LISTISH) {
X	arg = make_op(O_LIST,1,arg,Nullarg,Nullarg,0);
X	arg[1].arg_flags &= ~AF_SPECIAL;
X    }
X    return arg;
X}
X
X/* mark list of local variables */
X
XARG *
Xlocalize(arg)
XARG *arg;
X{
X    arg->arg_flags |= AF_LOCAL;
X    return arg;
X}
X
XARG *
Xstab2arg(atype,stab)
Xint atype;
Xregister STAB *stab;
X{
X    register ARG *arg;
X
X    arg = op_new(1);
X    arg->arg_type = O_ITEM;
X    arg[1].arg_type = atype;
X    arg[1].arg_ptr.arg_stab = stab;
X    return arg;
X}
X
XARG *
Xcval_to_arg(cval)
Xregister char *cval;
X{
X    register ARG *arg;
X
X    arg = op_new(1);
X    arg->arg_type = O_ITEM;
X    arg[1].arg_type = A_SINGLE;
X    arg[1].arg_ptr.arg_str = str_make(cval);
X    safefree(cval);
X    return arg;
X}
X
XARG *
Xop_new(numargs)
Xint numargs;
X{
X    register ARG *arg;
X
X    arg = (ARG*)safemalloc((numargs + 1) * sizeof (ARG));
X    bzero((char *)arg, (numargs + 1) * sizeof (ARG));
X    arg->arg_ptr.arg_str = str_new(0);
X    arg->arg_len = numargs;
X    return arg;
X}
X
Xvoid
Xfree_arg(arg)
XARG *arg;
X{
X    str_free(arg->arg_ptr.arg_str);
X    safefree((char*)arg);
X}
X
XARG *
Xmake_match(type,expr,spat)
Xint type;
XARG *expr;
XSPAT *spat;
X{
X    register ARG *arg;
X
X    arg = make_op(type,2,expr,Nullarg,Nullarg,0);
X
X    arg[2].arg_type = A_SPAT;
X    arg[2].arg_ptr.arg_spat = spat;
X#ifdef DEBUGGING
X    if (debug & 16)
X	fprintf(stderr,"make_match SPAT=%lx\n",(long)spat);
X#endif
X
X    if (type == O_SUBST || type == O_NSUBST) {
X	if (arg[1].arg_type != A_STAB)
X	    yyerror("Illegal lvalue");
X	arg[1].arg_type = A_LVAL;
X    }
X    return arg;
X}
X
XARG *
Xcmd_to_arg(cmd)
XCMD *cmd;
X{
X    register ARG *arg;
X
X    arg = op_new(1);
X    arg->arg_type = O_ITEM;
X    arg[1].arg_type = A_CMD;
X    arg[1].arg_ptr.arg_cmd = cmd;
X    return arg;
X}
X
XCMD *
Xwopt(cmd)
Xregister CMD *cmd;
X{
X    register CMD *tail;
X    register ARG *arg = cmd->c_expr;
X    STAB *asgnstab;
X
X    /* hoist "while (<channel>)" up into command block */
X
X    if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_READ) {
X	cmd->c_flags &= ~CF_OPTIMIZE;	/* clear optimization type */
X	cmd->c_flags |= CFT_GETS;	/* and set it to do the input */
X	cmd->c_stab = arg[1].arg_ptr.arg_stab;
X	if (arg[1].arg_ptr.arg_stab->stab_io->flags & IOF_ARGV) {
X	    cmd->c_expr = l(make_op(O_ASSIGN, 2,	/* fake up "$_ =" */
X	       stab2arg(A_LVAL,defstab), arg, Nullarg,1 ));
X	}
X	else {
X	    free_arg(arg);
X	    cmd->c_expr = Nullarg;
X	}
X    }
X    else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_INDREAD) {
X	cmd->c_flags &= ~CF_OPTIMIZE;	/* clear optimization type */
X	cmd->c_flags |= CFT_INDGETS;	/* and set it to do the input */
X	cmd->c_stab = arg[1].arg_ptr.arg_stab;
X	free_arg(arg);
X	cmd->c_expr = Nullarg;
X    }
X    else if (arg && arg->arg_type == O_ITEM && arg[1].arg_type == A_GLOB) {
X	if ((cmd->c_flags & CF_OPTIMIZE) == CFT_ARRAY)
X	    asgnstab = cmd->c_stab;
X	else
X	    asgnstab = defstab;
X	cmd->c_expr = l(make_op(O_ASSIGN, 2,	/* fake up "$foo =" */
X	   stab2arg(A_LVAL,asgnstab), arg, Nullarg,1 ));
X	cmd->c_flags &= ~CF_OPTIMIZE;	/* clear optimization type */
X    }
X
X    /* First find the end of the true list */
X
X    if (cmd->ucmd.ccmd.cc_true == Nullcmd)
X	return cmd;
X    for (tail = cmd->ucmd.ccmd.cc_true; tail->c_next; tail = tail->c_next) ;
X
X    /* if there's a continue block, link it to true block and find end */
X
X    if (cmd->ucmd.ccmd.cc_alt != Nullcmd) {
X	tail->c_next = cmd->ucmd.ccmd.cc_alt;
X	for ( ; tail->c_next; tail = tail->c_next) ;
X    }
X
X    /* Here's the real trick: link the end of the list back to the beginning,
X     * inserting a "last" block to break out of the loop.  This saves one or
X     * two procedure calls every time through the loop, because of how cmd_exec
X     * does tail recursion.
X     */
X
X    tail->c_next = (CMD *) safemalloc(sizeof (CMD));
X    tail = tail->c_next;
X    if (!cmd->ucmd.ccmd.cc_alt)
X	cmd->ucmd.ccmd.cc_alt = tail;	/* every loop has a continue now */
X
X    bcopy((char *)cmd, (char *)tail, sizeof(CMD));
X    tail->c_type = C_EXPR;
X    tail->c_flags ^= CF_INVERT;		/* turn into "last unless" */
X    tail->c_next = tail->ucmd.ccmd.cc_true;	/* loop directly back to top */
X    tail->ucmd.acmd.ac_expr = make_op(O_LAST,0,Nullarg,Nullarg,Nullarg,0);
X    tail->ucmd.acmd.ac_stab = Nullstab;
X    return cmd;
X}
X
XCMD *
Xover(eachstab,cmd)
XSTAB *eachstab;
Xregister CMD *cmd;
X{
X    /* hoist "for $foo (@bar)" up into command block */
X
X    cmd->c_flags &= ~CF_OPTIMIZE;	/* clear optimization type */
X    cmd->c_flags |= CFT_ARRAY;		/* and set it to do the iteration */
X    cmd->c_stab = eachstab;
X
X    return cmd;
X}
X
Xstatic int gensym = 0;
X
XSTAB *
Xgenstab()
X{
X    sprintf(tokenbuf,"_GEN_%d",gensym++);
X    return stabent(tokenbuf,TRUE);
X}
X
X/* this routine is in perly.c by virtue of being sort of an alternate main() */
X
XSTR *
Xdo_eval(str,optype)
XSTR *str;
Xint optype;
X{
X    int retval;
X    CMD *myroot;
X    ARRAY *ar;
X    int i;
X    char *oldfile = filename;
X    line_t oldline = line;
X    int oldtmps_base = tmps_base;
X    int oldsave = savestack->ary_fill;
X
X    tmps_base = tmps_max;
X    str_set(stabent("@",TRUE)->stab_val,"");
X    if (optype != O_DOFILE) {	/* normal eval */
X	filename = "(eval)";
X	line = 1;
X	str_sset(linestr,str);
X    }
X    else {
X	filename = savestr(str_get(str));	/* can't free this easily */
X	str_set(linestr,"");
X	rsfp = fopen(filename,"r");
X	ar = incstab->stab_array;
X	if (!rsfp && *filename != '/') {
X	    for (i = 0; i <= ar->ary_fill; i++) {
X		sprintf(tokenbuf,"%s/%s",str_get(afetch(ar,i)),filename);
X		rsfp = fopen(tokenbuf,"r");
X		if (rsfp) {
X		    free(filename);
X		    filename = savestr(tokenbuf);
X		    break;
X		}
X	    }
X	}
X	if (!rsfp) {
X	    filename = oldfile;
X	    tmps_base = oldtmps_base;
X	    return &str_no;
X	}
X	line = 0;
X    }
X    in_eval++;
X    bufptr = str_get(linestr);
X    if (setjmp(eval_env))
X	retval = 1;
X    else
X	retval = yyparse();
X    myroot = eval_root;		/* in case cmd_exec does another eval! */
X    if (retval)
X	str = &str_no;
X    else {
X	str = str_static(cmd_exec(eval_root));
X				/* if we don't save str, free zaps it */
X	cmd_free(myroot);	/* can't free on error, for some reason */
X    }
X    in_eval--;
X    filename = oldfile;
X    line = oldline;
X    tmps_base = oldtmps_base;
X    if (savestack->ary_fill > oldsave)	/* let them use local() */
X	restorelist(oldsave);
X    return str;
X}
X
Xcmd_free(cmd)
Xregister CMD *cmd;
X{
X    register CMD *tofree;
X    register CMD *head = cmd;
X
X    while (cmd) {
X	if (cmd->c_type != C_WHILE) {	/* WHILE block is duplicated */
X	    if (cmd->c_label)
X		safefree(cmd->c_label);
X	    if (cmd->c_short)
X		str_free(cmd->c_short);
X	    if (cmd->c_spat)
X		spat_free(cmd->c_spat);
X	    if (cmd->c_expr)
X		arg_free(cmd->c_expr);
X	}
X	switch (cmd->c_type) {
X	case C_WHILE:
X	case C_BLOCK:
X	case C_IF:
X	    if (cmd->ucmd.ccmd.cc_true)
X		cmd_free(cmd->ucmd.ccmd.cc_true);
X	    if (cmd->c_type == C_IF && cmd->ucmd.ccmd.cc_alt)
X		cmd_free(cmd->ucmd.ccmd.cc_alt);
X	    break;
X	case C_EXPR:
X	    if (cmd->ucmd.acmd.ac_expr)
X		arg_free(cmd->ucmd.acmd.ac_expr);
X	    break;
X	}
X	tofree = cmd;
X	cmd = cmd->c_next;
X	safefree((char*)tofree);
X	if (cmd && cmd == head)		/* reached end of while loop */
X	    break;
X    }
X}
X
Xarg_free(arg)
Xregister ARG *arg;
X{
X    register int i;
X
X    for (i = 1; i <= arg->arg_len; i++) {
X	switch (arg[i].arg_type) {
X	case A_NULL:
X	    break;
X	case A_LEXPR:
X	case A_EXPR:
X	    arg_free(arg[i].arg_ptr.arg_arg);
X	    break;
X	case A_CMD:
X	    cmd_free(arg[i].arg_ptr.arg_cmd);
X	    break;
X	case A_WORD:
X	case A_STAB:
X	case A_LVAL:
X	case A_READ:
X	case A_GLOB:
X	case A_ARYLEN:
X	    break;
X	case A_SINGLE:
X	case A_DOUBLE:
X	case A_BACKTICK:
X	    str_free(arg[i].arg_ptr.arg_str);
X	    break;
X	case A_SPAT:
X	    spat_free(arg[i].arg_ptr.arg_spat);
X	    break;
X	case A_NUMBER:
X	    break;
X	}
X    }
X    free_arg(arg);
X}
X
Xspat_free(spat)
Xregister SPAT *spat;
X{
X    register SPAT *sp;
X
X    if (spat->spat_runtime)
X	arg_free(spat->spat_runtime);
X    if (spat->spat_repl) {
X	arg_free(spat->spat_repl);
X    }
X    if (spat->spat_short) {
X	str_free(spat->spat_short);
X    }
X    if (spat->spat_regexp) {
X	regfree(spat->spat_regexp);
X    }
X
X    /* now unlink from spat list */
X    if (spat_root == spat)
X	spat_root = spat->spat_next;
X    else {
X	for (sp = spat_root; sp->spat_next != spat; sp = sp->spat_next) ;
X	sp->spat_next = spat->spat_next;
X    }
X
X    safefree((char*)spat);
X}
X
X/* Recursively descend a command sequence and push the address of any string
X * that needs saving on recursion onto the tosave array.
X */
X
Xstatic int
Xcmd_tosave(cmd)
Xregister CMD *cmd;
X{
X    register CMD *head = cmd;
X
X    while (cmd) {
X	if (cmd->c_spat)
X	    spat_tosave(cmd->c_spat);
X	if (cmd->c_expr)
X	    arg_tosave(cmd->c_expr);
X	switch (cmd->c_type) {
X	case C_WHILE:
X	case C_BLOCK:
X	case C_IF:
X	    if (cmd->ucmd.ccmd.cc_true)
X		cmd_tosave(cmd->ucmd.ccmd.cc_true);
X	    if (cmd->c_type == C_IF && cmd->ucmd.ccmd.cc_alt)
X		cmd_tosave(cmd->ucmd.ccmd.cc_alt);
X	    break;
X	case C_EXPR:
X	    if (cmd->ucmd.acmd.ac_expr)
X		arg_tosave(cmd->ucmd.acmd.ac_expr);
X	    break;
X	}
X	cmd = cmd->c_next;
X	if (cmd && cmd == head)		/* reached end of while loop */
X	    break;
X    }
X}
X
Xstatic int
Xarg_tosave(arg)
Xregister ARG *arg;
X{
X    register int i;
X    int saving = FALSE;
X
X    for (i = 1; i <= arg->arg_len; i++) {
X	switch (arg[i].arg_type) {
X	case A_NULL:
X	    break;
X	case A_LEXPR:
X	case A_EXPR:
X	    saving |= arg_tosave(arg[i].arg_ptr.arg_arg);
X	    break;
X	case A_CMD:
X	    cmd_tosave(arg[i].arg_ptr.arg_cmd);
X	    saving = TRUE;	/* assume hanky panky */
X	    break;
X	case A_WORD:
X	case A_STAB:
X	case A_LVAL:
X	case A_READ:
X	case A_GLOB:
X	case A_ARYLEN:
X	case A_SINGLE:
X	case A_DOUBLE:
X	case A_BACKTICK:
X	    break;
X	case A_SPAT:
X	    saving |= spat_tosave(arg[i].arg_ptr.arg_spat);
X	    break;
X	case A_NUMBER:
X	    break;
X	}
X    }
X    switch (arg->arg_type) {
X    case O_EVAL:
X    case O_SUBR:
X	saving = TRUE;
X    }
X    if (saving)
X	apush(tosave,arg->arg_ptr.arg_str);
X    return saving;
X}
X
Xstatic int
Xspat_tosave(spat)
Xregister SPAT *spat;
X{
X    int saving = FALSE;
X
X    if (spat->spat_runtime)
X	saving |= arg_tosave(spat->spat_runtime);
X    if (spat->spat_repl) {
X	saving |= arg_tosave(spat->spat_repl);
X    }
X
X    return saving;
X}
!STUFFY!FUNK!
echo Extracting x2p/Makefile.SH
sed >x2p/Makefile.SH <<'!STUFFY!FUNK!' -e 's/X//'
Xcase $CONFIG in
X'')
X    if test ! -f config.sh; then
X	ln ../config.sh . || \
X	ln ../../config.sh . || \
X	ln ../../../config.sh . || \
X	(echo "Can't find config.sh."; exit 1)
X    fi
X    . ./config.sh
X    ;;
Xesac
Xcase "$0" in
X*/*) cd `expr X$0 : 'X\(.*\)/'` ;;
Xesac
Xcase "$mallocsrc" in
X'') ;;
X*) mallocsrc="../$mallocsrc";;
Xesac
Xecho "Extracting x2p/Makefile (with variable substitutions)"
Xcat >Makefile <<!GROK!THIS!
X# $Header: Makefile.SH,v 2.0 88/06/05 00:15:31 root Exp $
X#
X# $Log:	Makefile.SH,v $
X# Revision 2.0  88/06/05  00:15:31  root
X# Baseline version 2.0.
X# 
X# 
X
XCC = $cc
Xbin = $bin
Xlib = $lib
Xmansrc = $mansrc
Xmanext = $manext
XCFLAGS = $ccflags -O
XLDFLAGS = $ldflags
XSMALL = $small
XLARGE = $large $split
Xmallocsrc = $mallocsrc
Xmallocobj = $mallocobj
X
Xlibs = $libnm -lm
X!GROK!THIS!
X
Xcat >>Makefile <<'!NO!SUBS!'
X
Xpublic = a2p s2p
X
Xprivate = 
X
Xmanpages = a2p.man s2p.man
X
Xutil =
X
Xsh = Makefile.SH makedepend.SH
X
Xh = EXTERN.h INTERN.h config.h handy.h hash.h a2p.h str.h util.h
X
Xc = hash.c $(mallocsrc) str.c util.c walk.c
X
Xobj = hash.o $(mallocobj) str.o util.o walk.o
X
Xlintflags = -phbvxac
X
Xaddedbyconf = Makefile.old bsd eunice filexp loc pdp11 usg v7
X
X# grrr
XSHELL = /bin/sh
X
X.c.o:
X	$(CC) -c $(CFLAGS) $(LARGE) $*.c
X
Xall: $(public) $(private) $(util)
X	touch all
X
Xa2p: $(obj) a2p.o
X	$(CC) $(LDFLAGS) $(LARGE) $(obj) a2p.o $(libs) -o a2p
X
Xa2p.c: a2p.y
X	@ echo Expect 103 shift/reduce errors...
X	yacc a2p.y
X	mv y.tab.c a2p.c
X
Xa2p.o: a2p.c a2py.c a2p.h EXTERN.h util.h INTERN.h handy.h ../config.h
X	$(CC) -c $(CFLAGS) $(LARGE) a2p.c
X
X# if a .h file depends on another .h file...
X$(h):
X	touch $@
Xinstall: a2p s2p
X# won't work with csh
X	export PATH || exit 1
X	- mv $(bin)/a2p $(bin)/a2p.old 2>/dev/null
X	- mv $(bin)/s2p $(bin)/s2p.old
X	- if test `pwd` != $(bin); then cp $(public) $(bin); fi
X	cd $(bin); \
Xfor pub in $(public); do \
Xchmod +x `basename $$pub`; \
Xdone
X#	chmod +x makedir
X#	- ./makedir `filexp $(lib)`
X#	- \
X#if test `pwd` != `filexp $(lib)`; then \
X#cp $(private) `filexp $(lib)`; \
X#fi
X#	cd `filexp $(lib)`; \
X#for priv in $(private); do \
X#chmod +x `basename $$priv`; \
X#done
X	- if test `pwd` != $(mansrc); then \
Xfor page in $(manpages); do \
Xcp $$page $(mansrc)/`basename $$page .man`.$(manext); \
Xdone; \
Xfi
X
Xclean:
X	rm -f *.o
X
Xrealclean:
X	rm -f a2p *.orig */*.orig *.o core $(addedbyconf)
X
X# The following lint has practically everything turned on.  Unfortunately,
X# you have to wade through a lot of mumbo jumbo that can't be suppressed.
X# If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message
X# for that spot.
X
Xlint:
X	lint $(lintflags) $(defs) $(c) > a2p.fuzz
X
Xdepend: ../makedepend
X	../makedepend
X
Xclist:
X	echo $(c) | tr ' ' '\012' >.clist
X
Xhlist:
X	echo $(h) | tr ' ' '\012' >.hlist
X
Xshlist:
X	echo $(sh) | tr ' ' '\012' >.shlist
X
X# AUTOMATICALLY GENERATED MAKE DEPENDENCIES--PUT NOTHING BELOW THIS LINE
X$(obj):
X	@ echo "You haven't done a "'"make depend" yet!'; exit 1
Xmakedepend: makedepend.SH
X	/bin/sh makedepend.SH
X!NO!SUBS!
X$eunicefix Makefile
Xcase `pwd` in
X*SH)
X    $rm -f ../Makefile
X    ln Makefile ../Makefile
X    ;;
Xesac
!STUFFY!FUNK!
echo Extracting Wishlist
sed >Wishlist <<'!STUFFY!FUNK!' -e 's/X//'
Xdate support
Xcase statement
Xioctl() support
Xrandom numbers
!STUFFY!FUNK!
echo ""
echo "End of kit 4 (of 15)"
cat /dev/null >kit4isdone
run=''
config=''
for iskit in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; 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."
	chmod 755 Configure
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.