[comp.os.minix] bawk part 2 of 2

ast@cs.vu.nl (Andy Tanenbaum) (09/14/87)

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
: --------------------------- cut here --------------------------
PATH=/bin:/usr/bin
echo Extracting \b\a\w\k\d\o\.\c
sed 's/^X//' > \b\a\w\k\d\o\.\c << '+ END-OF-FILE '\b\a\w\k\d\o\.\c
X/*
X * Bawk C actions interpreter
X */
X#include <stdio.h>
X#include "bawk.h"
X
Xdopattern( pat )
Xchar *pat;
X{
X	Where = PATTERN;
X	Actptr = pat;
X	getoken();
X	expression();
X	return popint();
X}
X
Xdoaction( act )
Xchar *act;
X{
X	Where = ACTION;
X	Actptr = act;
X	getoken();
X	while ( Token!=T_EOF )
X		statement();
X}
X
Xexpression()
X{
X	expr1();
X
X	if ( Token==T_ASSIGN )
X	{
X		getoken();
X		assignment( expression() );
X	}
X}
X
Xexpr1()
X{
X	int ival;
X
X	expr2();
X	for ( ;; )
X	{
X		if ( Token==T_LIOR )
X		{
X			getoken();
X			ival = popint();
X			expr2();
X			pushint( popint() || ival );
X		}
X		else
X			return;
X	}
X}
X
Xexpr2()
X{
X	int ival;
X
X	expr3();
X	for ( ;; )
X	{
X		if ( Token==T_LAND )
X		{
X			getoken();
X			ival = popint();
X			expr3();
X			pushint( popint() && ival );
X		}
X		else
X			return;
X	}
X}
X
Xexpr3()
X{
X	int ival;
X
X	expr4();
X	for ( ;; )
X	{
X		if ( Token==T_IOR )
X		{
X			getoken();
X			ival = popint();
X			expr4();
X			pushint( popint() | ival );
X		}
X		else
X			return;
X	}
X}
X
X
Xexpr4()
X{
X	int ival;
X
X	expr5();
X	for ( ;; )
X	{
X		if ( Token==T_AND )
X		{
X			getoken();
X			ival = popint();
X			expr5();
X			pushint( popint() & ival );
X		}
X		else
X			return;
X	}
X}
X
Xexpr5()
X{
X	int ival;
X
X	expr6();
X	for ( ;; )
X	{
X		if ( Token==T_XOR )
X		{
X			getoken();
X			ival = popint();
X			expr6();
X			pushint( popint() ^ ival );
X		}
X		else
X			return;
X	}
X}
X
Xexpr6()
X{
X	int ival;
X
X	expr7();
X	for ( ;; )
X	{
X		if ( Token==T_EQ )
X		{
X			getoken();
X			ival = popint();
X			expr7();
X			pushint( ival == popint() );
X		}
X		else if ( Token==T_NE )
X		{
X			getoken();
X			ival = popint();
X			expr7();
X			pushint( ival != popint() );
X		}
X		else
X			return;
X	}
X}
X
Xexpr7()
X{
X	int ival;
X
X	expr8();
X	for ( ;; )
X	{
X		if ( Token==T_LE )
X		{
X			getoken();
X			ival = popint();
X			expr8();
X			pushint( ival <= popint() );
X		}
X		else if ( Token==T_GE )
X		{
X			getoken();
X			ival = popint();
X			expr8();
X			pushint( ival >= popint() );
X		}
X		else if ( Token==T_LT )
X		{
X			getoken();
X			ival = popint();
X			expr8();
X			pushint( ival < popint() );
X		}
X		else if ( Token==T_GT )
X		{
X			getoken();
X			ival = popint();
X			expr8();
X			pushint( ival > popint() );
X		}
X		else
X			return;
X	}
X}
X
Xexpr8()
X{
X	int ival;
X
X	expr9();
X	for ( ;; )
X	{
X		if ( Token==T_SHL )
X		{
X			getoken();
X			ival = popint();
X			expr9();
X			pushint( ival << popint() );
X		}
X		else if ( Token==T_SHR )
X		{
X			getoken();
X			ival = popint();
X			expr9();
X			pushint( ival >> popint() );
X		}
X		else
X			return;
X	}
X}
X
Xexpr9()
X{
X	int ival;
X
X	expr10();
X	for ( ;; )
X	{
X		if ( Token==T_ADD )
X		{
X			getoken();
X			ival = popint();
X			expr10();
X			pushint( ival + popint() );
X		}
X		else if ( Token==T_SUB )
X		{
X			getoken();
X			ival = popint();
X			expr10();
X			pushint( ival - popint() );
X		}
X		else
X			return;
X	}
X}
X
Xexpr10()
X{
X	int ival;
X
X	primary();
X	for ( ;; )
X	{
X		if ( Token==T_MUL )
X		{
X			getoken();
X			ival = popint();
X			primary();
X			pushint( ival * popint() );
X		}
X		else if ( Token==T_DIV )
X		{
X			getoken();
X			ival = popint();
X			primary();
X			pushint( ival / popint() );
X		}
X		else if ( Token==T_MOD )
X		{
X			getoken();
X			ival = popint();
X			primary();
X			pushint( ival % popint() );
X		}
X		else
X			return;
X	}
X}
X
Xprimary()
X{
X	int index;
X	DATUM data;
X	VARIABLE *pvar;
X
X	switch ( Token )
X	{
X	case T_LPAREN:
X		/*
X		 * it's a parenthesized expression
X		 */
X		getoken();
X		expression();
X		if ( Token!=T_RPAREN )
X			error( "missing ')'", ACT_ERROR );
X		getoken();
X		break;
X	case T_LNOT:
X		getoken();
X		primary();
X		pushint( ! popint() );
X		break;
X	case T_NOT:
X		getoken();
X		primary();
X		pushint( ~ popint() );
X		break;
X	case T_ADD:
X		getoken();
X		primary();
X		break;
X	case T_SUB:
X		getoken();
X		primary();
X		pushint( - popint() );
X		break;
X	case T_INCR:
X	case T_DECR:
X		preincdec();
X		break;
X	case T_MUL:
X		getoken();
X		primary();
X		/*
X		 * If item on stack is an LVALUE, do an extra level of
X		 * indirection before changing it to an LVALUE.
X		 */
X		if (Stackptr->lvalue)
X			Stackptr->value.ptrptr =
X					(char **)*Stackptr->value.ptrptr;
X		Stackptr->lvalue = 1;
X		--Stackptr->class;
X		break;
X	case T_AND:
X		getoken();
X		primary();
X		if ( Stackptr->lvalue )
X			Stackptr->lvalue = 0;
X		else
X			error( "'&' operator needs an lvalue", ACT_ERROR );
X		break;
X	case T_CONSTANT:
X		pushint( Value.ival );
X		getoken();
X		break;
X	case T_REGEXP:
X		/*
X		 * It's a regular expression - parse it and compile it.
X		 */
X		if ( Where == PATTERN )
X		{
X			/*
X			 * We're processing a pattern right now - perform a
X			 * match of the regular expression agains input line.
X			 */
X			unparse( Fields, Fieldcount, Linebuf, Fieldsep );
X			pushint( match( Linebuf, Value.dptr ) );
X		}
X		else
X			push( 1, ACTUAL, BYTE, &Value );
X		getoken();
X		break;
X	case T_NF:
X		pushint( Fieldcount );
X		getoken();
X		break;
X	case T_NR:
X		pushint( Recordcount );
X		getoken();
X		break;
X	case T_FS:
X		Fieldsep[1] = 0;
X		data.dptr = Fieldsep;
X		push( 0, LVALUE, BYTE, &data );
X		getoken();
X		break;
X	case T_RS:
X		Recordsep[1] = 0;
X		data.dptr = Recordsep;
X		push( 0, LVALUE, BYTE, &data );
X		getoken();
X		break;
X	case T_FILENAME:
X		data.dptr = Filename;
X		push( 1, ACTUAL, BYTE, &data );
X		getoken();
X		break;
X	case T_DOLLAR:
X		/*
X		 * It's a reference to one (or all) of the words in Linebuf.
X		 */
X		getoken();
X		primary();
X		if ( index = popint() )
X		{
X			if ( index > Fieldcount )
X				index = Fieldcount;
X			else if ( index < 1 )
X				index = 1;
X			data.dptr = Fields[ index-1 ];
X		}
X		else
X		{
X			/*
X			 * Reconstitute the line buffer in case any of the
X			 * fields have been changed.
X			 */
X			unparse( Fields, Fieldcount, Linebuf, Fieldsep );
X			data.dptr = Linebuf;
X		}
X		/*
X		 * $<expr>'s are treated the same as string constants:
X		 */
X		push( 1, ACTUAL, BYTE, &data );
X		break;
X	case T_STRING:
X		push( 1, ACTUAL, BYTE, &Value );
X		getoken();
X		break;
X	case T_FUNCTION:
X		/*
X		 * Do a built-in function call
X		 */
X		index = Value.ival;
X		getoken();
X		function( index );
X		break;
X	case T_VARIABLE:
X/* cast for msc - right cast, really what is wanted? */
X		pvar = (VARIABLE *)Value.dptr;
X		getoken();
X		/*
X		 * it's a plain variable. The way a variable is
X		 * represented on the stack depends on its type:
X		 *      lvalue class value.dptr
X		 * vars:  1      0   address of var
X		 * ptrs:  1      1   ptr to address of ptr
X		 * array: 0      1   address of var
X		 */
X		if ( pvar->vclass && !pvar->vlen )
X			/* it's a pointer */
X/* cast for msc - right cast, really what is wanted? */
X			data.dptr = (char *)&pvar->vptr;
X		else
X			/* an array or simple variable */
X			data.dptr = pvar->vptr;
X		/*
X		 * If it's an array it can't be used as an LVALUE.
X		 */
X		push( pvar->vclass, !pvar->vlen, pvar->vsize, &data );
X		break;
X	case T_EOF:
X		break;
X	default:
X		syntaxerror();
X	}
X	/*
X	 * a "[" means it's an array reference
X	 */
X	if ( Token==T_LBRACKET )
X	{
X		getoken();
X		if ( ! Stackptr->class )
X			error( "'[]' needs an array or pointer", ACT_ERROR );
X		/*
X		 * compute the subscript
X		 */
X		expression();
X		if ( Token!=T_RBRACKET )
X			error( "missing ']'", ACT_ERROR );
X		getoken();
X		index = popint();
X		/*
X		 * compute the offset (subscript times two for int arrays)
X		 * and then the effective address.
X		 */
X		index *= Stackptr->size;
X		if ( Stackptr->lvalue )
X			/*
X			 * It's a pointer - don't forget that the stack top
X			 * item's value is the address of the pointer so we
X			 * must do another level of indirection.
X			 */
X			Stackptr->value.dptr = *Stackptr->value.ptrptr+index;
X		else
X			/*
X			 * It's a plain array - the stack top item's value is
X			 * the address of the first element in the array.
X			 */
X			Stackptr->value.dptr += index;
X
X		/*
X		 * The stack top item now becomes an LVALUE, but we've
X		 * reduced the indirection level.
X		 */
X		Stackptr->lvalue = 1;
X		--Stackptr->class;
X	}
X
X	if ( Token==T_INCR || Token==T_DECR )
X		postincdec();
X}
X
Xpreincdec()
X{
X	/*
X	 * Pre increment/decrement
X	 */
X	int incr;
X
X	incr = Token==T_INCR ? 1 : -1;
X	getoken();
X	primary();
X	if ( Stackptr->lvalue )
X	{
X		if ( Stackptr->class )
X			incr *= Stackptr->size;
X		*Stackptr->value.ptrptr += incr;
X	}
X	else
X		error( "pre '++' or '--' needs an lvalue", ACT_ERROR );
X}
X
Xpostincdec()
X{
X	/*
X	 * Post increment/decrement
X	 */
X	char **pp;
X	int incr;
X
X	incr = Token==T_INCR ? 1 : -1;
X	getoken();
X	if ( Stackptr->lvalue )
X	{
X		if ( Stackptr->class )
X		{
X			/*
X			 * It's a pointer - save its old value then
X			 * increment/decrement the pointer.  This makes the
X			 * item on top of the stack look like an array, which
X			 * means it can no longer be used as an LVALUE. This
X			 * doesn't really hurt, since it doesn't make much
X			 * sense to say:
X			 *   char *cp;
X			 *   cp++ = value;
X			 */
X/* right cast for msc, but is it right for bawk? */
X			pp = (char **) *Stackptr->value.ptrptr;
X
X			*Stackptr->value.ptrptr += incr * Stackptr->size;
X
X			Stackptr->value.ptrptr = pp;
X		}
X		else
X		{
X			/*
X			 * It's a simple variable - save its old value then
X			 * increment/decrement the variable.  This makes the
X			 * item on top of the stack look like a constant,
X			 * which means it can no longer be used as an LVALUE.
X			 * Same reasoning as above.
X			 */
X			if ( Stackptr->size == BYTE )
X/* casts for msc, are they right for bawk? */
X				pp = (char **) *Stackptr->value.dptr;
X
X			else
X/* casts for msc, are they right for bawk? */
X				pp = (char **) *Stackptr->value.ptrptr;
X
X			*Stackptr->value.ptrptr += incr;
X/* casts for msc, are they right for bawk? */
X			Stackptr->value.ival = (int) pp;
X		}
X		Stackptr->lvalue = 0;
X	}
X	else
X		error( "post '++' or '--' needs an lvalue", ACT_ERROR );
X}
X
Xstatement()
X{
X	/*
X	 * Evaluate a statement
X	 */
X	char *repeat, *body;
X
X	switch ( Token )
X	{
X	case T_EOF:
X		break;
X	case T_CHAR:
X	case T_INT:
X		declist();
X		break;
X	case T_LBRACE:
X		/*
X		 * parse a compound statement
X		 */
X		getoken();
X		while ( !Saw_break && Token!=T_RBRACE )
X			statement();
X
X		if ( Token==T_RBRACE )
X			getoken();
X		break;
X	case T_IF:
X		/*
X		 * parse an "if-else" statement
X		 */
X		if ( getoken() != T_LPAREN )
X			syntaxerror();
X		getoken();
X		expression();
X		if ( Token!=T_RPAREN )
X			syntaxerror();
X		getoken();
X		if ( popint() )
X		{
X			statement();
X			if ( Token==T_ELSE )
X			{
X				getoken();
X				skipstatement();
X			}
X		}
X		else
X		{
X			skipstatement();
X			if ( Token==T_ELSE )
X			{
X				getoken();
X				statement();
X			}
X		}
X		break;
X	case T_WHILE:
X		/*
X		 * parse a "while" statement
X		 */
X		repeat = Actptr;
X		for ( ;; )
X		{
X			if ( getoken() != T_LPAREN )
X				syntaxerror();
X
X			getoken();
X			expression();
X			if ( Token!=T_RPAREN )
X				syntaxerror();
X
X			if ( popint() )
X			{
X				body = Actptr;
X				getoken();
X				statement();
X				if ( Saw_break )
X				{
X					Actptr = body;
X					Saw_break = 0;
X					break;
X				}
X				Actptr = repeat;
X			}
X			else
X				break;
X		}
X		getoken();
X		skipstatement();
X		break;
X	case T_BREAK:
X		/*
X		 * parse a "break" statement
X		 */
X		getoken();
X		Saw_break = 1;
X		break;
X	case T_SEMICOLON:
X		break;
X	default:
X		expression();
X		popint();
X	}
X
X	if ( Token==T_SEMICOLON )
X		getoken();
X}	
X
Xskipstatement()
X{
X	/*
X	 * Skip a statement
X	 */
X
X	switch ( Token )
X	{
X	case T_LBRACE:
X		/*
X		 * skip a compound statement
X		 */
X		skip( T_LBRACE, T_RBRACE );
X		break;
X	case T_IF:
X		/*
X		 * skip an "if-else" statement
X		 */
X		getoken();	/* skip 'if' */
X		skip( T_LPAREN, T_RPAREN );
X		skipstatement();
X		if ( Token==T_ELSE )
X		{
X			getoken();	/* skip 'else' */
X			skipstatement();
X		}
X		break;
X	case T_WHILE:
X		/*
X		 * skip a "while" statement
X		 */
X		getoken();	/* skip 'while' */
X		skip( T_LPAREN, T_RPAREN );
X		skipstatement();
X		break;
X	default:
X		/*
X		 * skip a one-liner
X		 */
X		while (Token!=T_SEMICOLON && Token!=T_RBRACE && Token!=T_EOF)
X			getoken();
X		if ( Token==T_EOF )
X			error( "unexpected end", ACT_ERROR );
X		if ( Token==T_SEMICOLON )
X			getoken();
X	}
X}
X
Xskip( left, right )
Xchar left, right;
X{
X	/*
X	 * Skip matched left and right delimiters and everything in between
X	 */
X	int parity;
X	char *save, errmsg[ 80 ];
X
X	parity = 1;
X	save = Actptr;
X	while ( getoken() != T_EOF )
X	{
X		if ( Token == left )
X		{
X			save = Actptr;
X			++parity;
X		}
X		else if ( Token == right )
X			--parity;
X		if ( !parity )
X		{
X			getoken();
X			return;
X		}
X	}
X	Actptr = save;
X
X	sprintf( errmsg, "mismatched '%c' and '%c'", left, right );
X	error( errmsg, ACT_ERROR );
X}
X
Xsyntaxerror()
X{
X	error( "syntax error", ACT_ERROR );
X}
+ END-OF-FILE bawkdo.c
chmod 'u=rw,g=r,o=r' \b\a\w\k\d\o\.\c
set `sum \b\a\w\k\d\o\.\c`
sum=$1
case $sum in
50452)	:;;
*)	echo 'Bad sum in '\b\a\w\k\d\o\.\c >&2
esac
echo Extracting \b\a\w\k\p\a\t\.\c
sed 's/^X//' > \b\a\w\k\p\a\t\.\c << '+ END-OF-FILE '\b\a\w\k\p\a\t\.\c
X/*
X * Bawk regular expression compiler/interpreter
X */
X#include <stdio.h>
X#include "bawk.h"
X
Xre_compile(patbuf)
X	char *patbuf;		/* where to put compiled pattern */
X{
X	/*
X	 * Compile a regular expression from current input file into the
X	 * given pattern buffer. 
X	 */
X	int c,			/* Current character         */
X	 o;			/* Temp                      */
X	char *patptr,		/* destination string pntr   */
X	*lp,			/* Last pattern pointer      */
X	*spp,			/* Save beginning of pattern */
X	 delim,			/* pattern delimiter         */
X	*cclass();		/* Compile class routine     */
X
X	patptr = patbuf;
X	delim = getcharacter();
X
X	while ((c = getcharacter()) != -1 && c != delim) {
X		/*
X		 * STAR, PLUS and MINUS are special. 
X		 */
X		if (c == '*' || c == '+' || c == '-') {
X			if (patptr == patbuf ||
X			    (o = patptr[-1]) == BOL ||
X			    o == EOL ||
X			    o == STAR ||
X			    o == PLUS ||
X			    o == MINUS)
X				error("illegal occurrance op", RE_ERROR);
X			*patptr++ = ENDPAT;
X			*patptr++ = ENDPAT;
X			spp = patptr;	/* Save pattern end     */
X			while (--patptr > lp)	/* Move pattern down... */
X				*patptr = patptr[-1];	/* one byte     */
X			*patptr = (c == '*') ? STAR :
X				(c == '-') ? MINUS : PLUS;
X			patptr = spp;	/* Restore pattern end  */
X			continue;
X		}
X		/*
X		 * All the rest. 
X		 */
X		lp = patptr;	/* Remember start       */
X		switch (c) {
X
X		case '^':
X			*patptr++ = BOL;
X			break;
X
X		case '$':
X			*patptr++ = EOL;
X			break;
X
X		case '.':
X			*patptr++ = ANY;
X			break;
X
X		case '[':
X			patptr = cclass(patptr);
X			break;
X
X		case ':':
X			if ((c = getcharacter()) != -1) {
X				switch (tolower(c)) {
X
X				case 'a':
X					*patptr++ = ALPHA;
X					break;
X
X				case 'd':
X					*patptr++ = DIGIT;
X					break;
X
X				case 'n':
X					*patptr++ = NALPHA;
X					break;
X
X				case ' ':
X					*patptr++ = PUNCT;
X					break;
X
X				default:
X					error("unknown ':' type", RE_ERROR);
X
X				}
X			}
X			else
X				error("no ':' type", RE_ERROR);
X			break;
X
X		case '\\':
X			c = getcharacter();
X
X		default:
X			*patptr++ = CHAR;
X			*patptr++ = c;
X		}
X	}
X	*patptr++ = ENDPAT;
X	*patptr++ = 0;		/* Terminate string     */
X
X#ifdef DEBUG
X	if (Debug > 1) {
X		for (lp = patbuf; lp < patptr; ++lp) {
X			switch (c = *lp) {
X			case CHAR:
X				printf("char ");
X				break;
X			case BOL:
X				printf("bol ");
X				break;
X			case EOL:
X				printf("eol ");
X				break;
X			case ANY:
X				printf("any ");
X				break;
X			case CLASS:
X				printf("class(%d) ", *++lp);
X				break;
X			case NCLASS:
X				printf("notclass(%d) ", *++lp);
X				break;
X			case STAR:
X				printf("star ");
X				break;
X			case PLUS:
X				printf("plus ");
X				break;
X			case MINUS:
X				printf("minus ");
X				break;
X			case ALPHA:
X				printf("alpha ");
X				break;
X			case DIGIT:
X				printf("digit ");
X				break;
X			case NALPHA:
X				printf("notalpha ");
X				break;
X			case PUNCT:
X				printf("punct ");
X				break;
X			case RANGE:
X				printf("range ");
X				break;
X			case ENDPAT:
X				printf("endpat ");
X				break;
X			default:
X				printf("<%c> ", c);
X				break;
X			}
X		}
X		printf("\n");
X	}
X#endif
X
X	return patptr - patbuf;
X}
X
Xchar *
Xcclass(patbuf)
X	char *patbuf;		/* destination pattern buffer */
X{
X	/*
X	 * Compile a class (within []) 
X	 */
X	char *patptr,		/* destination pattern pointer */
X	*cp;			/* Pattern start     */
X	int c,			/* Current character */
X	 o;			/* Temp              */
X
X	patptr = patbuf;
X
X	if ((c = getcharacter()) == -1)
X		error("class terminates badly", RE_ERROR);
X	else if (c == '^') {
X		/*
X		 * Class exclusion, for example: [^abc] Swallow the "^" and
X		 * set token type to class exclusion. 
X		 */
X		o = NCLASS;
X	}
X	else {
X		/*
X		 * Normal class, for example: [abc] push back the character
X		 * and set token type to class 
X		 */
X		ungetcharacter(c);
X		o = CLASS;
X	}
X	*patptr++ = o;
X
X	cp = patptr;		/* remember where byte count is */
X	*patptr++ = 0;		/* and initialize byte count */
X	while ((c = getcharacter()) != -1 && c != ']') {
X		o = getcharacter();	/* peek at next char */
X		if (c == '\\') {/* Store quoted chars */
X			if (o == -1)	/* Gotta get something */
X				error("class terminates badly", RE_ERROR);
X			*patptr++ = o;
X		}
X		else if (c == '-' && (patptr - cp) > 1 && o != ']' && o != -1) {
X			c = patptr[-1];	/* Range start     */
X			patptr[-1] = RANGE;	/* Range signal    */
X			*patptr++ = c;	/* Re-store start  */
X			*patptr++ = o;	/* Store end char  */
X		}
X		else {
X			*patptr++ = c;	/* Store normal char */
X			ungetcharacter(o);
X		}
X	}
X	if (c != ']')
X		error("unterminated class", RE_ERROR);
X	if ((c = (patptr - cp)) >= 256)
X		error("class too large", RE_ERROR);
X	if (c == 0)
X		error("empty class", RE_ERROR);
X	*cp = c;		/* fill in byte count */
X
X	return patptr;
X}
X
Xmatch(line, pattern)
X	char *line;		/* line to match */
X	char *pattern;		/* pattern to match */
X{
X	/*
X	 * Match the current line (in Linebuf[]), return 1 if it does. 
X	 */
X	char *l;		/* Line pointer       */
X	char *pmatch();
X	char *next;
X	int matches;
X
X	matches = 0;
X	for (l = line; *l; l++) {
X		if (next = pmatch(line, l, pattern)) {
X			l = next - 1;
X			++matches;
X#ifdef DEBUG
X			if (Debug)
X				printf("match!\n");
X#endif
X		}
X	}
X
X	return matches;
X}
X
Xchar *
Xpmatch(linestart, line, pattern)
X	char *linestart;	/* start of line to match */
X	char *line;		/* (partial) line to match      */
X	char *pattern;		/* (partial) pattern to match   */
X{
X	char *l;		/* Current line pointer         */
X	char *p;		/* Current pattern pointer      */
X	char c;			/* Current character            */
X	char *e;		/* End for STAR and PLUS match  */
X	int op;			/* Pattern operation            */
X	int n;			/* Class counter                */
X	char *are;		/* Start of STAR match          */
X
X	l = line;
X
X#ifdef DEBUG
X	if (Debug > 1)
X		printf("pmatch(\"%s\")\n", line);
X#endif
X
X	p = pattern;
X	while ((op = *p++) != ENDPAT) {
X
X#ifdef DEBUG
X		if (Debug > 1)
X			printf("byte[%d] = 0%o, '%c', op = 0%o\n",
X			       l - line, *l, *l, op);
X#endif
X
X		switch (op) {
X
X		case CHAR:
X			if (*l++ != *p++)
X				return 0;
X			break;
X
X		case BOL:
X			if (l != linestart)
X				return 0;
X			break;
X
X		case EOL:
X			if (*l != '\0')
X				return 0;
X			break;
X
X		case ANY:
X			if (*l++ == '\0')
X				return 0;
X			break;
X
X		case DIGIT:
X			if ((c = *l++) < '0' || (c > '9'))
X				return 0;
X			break;
X
X		case ALPHA:
X			c = tolower(*l++);
X			if (c < 'a' || c > 'z')
X				return 0;
X			break;
X
X		case NALPHA:
X			c = tolower(*l++);
X			if (c >= 'a' && c <= 'z')
X				break;
X			else if (c < '0' || c > '9')
X				return 0;
X			break;
X
X		case PUNCT:
X			c = *l++;
X			if (c == 0 || c > ' ')
X				return 0;
X			break;
X
X		case CLASS:
X		case NCLASS:
X			c = *l++;
X			n = *p++ & 0377;
X			do {
X				if (*p == RANGE) {
X					p += 3;
X					n -= 2;
X					if (c >= p[-2] && c <= p[-1])
X						break;
X				}
X				else if (c == *p++)
X					break;
X			} while (--n > 1);
X			if ((op == CLASS) == (n <= 1))
X				return 0;
X			if (op == CLASS)
X				p += n - 2;
X			break;
X
X		case MINUS:
X			e = pmatch(linestart, l, p);	/* Look for a match    */
X			while (*p++ != ENDPAT);	/* Skip over pattern   */
X			if (e)	/* Got a match?        */
X				l = e;	/* Yes, update string  */
X			break;	/* Always succeeds     */
X
X		case PLUS:	/* One or more ...     */
X			if ((l = pmatch(linestart, l, p)) == 0)
X				return 0;	/* Gotta have a match  */
X		case STAR:	/* Zero or more ...    */
X			are = l;/* Remember line start */
X			while (*l && (e = pmatch(linestart, l, p)))
X				l = e;	/* Get longest match   */
X			while (*p++ != ENDPAT);	/* Skip over pattern   */
X			while (l >= are) {	/* Try to match rest   */
X				if (e = pmatch(linestart, l, p))
X					return e;
X				--l;	/* Nope, try earlier   */
X			}
X			return 0;	/* Nothing else worked */
X
X		default:
X			fprintf(stderr, "bad op code %d\n", op);
X			error("can't happen -- match", RE_ERROR);
X		}
X	}
X	return l;
X}
+ END-OF-FILE bawkpat.c
chmod 'u=rw,g=r,o=r' \b\a\w\k\p\a\t\.\c
set `sum \b\a\w\k\p\a\t\.\c`
sum=$1
case $sum in
19618)	:;;
*)	echo 'Bad sum in '\b\a\w\k\p\a\t\.\c >&2
esac
echo Extracting \b\a\w\k\s\y\m\.\c
sed 's/^X//' > \b\a\w\k\s\y\m\.\c << '+ END-OF-FILE '\b\a\w\k\s\y\m\.\c
X/*
X * Bawk C actions builtin functions, variable declaration, and
X * stack management routines.
X */
X#include	<stdio.h>
X#include	<ctype.h>
X#include "bawk.h"
X
X#define MAXARGS		10	/* max # of arguments to a builtin func */
X#define F_PRINTF	1
X#define F_GETLINE	2
X#define F_STRLEN	3
X#define F_STRCPY	4
X#define F_STRCMP	5
X#define F_TOUPPER	6
X#define F_TOLOWER	7
X#define F_MATCH		8
X#define F_NEXTFILE	9
X
Xisfunction( s )
Xchar *s;
X{
X	/*
X	 * Compare the string "s" to a list of builtin functions
X	 * and return its (non-zero) token number.
X	 * Return zero if "s" is not a function.
X	 */
X	if ( !strcmp( s, "printf" ) )
X		return F_PRINTF;
X	if ( !strcmp( s, "getline" ) )
X		return F_GETLINE;
X	if ( !strcmp( s, "strlen" ) )
X		return F_STRLEN;
X	if ( !strcmp( s, "strcpy" ) )
X		return F_STRCPY;
X	if ( !strcmp( s, "strcmp" ) )
X		return F_STRCMP;
X	if ( !strcmp( s, "toupper" ) )
X		return F_TOUPPER;
X	if ( !strcmp( s, "tolower" ) )
X		return F_TOLOWER;
X	if ( !strcmp( s, "match" ) )
X		return F_MATCH;
X	if ( !strcmp( s, "nextfile" ) )
X		return F_NEXTFILE;
X	return 0;
X}
X
Xiskeyword( s )
Xchar *s;
X{
X	/*
X	 * Compare the string "s" to a list of keywords and return its
X	 * (non-zero) token number.  Return zero if "s" is not a keyword.
X	 */
X	if ( !strcmp( s, "char" ) )
X		return T_CHAR;
X	if ( !strcmp( s, "int" ) )
X		return T_INT;
X	if ( !strcmp( s, "if" ) )
X		return T_IF;
X	if ( !strcmp( s, "else" ) )
X		return T_ELSE;
X	if ( !strcmp( s, "while" ) )
X		return T_WHILE;
X	if ( !strcmp( s, "break" ) )
X		return T_BREAK;
X
X	if ( !strcmp( s, "NF" ) )
X		return T_NF;
X	if ( !strcmp( s, "NR" ) )
X		return T_NR;
X	if ( !strcmp( s, "FS" ) )
X		return T_FS;
X	if ( !strcmp( s, "RS" ) )
X		return T_RS;
X	if ( !strcmp( s, "FILENAME" ) )
X		return T_FILENAME;
X	if ( !strcmp( s, "BEGIN" ) )
X		return T_BEGIN;
X	if ( !strcmp( s, "END" ) )
X		return T_END;
X	return 0;
X}
X
Xfunction( funcnum )
X{
X	int argc, args[ MAXARGS ];
X	char lpar;
X
X	argc = 0;
X	if ( Token==T_LPAREN )
X	{
X		lpar = 1;
X		getoken();
X	}
X	else
X		lpar = 0;
X	/*
X	 * If there are any arguments, evaluate them and copy their values
X	 * to a local array.
X	 */
X	if ( Token!=T_RPAREN && Token!=T_EOF )
X	{
X		for ( ;; )
X		{
X			expression();
X			if ( argc<MAXARGS )
X				args[ argc++ ] = popint();
X			else
X				popint();
X			if ( Token==T_COMMA )
X				getoken();
X			else
X				break;
X		}
X	}
X	if ( lpar && Token!=T_RPAREN )
X		error( "missing ')'", ACT_ERROR );
X	else if ( Token==T_RPAREN )
X		getoken();
X
X	switch ( funcnum )
X	{
X	case F_PRINTF:	/* just like the real printf() function */
X		pushint( printf( args[0], args[1], args[2], args[3], args[4],
X			 args[5], args[6], args[7], args[8], args[9] ) );
X		break;
X	case F_GETLINE:
X		/*
X		 * Get the next line of input from the current input file
X		 * and parse according to the current field seperator.
X		 * Don't forget to free up the previous line's words first...
X		 */
X		while ( Fieldcount )
X			free( Fields[ --Fieldcount ] );
X		pushint( getline() );
X		Fieldcount = parse( Linebuf, Fields, Fieldsep );
X		break;
X	case F_STRLEN:	/* calculate length of string argument */
X		pushint( strlen( args[0] ) );
X		break;
X	case F_STRCPY:	/* copy second string argument to first string */
X		pushint( strcpy( args[0], args[1] ) );
X		break;
X	case F_STRCMP:	/* compare two strings */
X		pushint( strcmp( args[0], args[1] ) );
X		break;
X	case F_TOUPPER:	/* convert the character argument to upper case */
X		pushint( toupper( args[0] ) );
X		break;
X	case F_TOLOWER:	/* convert the character argument to lower case */
X		pushint( tolower( args[0] ) );
X		break;
X	case F_MATCH:	/* match a string argument to a regular expression */
X		pushint( match( args[0], args[1] ) );
X		break;
X	case F_NEXTFILE:/* close current input file and process next file */
X		pushint( endfile() );
X		break;
X	default:	/* oops! */
X		error( "bad function call", ACT_ERROR );
X	}
X}
X
XVARIABLE *
Xfindvar( s )
Xchar *s;
X{
X	/*
X	 * Search the symbol table for a variable whose name is "s".
X	 */
X	VARIABLE *pvar;
X	int i;
X	char name[ MAXVARLEN ];
X
X	i = 0;
X	while ( i < MAXVARLEN && alphanum( *s ) )
X		name[i++] = *s++;
X	if ( i<MAXVARLEN )
X		name[i] = 0;
X
X	for ( pvar = Vartab; pvar<Nextvar; ++pvar )
X	{
X		if ( !strncmp( pvar->vname, name, MAXVARLEN ) )
X			return pvar;
X	}
X	return NULL;
X}
X
XVARIABLE *
Xaddvar( name )
Xchar *name;
X{
X	/*
X	 * Add a new variable to symbol table and assign it default
X	 * attributes (int name;)
X	 */
X	int i;
X
X	if ( Nextvar <= Vartab + MAXVARTABSZ )
X	{
X		i = 0;
X		while ( i<MAXVARLEN && alphanum( *name ) )
X			Nextvar->vname[i++] = *name++;
X		if ( i<MAXVARLEN )
X			Nextvar->vname[i] = 0;
X
X		Nextvar->vclass = 0;
X		Nextvar->vsize = WORD;
X		Nextvar->vlen = 0;
X		/*
X		 * Allocate some new room
X		 */
X		Nextvar->vptr = getmem( WORD );
X		fillmem( Nextvar->vptr, WORD, 0 );
X	}
X	else
X		error( "symbol table overflow", MEM_ERROR );
X
X	return Nextvar++;
X}
X
Xdeclist()
X{
X	/*
X	 * Parse a "char" or "int" statement.
X	 */
X	char type;
X
X	type = Token;
X	getoken();
X	decl( type );
X	while ( Token==T_COMMA )
X	{
X		getoken();
X		decl( type );
X	}
X	if ( Token==T_SEMICOLON )
X		getoken();
X}
X
XVARIABLE *
Xdecl( type )
X{
X	/*
X	 * Parse an element of a "char" or "int" declaration list.
X	 * The function stmt_compile() has already entered the variable
X	 * into the symbol table as an integer, this routine simply changes
X	 * the symbol's class, size or length according to the declaraction.
X	 * WARNING: The interpreter depends on the fact that pointers are
X	 * the same length as int's.  If your machine uses long's for
X	 * pointers either change the code or #define int long (or whatever).
X	 */
X	char class, size;
X	int len;
X	unsigned oldsize, newsize;
X	VARIABLE *pvar;
X
X	if ( Token==T_MUL )
X	{
X		/*
X		 * it's a pointer
X		 */
X		getoken();
X		pvar = decl( type );
X		++pvar->vclass;
X	}
X	else if ( Token==T_VARIABLE )
X	{
X		/*
X		 * Simple variable so far.  The token value (in the global
X		 * "Value" variable) is a pointer to the variable's symbol
X		 * table entry.
X		 */
X/* cast for msc, right for bawk? */
X		pvar = (VARIABLE *) Value.dptr;
X		getoken();
X		class = 0;
X		/*
X		 * Compute its length
X		 */
X		if ( Token==T_LBRACKET )
X		{
X			/*
X			 * It's an array.
X			 */
X			getoken();
X			++class;
X			/*
X			 * Compute the dimension
X			 */
X			expression();
X			if ( Token!=T_RBRACKET )
X				error( "missing ']'", ACT_ERROR );
X			getoken();
X			len = popint();
X		}
X		else
X			/*
X			 * It's a simple variable - array length is zero.
X			 */
X			len = 0;
X
X		size = (type==T_CHAR) ? BYTE : WORD;
X
X		newsize = (len ? len : 1) * size;
X		oldsize = (pvar->vlen ? pvar->vlen : 1) * pvar->vsize;
X		if ( newsize != oldsize )
X		{
X			/*
X			 * The amount of storage needed for the variable
X			 * has changed - free up memory allocated initially
X			 * and reallocate for new size.
X			 */
X			free( pvar->vptr );
X			pvar->vptr = getmem( newsize );
X		}
X		/*
X		 * Now change the variable's attributes.
X		 */
X		pvar->vclass = class;
X		pvar->vsize = size;
X		pvar->vlen = len;
X	}
X	else
X		syntaxerror();
X
X	return pvar;
X}
X
Xassignment()
X{
X	/*
X	 * Perform an assignment
X	 */
X	int ival;
X
X	ival = popint();
X	/*
X	 * make sure we've got an lvalue
X	 */
X	if ( Stackptr->lvalue )
X	{
X		if ( Stackptr->class )
X			movemem( &ival, Stackptr->value.dptr, WORD );
X		else
X			movemem(&ival, Stackptr->value.dptr, Stackptr->size);
X		pop();
X		pushint( ival );
X	}
X	else
X		error( "'=' needs an lvalue", ACT_ERROR );
X}
X
Xpop()
X{
X	/*
X	 * Pop the stack and return the integer value
X	 */
X	if ( Stackptr >= Stackbtm )
X		return (Stackptr--)->value.ival;
X	return error( "stack underflow", ACT_ERROR );
X}
X
Xpush( pclass, plvalue, psize, pdatum )
Xchar pclass, plvalue, psize;
XDATUM *pdatum;
X{
X	/*
X	 * Push item parts onto the stack
X	 */
X	if ( ++Stackptr <= Stacktop )
X	{
X		Stackptr->lvalue = plvalue;
X		Stackptr->size = psize;
X		if ( !(Stackptr->class = pclass)  &&  !plvalue )
X			Stackptr->value.ival = pdatum->ival;
X		else
X			Stackptr->value.dptr = pdatum->dptr;
X	}
X	else
X		error( "stack overflow", MEM_ERROR );
X}
X
Xpushint( intvalue )
Xint intvalue;
X{
X	/*
X	 * push an integer onto the stack
X	 */
X	if ( ++Stackptr <= Stacktop )
X	{
X		Stackptr->lvalue =
X		Stackptr->class = 0;
X		Stackptr->size = WORD;
X		Stackptr->value.ival = intvalue;
X	}
X	else
X		error( "stack overflow", MEM_ERROR );
X}
X
Xpopint()
X{
X	/*
X	 * Resolve the item on the top of the stack and return it
X	 */
X	int intvalue;
X
X	if ( Stackptr->lvalue )
X	{
X		/*
X		 * if it's a byte indirect, sign extend it
X		 */
X		if ( Stackptr->size == BYTE && !Stackptr->class )
X			intvalue = *Stackptr->value.dptr;
X		else
X		{
X			/*
X			 * otherwise, it's an unsigned int
X			 */
X			intvalue = (int)*Stackptr->value.ptrptr;
X		}
X		pop();
X		return intvalue;
X	}
X	else
X	{
X		/*
X		 * else it's an ACTUAL, just pop it
X		 */
X		return pop();
X	}
X}
+ END-OF-FILE bawksym.c
chmod 'u=rw,g=r,o=r' \b\a\w\k\s\y\m\.\c
set `sum \b\a\w\k\s\y\m\.\c`
sum=$1
case $sum in
56175)	:;;
*)	echo 'Bad sum in '\b\a\w\k\s\y\m\.\c >&2
esac
echo Extracting \m\a\k\e\f\i\l\e
sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e
XOBJ=	bawk.s bawkact.s bawkdo.s bawkpat.s bawksym.s scanf.s
X
Xbawk:	$(OBJ)
X	cc -o bawk $(OBJ) 
X
+ END-OF-FILE makefile
chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e
set `sum \m\a\k\e\f\i\l\e`
sum=$1
case $sum in
48160)	:;;
*)	echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2
esac
echo Extracting \s\c\a\n\f\.\c
sed 's/^X//' > \s\c\a\n\f\.\c << '+ END-OF-FILE '\s\c\a\n\f\.\c
X/* scanf - formatted input conversion	Author: Patrick van Kleef */
X
X#include "stdio.h"
X
X
Xint scanf (format, args)
Xchar           *format;
Xunsigned        args;
X{
X	return _doscanf (0, stdin, format, &args);
X}
X
X
X
Xint fscanf (fp, format, args)
XFILE           *fp;
Xchar           *format;
Xunsigned        args;
X{
X	return _doscanf (0, fp, format, &args);
X}
X
X
Xint sscanf (string, format, args)
Xchar           *string;		/* source of data */
Xchar           *format;		/* control string */
Xunsigned        args;		/* our args */
X{
X	return _doscanf (1, string, format, &args);
X}
X
X
Xunion ptr_union {
X	char           *chr_p;
X	unsigned int   *uint_p;
X	unsigned long  *ulong_p;
X};
X
Xstatic int      ic;		/* the current character */
Xstatic char    *rnc_arg;	/* the string or the filepointer */
Xstatic          rnc_code;	/* 1 = read from string, else from FILE */
X
X
X
X
X/* get the next character */
X
Xstatic rnc ()
X{
X	if (rnc_code) {
X		if (!(ic = *rnc_arg++))
X			ic = EOF;
X	} else
X		ic = getc ((FILE *) rnc_arg);
X}
X
X
X
X/*
X * unget the current character 
X */
X
Xstatic ugc ()
X{
X
X	if (rnc_code)
X		--rnc_arg;
X	else
X		ungetc (ic, rnc_arg);
X}
X
X
X
Xstatic scnindex(ch, string)
Xchar ch;
Xchar *string;
X{
X	while (*string++ != ch) 
X		if (!*string)
X			return 0;
X	return 1;
X}
X
X
X/*
X * this is cheaper than iswhite from <ctype.h> 
X */
X
Xstatic iswhite (ch)
Xint             ch;
X{
X
X	return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
X}
X
X
X
X
X
Xstatic isdigit (ch)
Xint             ch;
X{
X	return (ch >= '0' && ch <= '9');
X}
X
X
X
X
Xstatic tolower (ch)
Xint             ch;
X{
X	if (ch >= 'A' && ch <= 'Z')
X		ch = ch + 'a' - 'A';
X
X	return ch;
X}
X
X
X/*
X * the routine that does the job 
X */
X
X_doscanf (code, funcarg, format, argp)
Xint             code;		/* function to get a character */
Xchar           *funcarg;	/* an argument for the function */
Xchar           *format;		/* the format control string */
Xunion ptr_union *argp;		/* our argument list */
X{
X	int             done = 0;	/* number of items done */
X	int             base;		/* conversion base */
X	long            val;		/* an integer value */
X	int             sign;		/* sign flag */
X	int             do_assign;	/* assignment suppression flag */
X	unsigned        width;		/* width of field */
X	int             widflag;	/* width was specified */
X	int             longflag;	/* true if long */
X	int             done_some;	/* true if we have seen some data */
X	int		reverse;	/* reverse the checking in [...] */
X	char	       *endbracket;     /* position of the ] in format string */
X
X	rnc_arg = funcarg;
X	rnc_code = code;
X
X	rnc ();			/* read the next character */
X
X	if (ic == EOF) {
X		done = EOF;
X		goto quit;
X	}
X
X	while (1) {
X		while (iswhite (*format))
X			++format;	/* skip whitespace */
X		if (!*format)
X			goto all_done;	/* end of format */
X		if (ic < 0)
X			goto quit;	/* seen an error */
X		if (*format != '%') {
X			while (iswhite (ic))
X				rnc ();
X			if (ic != *format)
X				goto all_done;
X			++format;
X			rnc ();
X			++done;
X			continue;
X		}
X		++format;
X		do_assign = 1;
X		if (*format == '*') {
X			++format;
X			do_assign = 0;
X		}
X		if (isdigit (*format)) {
X			widflag = 1;
X			for (width = 0; isdigit (*format);)
X				width = width * 10 + *format++ - '0';
X		} else
X			widflag = 0;	/* no width spec */
X		if (longflag = (tolower (*format) == 'l'))
X			++format;
X		if (*format != 'c')
X			while (iswhite (ic))
X				rnc ();
X		done_some = 0;	/* nothing yet */
X		switch (*format) {
X		case 'o':
X			base = 8;
X			goto decimal;
X		case 'u':
X		case 'd':
X			base = 10;
X			goto decimal;
X		case 'x':
X			base = 16;
X			if (((!widflag) || width >= 2) && ic == '0') {
X				rnc ();
X				if (tolower (ic) == 'x') {
X					width -= 2;
X					done_some = 1;
X					rnc ();
X				} else {
X					ugc ();
X					ic = '0';
X				}
X			}
X	decimal:
X			val = 0L;	/* our result value */
X			sign = 0;	/* assume positive */
X			if (!widflag)
X				width = 0xffff;	/* very wide */
X			if (width && ic == '+')
X				rnc ();
X			else if (width && ic == '-') {
X				sign = 1;
X				rnc ();
X			}
X			while (width--) {
X				if (isdigit (ic) && ic - '0' < base)
X					ic -= '0';
X				else if (base == 16 && tolower (ic) >= 'a' && tolower (ic) <= 'f')
X					ic = 10 + tolower (ic) - 'a';
X				else
X					break;
X				val = val * base + ic;
X				rnc ();
X				done_some = 1;
X			}
X			if (do_assign) {
X				if (sign)
X					val = -val;
X				if (longflag)
X					*(argp++)->ulong_p = (unsigned long) val;
X				else
X					*(argp++)->uint_p = (unsigned) val;
X			}
X			if (done_some)
X				++done;
X			else
X				goto all_done;
X			break;
X		case 'c':
X			if (!widflag)
X				width = 1;
X			while (width-- && ic >= 0) {
X				if (do_assign)
X					*(argp)->chr_p++ = (char) ic;
X				rnc ();
X				done_some = 1;
X			}
X			if (do_assign)
X				argp++;	/* done with this one */
X			if (done_some)
X				++done;
X			break;
X		case 's':
X			if (!widflag)
X				width = 0xffff;
X			while (width-- && !iswhite (ic) && ic > 0) {
X				if (do_assign)
X					*(argp)->chr_p++ = (char) ic;
X				rnc ();
X				done_some = 1;
X			}
X			if (do_assign)		/* terminate the string */
X				*(argp++)->chr_p = '\0';	
X			if (done_some)
X				++done;
X			else
X				goto all_done;
X			break;
X		case '[':
X			if (!widflag)
X				width = 0xffff;
X
X			if ( *(++format) == '^' ) {
X				reverse = 1;
X				format++;
X			} else
X				reverse = 0;
X			
X			endbracket = format;
X			while ( *endbracket != ']'  && *endbracket != '\0')
X				endbracket++;
X			
X			if (!*endbracket)
X				goto quit;
X			
X			*endbracket = '\0';	/* change format string */
X
X			while (width-- && !iswhite (ic) && ic > 0 &&
X				(scnindex (ic, format) ^ reverse)) {
X				if (do_assign)
X					*(argp)->chr_p++ = (char) ic;
X				rnc ();
X				done_some = 1;
X			}
X			format = endbracket;
X			*format = ']';		/* put it back */
X			if (do_assign)		/* terminate the string */
X				*(argp++)->chr_p = '\0';	
X			if (done_some)
X				++done;
X			else
X				goto all_done;
X			break;
X		}		/* end switch */
X		++format;
X	}
Xall_done:
X	if (ic >= 0)
X		ugc ();		/* restore the character */
Xquit:
X	return done;
X}
+ END-OF-FILE scanf.c
chmod 'u=rw,g=r,o=r' \s\c\a\n\f\.\c
set `sum \s\c\a\n\f\.\c`
sum=$1
case $sum in
45242)	:;;
*)	echo 'Bad sum in '\s\c\a\n\f\.\c >&2
esac
exit 0