home *** CD-ROM | disk | FTP | other *** search
/ Boston 2 / boston-2.iso / DOS / PROGRAM / C / CUG / BAWK.C < prev    next >
Text File  |  1993-12-01  |  12KB  |  616 lines

  1. /*
  2.     HEADER:        CUG000.00;
  3.     TITLE:        BAWK Text Pattern/Action Tool (Brod86);
  4.     DATE:        05/17/1987;
  5.     DESCRIPTION:    "BAWK scans text files for regular expression patterns
  6.             and executes a user-defined action (C code fragment)
  7.             for each specified pattern found.  Descended from the
  8.             DECUS version of the same program.";
  9.     VERSION:    1.1;
  10.     KEYWORDS:    Text Filter;
  11.     FILENAME:    BAWK.C;
  12.     SEE-ALSO:    BAWK.H, BAWK.DOC, BAWKACT.C, BAWKDO.C, BAWKPAT.C,
  13.             BAWKSYM.C;
  14.     COMPILERS:    vanilla;
  15.     AUTHORS:    W. C. Colley III, B. Brodt;
  16.     WARNINGS:    "Program runs out of memory under CP/M Eco-C and dies
  17.             without a trace.  Aztec C II is OK.  Won't compile
  18.             yet under ECO-C88 -- module bawkdo.c gives a strange
  19.             code generator error.  REQUIRES that sizeof(int) ==
  20.             sizeof(char *)!!!!!  Therefore, the MSDOS small model
  21.             is OK, but the MSDOS large model is no good.";
  22. */
  23.  
  24. /*
  25.  * Bawk main program
  26.  */
  27. #define MAIN 1
  28. #include <stdio.h>
  29. #include "bawk.h"
  30.  
  31. /* Functions local to this module.    */
  32.  
  33. void compile(), newfile(), process(), usage();
  34.  
  35. /*
  36.  * Main program
  37.  */
  38. int main( argc, argv )
  39. int argc;
  40. char **argv;
  41. {
  42.     char gotrules, didfile, getstdin;
  43.  
  44.     getstdin =
  45.     didfile =
  46.     gotrules = 0;
  47.  
  48.     /*
  49.      * Initialize global variables:
  50.      */
  51.     Stackptr = Stackbtm - 1;
  52.     Stacktop = Stackbtm + MAXSTACKSZ;
  53.     Nextvar = Vartab;
  54.  
  55.     strcpy( Fldsep, " \t" );
  56.     strcpy( Rcrdsep, "\n" );
  57.  
  58.     /*
  59.      * Parse command line
  60.      */
  61.     while ( --argc )
  62.     {
  63.         if ( **(++argv) == '-' )
  64.         {
  65.             /*
  66.              * Process dash options.
  67.              */
  68.             switch (*++*argv)
  69.             {
  70. #ifdef DEBUG
  71.             case 'D':
  72.             case 'd':
  73.                 ++Debug;
  74.                 break;
  75. #endif
  76.             case 0:
  77.                 ++getstdin;
  78.                 --argv;
  79.                 goto dosomething;
  80.                 break;
  81.             default: usage();
  82.             }
  83.         }
  84.         else
  85.         {
  86. dosomething:
  87.             if ( gotrules )
  88.             {
  89.                 /*
  90.                  * Already read rules file - assume this is
  91.                  * is a text file for processing.
  92.                  */
  93.                 if ( ++didfile == 1 && Beginact )
  94.                     doaction( Beginact );
  95.                 if ( getstdin )
  96.                 {
  97.                     --getstdin;
  98.                     newfile( 0 );
  99.                 }
  100.                 else
  101.                     newfile( *argv );
  102.                 process();
  103.             }
  104.             else
  105.             {
  106.                 /*
  107.                  * First file name argument on command line
  108.                  * is assumed to be a rules file - attempt to
  109.                  * compile it.
  110.                  */
  111.                 if ( getstdin )
  112.                 {
  113.                     --getstdin;
  114.                     newfile( 0 );
  115.                 }
  116.                 else
  117.                     newfile( *argv );
  118.                 compile();
  119.                 gotrules = 1;
  120.             }
  121.         }
  122.     }
  123.     if ( !gotrules )
  124.         usage();
  125.  
  126.     if ( ! didfile )
  127.     {
  128.         /*
  129.          * Didn't process any files yet - process stdin.
  130.          */
  131.         newfile( 0 );
  132.         if ( Beginact )
  133.             doaction( Beginact );
  134.         process();
  135.     }
  136.     if ( Endact )
  137.         doaction( Endact );
  138.     return 0;
  139. }
  140.  
  141. /*
  142.  * Regular expression/action file compilation routines.
  143.  */
  144. void compile()
  145. {
  146.     /*
  147.      * Compile regular expressions and C actions into Rules struct,
  148.      * reading from current input file "Fileptr".
  149.      */
  150.     int c, len;
  151.  
  152. #ifdef DEBUG
  153.     if ( Debug )
  154.         error( "compiling...", 0 );
  155. #endif
  156.  
  157.     while ( (c = getcharacter()) != -1 )
  158.     {
  159.         if ( c==' ' || c=='\t' || c=='\n' )
  160.             /* swallow whitespace */
  161.             ;
  162.         else if ( c=='#' )
  163.         {
  164.             /*
  165.              * Swallow comments
  166.              */
  167.             while ( (c=getcharacter()) != -1 && c!='\n' )
  168.                 ;
  169.         }
  170.         else if ( c=='{' )
  171.         {
  172. #ifdef DEBUG
  173.             if ( Debug )
  174.                 error( "action", 0 );
  175. #endif
  176.             /*
  177.              * Compile (tokenize) the action string into our
  178.              * global work buffer, then allocate some memory
  179.              * for it and copy it over.
  180.              */
  181.             ungetcharacter( '{' );
  182.             len = act_compile( Workbuf );
  183.  
  184.             if ( Rulep && Rulep->action )
  185.             {
  186.                 Rulep->nextrule =
  187.                     (RULE *)getmem(sizeof(RULE));
  188.                 Rulep = Rulep->nextrule;
  189.                 fillmem( Rulep, sizeof(RULE), 0 );
  190.             }
  191.             if ( !Rulep )
  192.             {
  193.                 /*
  194.                  * This is the first action encountered.
  195.                  * Allocate the first Rules structure and
  196.                  * initialize it
  197.                  */
  198.                 Rules = Rulep =
  199.                     (RULE *)getmem(sizeof(RULE));
  200.                 fillmem( Rulep, sizeof(RULE), 0 );
  201.             }
  202.             Rulep->action = getmem( len );
  203.             movemem( Workbuf, Rulep->action, len );
  204.         }
  205.         else if ( c==',' )
  206.         {
  207. #ifdef DEBUG
  208.             if ( Debug )
  209.                 error( "stop pattern", 0 );
  210. #endif
  211.             /*
  212.              * It's (hopefully) the second part of a two-part
  213.              * pattern string.  Swallow the comma and start
  214.              * compiling an action string.
  215.              */
  216.             if ( !Rulep || !Rulep->pattern.start )
  217.                 error( "stop pattern without a start",
  218.                     RE_ERROR );
  219.             if ( Rulep->pattern.stop )
  220.                 error( "already have a stop pattern",
  221.                     RE_ERROR );
  222.             len = pat_compile( Workbuf );
  223.             Rulep->pattern.stop = getmem( len );
  224.             movemem( Workbuf, Rulep->pattern.stop, len );
  225.         }
  226.         else
  227.         {
  228.             /*
  229.              * Assume it's a regular expression pattern
  230.              */
  231. #ifdef DEBUG
  232.             if ( Debug )
  233.                 error( "start pattern", 0 );
  234. #endif
  235.  
  236.             ungetcharacter( c );
  237.             len = pat_compile( Workbuf );
  238.  
  239.             if ( *Workbuf == T_BEGIN )
  240.             {
  241.                 /*
  242.                  * Saw a "BEGIN" keyword - compile following
  243.                  * action into special "Beginact" buffer.
  244.                  */
  245.                 len = act_compile( Workbuf );
  246.                 Beginact = getmem( len );
  247.                 movemem( Workbuf, Beginact, len );
  248.                 continue;
  249.             }
  250.             if ( *Workbuf == T_END )
  251.             {
  252.                 /*
  253.                  * Saw an "END" keyword - compile following
  254.                  * action into special "Endact" buffer.
  255.                  */
  256.                 len = act_compile( Workbuf );
  257.                 Endact = getmem( len );
  258.                 movemem( Workbuf, Endact, len );
  259.                 continue;
  260.             }
  261.             if ( Rulep )
  262.             {
  263.                 /*
  264.                  * Already saw a pattern/action - link in
  265.                  * another Rules structure.
  266.                  */
  267.                 Rulep->nextrule =
  268.                     (RULE *)getmem(sizeof(RULE));
  269.                 Rulep = Rulep->nextrule;
  270.                 fillmem( Rulep, sizeof(RULE), 0 );
  271.             }
  272.             if ( !Rulep )
  273.             {
  274.                 /*
  275.                  * This is the first pattern encountered.
  276.                  * Allocate the first Rules structure and
  277.                  * initialize it
  278.                  */
  279.                 Rules = Rulep =
  280.                     (RULE *)getmem(sizeof(RULE));
  281.                 fillmem( Rulep, sizeof(RULE), 0 );
  282.             }
  283.             if ( Rulep->pattern.start )
  284.                 error( "already have a start pattern",
  285.                     RE_ERROR );
  286.  
  287.             Rulep->pattern.start = getmem( len );
  288.             movemem( Workbuf, Rulep->pattern.start, len );
  289.         }
  290.     }
  291.     endfile();
  292. }
  293.  
  294. /*
  295.  * Text file main processing loop.
  296.  */
  297. void process()
  298. {
  299.     /*
  300.      * Read a line at a time from current input file at "Fileptr",
  301.      * then apply each rule in the Rules chain to the input line.
  302.      */
  303.     int i;
  304.  
  305. #ifdef DEBUG
  306.     if ( Debug )
  307.         error( "processing...", 0 );
  308. #endif
  309.  
  310.     Recordcount = 0;
  311.  
  312.     while ( getline() )
  313.     {
  314.         /*
  315.          * Parse the input line.
  316.          */
  317.         Fieldcount = parse( Linebuf, Fields, Fldsep );
  318. #ifdef DEBUG
  319.         if ( Debug>1 )
  320.         {
  321.             printf( "parsed %d words:\n", Fieldcount );
  322.             for(i=0; i<Fieldcount; ++i )
  323.                 printf( "<%s>\n", Fields[i] );
  324.         }
  325. #endif
  326.  
  327.         Rulep = Rules;
  328.         do
  329.         {
  330.             if ( ! Rulep->pattern.start )
  331.             {
  332.                 /*
  333.                  * No pattern given - perform action on
  334.                  * every input line.
  335.                  */
  336.                 doaction( Rulep->action );
  337.             }
  338.             else if ( Rulep->pattern.startseen )
  339.             {
  340.                 /*
  341.                  * Start pattern already found - perform
  342.                  * action then check if line matches
  343.                  * stop pattern.
  344.                  */
  345.                 doaction( Rulep->action );
  346.                 if ( dopattern( Rulep->pattern.stop ) )
  347.                     Rulep->pattern.startseen = 0;
  348.             }
  349.             else if ( dopattern( Rulep->pattern.start ) )
  350.             {
  351.                 /*
  352.                  * Matched start pattern - perform action.
  353.                  * If a stop pattern was given, set "start
  354.                  * pattern seen" flag and process every input
  355.                  * line until stop pattern found.
  356.                  */
  357.                 doaction( Rulep->action );
  358.                 if ( Rulep->pattern.stop )
  359.                     Rulep->pattern.startseen = 1;
  360.             }
  361.         }
  362.         while ( Rulep = Rulep->nextrule );
  363.  
  364.         /*
  365.          * Release memory allocated by parse().
  366.          */
  367.         while ( Fieldcount )
  368.             free( Fields[ --Fieldcount ] );
  369.     }
  370. }
  371.  
  372. /*
  373.  * Miscellaneous functions
  374.  */
  375. int parse( str, wrdlst, delim )
  376. char *str;
  377. char *wrdlst[];
  378. char *delim;
  379. {
  380.     /*
  381.      * Parse the string of words in "str" into the word list at "wrdlst".
  382.      * A "word" is a sequence of characters delimited by one or more
  383.      * of the characters found in the string "delim".
  384.      * Returns the number of words parsed.
  385.      * CAUTION: the memory for the words in "wrdlst" is allocated
  386.      * by malloc() and should eventually be returned by free()...
  387.      */
  388.     int wrdcnt, wrdlen;
  389.     char wrdbuf[ MAXLINELEN ], c;
  390.  
  391.     wrdcnt = 0;
  392.     while ( *str )
  393.     {
  394.         while ( instr( *str, delim ) )
  395.             ++str;
  396.         if ( !*str )
  397.             break;
  398.         wrdlen = 0;
  399.         while ( (c = *str) && !instr( c, delim ) )
  400.         {
  401.             wrdbuf[ wrdlen++ ] = c;
  402.             ++str;
  403.         }
  404.         wrdbuf[ wrdlen++ ] = 0;
  405.         /*
  406.          * NOTE: allocate a MAXLINELEN sized buffer for every
  407.          * word, just in case user wants to copy a larger string
  408.          * into a field.
  409.          */
  410.         wrdlst[ wrdcnt ] = getmem( MAXLINELEN );
  411.         strcpy( wrdlst[ wrdcnt++ ], wrdbuf );
  412.     }
  413.  
  414.     return wrdcnt;
  415. }
  416.  
  417. void unparse( wrdlst, wrdcnt, str, delim )
  418. char *wrdlst[];
  419. int wrdcnt;
  420. char *str;
  421. char *delim;
  422. {
  423.     /*
  424.      * Replace all the words in "str" with the words in "wrdlst",
  425.      * maintaining the same word seperation distance as found in
  426.      * the string.
  427.      * A "word" is a sequence of characters delimited by one or more
  428.      * of the characters found in the string "delim".
  429.      */
  430.     int wc;
  431.     char strbuf[ MAXLINELEN ], *sp, *wp, *start;
  432.  
  433.     wc = 0;        /* next word in "wrdlst" */
  434.     sp = strbuf;    /* points to our local string */
  435.     start = str;    /* save start address of "str" for later... */
  436.     while ( *str )
  437.     {
  438.         /*
  439.          * Copy the field delimiters from the original string to
  440.          * our local version.
  441.          */
  442.         while ( instr( *str, delim ) )
  443.             *sp++ = *str++;
  444.         if ( !*str )
  445.             break;
  446.         /*
  447.          * Skip over the field in the original string and...
  448.          */
  449.         while ( *str && !instr( *str, delim ) )
  450.             ++str;
  451.  
  452.         if ( wc < wrdcnt )
  453.         {
  454.             /*
  455.              * ...copy in the field in the wordlist instead.
  456.              */
  457.             wp = wrdlst[ wc++ ];
  458.             while ( *wp )
  459.                 *sp++ = *wp++;
  460.         }
  461.     }
  462.     /*
  463.      * Tie off the local string, then copy it back to caller's string.
  464.      */
  465.     *sp = 0;
  466.     strcpy( start, strbuf );
  467. }
  468.  
  469. int instr( c, s )
  470. char c, *s;
  471. {
  472.     while ( *s )
  473.         if ( c==*s++ )
  474.             return 1;
  475.     return 0;
  476. }
  477.  
  478. char *getmem( len )
  479. unsigned len;
  480. {
  481.     char *cp;
  482.  
  483.     if (cp = malloc(len)) return cp;
  484.     error( "out of memory", MEM_ERROR );
  485. }
  486.  
  487. void newfile(s)
  488. char *s;
  489. {
  490.     Linecount = 0;
  491.     if ( Filename = s )
  492.     {
  493.         if ( !(Fileptr = fopen( s, "r" )) )
  494.             error( "file not found", FILE_ERROR );
  495.     }
  496.     else
  497.     {
  498.         /*
  499.          * No file name given - process standard input.
  500.          */
  501.         Fileptr = stdin;
  502.         Filename = "standard input";
  503.     }
  504. }
  505.  
  506. int getline()
  507. {
  508.     /*
  509.      * Read a line of text from current input file.     Strip off
  510.      * trailing record seperator (newline).
  511.      */
  512.     int rtn, len;
  513.  
  514.     for ( len=0; len<MAXLINELEN; ++len )
  515.     {
  516.         if ( (rtn = getcharacter()) == *Rcrdsep || rtn == -1 )
  517.             break;
  518.         Linebuf[ len ] = rtn;
  519.     }
  520.     Linebuf[ len ] = 0;
  521.  
  522.     if ( rtn == -1 )
  523.     {
  524.         endfile();
  525.         return 0;
  526.     }
  527.     return 1;
  528. }
  529.  
  530. int getcharacter()
  531. {
  532.     /*
  533.      * Read a character from curren input file.
  534.      * WARNING: your getc() must convert lines that end with CR+LF
  535.      * to LF and EOF marks (like CP/M's ^Z) to a -1.
  536.      * Also, getc() must return a -1 when attempting to read from
  537.      * an unopened file.
  538.      */
  539.     int c;
  540.  
  541.     if ((c = getc(Fileptr)) == *Rcrdsep )
  542.         ++Recordcount;
  543.     if ( c=='\n' )
  544.         ++Linecount;
  545.  
  546.     return c;
  547. }
  548.  
  549. int ungetcharacter( c )
  550. {
  551.     /*
  552.      * Push a character back into the input stream.
  553.      * If the character is a record seperator, or a newline character,
  554.      * the record and line counters are adjusted appropriately.
  555.      */
  556.     if ( c == *Rcrdsep )
  557.         --Recordcount;
  558.     if ( c=='\n' )
  559.         --Linecount;
  560.     return ungetc( c, Fileptr );
  561. }
  562.  
  563. void endfile()
  564. {
  565.     fclose( Fileptr );
  566.     Filename = NULL;  Linecount = 0;
  567. }
  568.  
  569. void error( s, severe )
  570. char *s;
  571. int severe;
  572. {
  573.     if ( Filename )
  574.         fprintf( stderr, "%s:", Filename );
  575.  
  576.     if ( Linecount )
  577.         fprintf( stderr, " line %d:", Linecount );
  578.  
  579.     fprintf( stderr, " %s\n", s );
  580.     if ( severe )
  581.         exit( severe );
  582. }
  583.  
  584. void usage()
  585. {
  586.     error( "Usage: bawk <actfile> [<file> ...]\n", USAGE_ERROR );
  587. }
  588.  
  589. void movemem( from, to, count )
  590. char *from, *to;
  591. int count;
  592. {
  593.     while ( count-- > 0 )
  594.         *to++ = *from++;
  595. }
  596.  
  597. void fillmem( array, count, value )
  598. char *array, value;
  599. int count;
  600. {
  601.     while ( count-- > 0 )
  602.         *array++ = value;
  603. }
  604.  
  605. int alpha( c )
  606. char c;
  607. {
  608.     return isalpha(c) || c == '_';
  609. }
  610.  
  611. int alphanum( c )
  612. char c;
  613. {
  614.     return isalnum(c) || c == '_';
  615. }
  616.