[comp.os.minix] Bawk, part 2 of 2

ast@cs.vu.nl (09/23/89)

: 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:/usr/ucb
echo Extracting 'bawkdo.c'
sed 's/^X//' > 'bawkdo.c' << '+ END-OF-FILE ''bawkdo.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 = *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                pvar = 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                        data.dptr = &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                        pp = *Stackptr->value.ptrptr;
X                        *Stackptr->value.ptrptr += incr * Stackptr->size;
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                                pp = *Stackptr->value.dptr;
X                        else
X                                pp = *Stackptr->value.ptrptr;
X                        *Stackptr->value.ptrptr += incr;
X                        Stackptr->value.ival = 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' 'bawkdo.c'
set `wc -c 'bawkdo.c'`
count=$1
case $count in
21383)	:;;
*)	echo 'Bad character count in ''bawkdo.c' >&2
		echo 'Count should be 21383' >&2
esac
echo Extracting 'bawkpat.c'
sed 's/^X//' > 'bawkpat.c' << '+ END-OF-FILE ''bawkpat.c'
X/*
X * Bawk regular expression compiler/interpreter
X */
X#include <stdio.h>
X#include "bawk.h"
X
Xre_compile( patbuf )
Xchar    *patbuf;                /* where to put compiled pattern */
X{
X        /*
X         * Compile a regular expression from current input file
X         * into the 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                /*
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                        {
X                                switch( tolower( c ) )
X                                {
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        {
X                for ( lp=patbuf; lp<patptr; ++lp )
X                {
X                        switch ( c = *lp )
X                        {
X                        case CHAR:      printf("char "); break;
X                        case BOL:       printf("bol "); break;
X                        case EOL:       printf("eol "); break;
X                        case ANY:       printf("any "); break;
X                        case CLASS:     printf("class(%d) ", *++lp); break;
X                        case NCLASS:    printf("notclass(%d) ",*++lp); break;
X                        case STAR:      printf("star "); break;
X                        case PLUS:      printf("plus "); break;
X                        case MINUS:     printf("minus "); break;
X                        case ALPHA:     printf("alpha "); break;
X                        case DIGIT:     printf("digit "); break;
X                        case NALPHA:    printf("notalpha "); break;
X                        case PUNCT:     printf("punct "); break;
X                        case RANGE:     printf("range "); break;
X                        case ENDPAT:    printf("endpat "); break;
X                        default:        printf("<%c> ", c); break;
X                        }
X                }
X                printf( "\n" );
X        }
X#endif
X
X        return patptr - patbuf;
X}
X
Xchar *
Xcclass( patbuf )
Xchar    *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                /*
X                 * Class exclusion, for example: [^abc]
X                 * Swallow the "^" and set token type to class exclusion.
X                 */
X                o = NCLASS;
X        }
X        else
X        {
X                /*
X                 * Normal class, for example: [abc]
X                 * push back the character 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        {
X                o = getcharacter();             /* peek at next char */
X                if (c == '\\')                  /* Store quoted chars */
X                {
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                {
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                {
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 )
Xchar    *line;          /* line to match */
Xchar    *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        {
X                if ( next = pmatch(line, l, pattern) )
X                {
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)
Xchar    *linestart;     /* start of line to match */
Xchar    *line;          /* (partial) line to match      */
Xchar    *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' 'bawkpat.c'
set `wc -c 'bawkpat.c'`
count=$1
case $count in
13298)	:;;
*)	echo 'Bad character count in ''bawkpat.c' >&2
		echo 'Count should be 13298' >&2
esac
echo Extracting 'bawksym.c'
sed 's/^X//' > 'bawksym.c' << '+ END-OF-FILE ''bawksym.c'
X/*
X * Bawk C actions builtin functions, variable declaration, and
X * stack management routines.
X */
X#include <stdio.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                pvar = 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 = *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' 'bawksym.c'
set `wc -c 'bawksym.c'`
count=$1
case $count in
12636)	:;;
*)	echo 'Bad character count in ''bawksym.c' >&2
		echo 'Count should be 12636' >&2
esac
exit 0