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