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