home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / lambda / soundpot / f / grep2.lbr / GREP.CZ / GREP.C
Encoding:
C/C++ Source or Header  |  1993-10-26  |  20.5 KB  |  908 lines

  1. /*
  2.  * grep.c -- Get Regular Expression and Print
  3.  *
  4.  * To get instructions for use invoke as:
  5.  *    grep ?
  6.  *
  7.  * The following was extensively modified for use with BDS C.
  8.  * However, the code which constructs a match pattern and that which
  9.  * checks for a match within a line (i.e the hard part) suffered only
  10.  * minor cosmetic changes.
  11.  *
  12.  * Functionally, the pattern mark ": " was changed to ":_" to avoid an
  13.  * inconvience when using MicroShell.
  14.  *
  15.  * By the way, if you are running CP/M 2.x and are not running MicroShell,
  16.  * you are doing yourself a great dis-service.  Among MANY other things,
  17.  * it makes "users" useful.  No, I have no financial interest in MicroShell.
  18.  *
  19.  * H.R.Moran, Jr. 2/28/83
  20.  *
  21.  * P.S.
  22.  *    If you are not convinced in re Microshell, you will want to uncomment
  23.  * the #include "dio.h" and undefine MICROSHELL to get output redirection
  24.  * internal to grep.  Input re-direction doesn't work in spite of what the
  25.  * instructions for use say.  I haven't missed it.
  26.  *                        H.R.M.
  27.  */
  28.  
  29. /*
  30.  *
  31.  *
  32.  * The  information  in  this  document  is  subject  to  change
  33.  * without  notice  and  should not be construed as a commitment
  34.  * by Digital Equipment Corporation or by DECUS.
  35.  * 
  36.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  37.  * assume any responsibility for the use or reliability of  this
  38.  * document or the described software.
  39.  * 
  40.  *      Copyright (C) 1980, DECUS
  41.  * 
  42.  * 
  43.  * General permission to copy or modify, but not for profit,  is
  44.  * hereby  granted,  provided that the above copyright notice is
  45.  * included and reference made to  the  fact  that  reproduction
  46.  * privileges were granted by DECUS.
  47.  *
  48.  */
  49.  
  50.  
  51.  
  52. #include <bdscio.h>
  53. /*
  54.  * #include "dio.h"
  55.  */
  56. #define MICROSHELL    /* don't use dio, Microshell will provide it */
  57.  
  58.  
  59. #define FILE struct _buf
  60. #define BOOL int    /* Boolean i.e. YES or NO */
  61. #define PROC int    /* PROCedure i.e. function returning no value */
  62.  
  63. #define YES 1
  64. #define NO 0
  65. #define NIL 0        /* pointer to nothing */
  66.  
  67.     /*
  68.      * CP/M Stuff
  69.      */
  70.  
  71. #define TBUF 0x80    /* Address of the Transient BUFfer */
  72. #define MAXENTRIES 256    /* max # of file name entries permitted at a time */
  73. #define NAMSIZ      8
  74. #define TYPSIZ      3
  75. #define NAMESIZE (NAMSIZ+TYPSIZ+4) /* chars in a full file name */
  76. #define FCB struct file_control_block
  77.  
  78.  
  79. FCB {        /* CP/M File Control Block structure */
  80.   char    drive;
  81.   char    name[NAMSIZ];
  82.   char    type[TYPSIZ];
  83.   char    extent;
  84.   char    s[2];
  85.   char    rec_cnt;
  86.   char    d[16];
  87.   char  reccnt;
  88.   char  r[3];    /* I think this is required to use setfcb() */
  89.   };
  90.  
  91. #define setdisk(d)    bdos(14, d)      /* select a disk */
  92. #define srch1st(f)    bdos(17, f)      /* find 1'st match BDOS call */
  93. #define srchnxt(f)    bdos(18, f)      /* find next match BDOS call */
  94. #define getdisk()    bdos(25)      /* find current default disk */
  95. #define setdma(p)    bdos(26, p)      /* set dma address BDOS call */
  96. #define getkey()    (bios(3) & 0x7f)  /* non-echoing getchar() */
  97.  
  98.     /* These definitions from DECUS */
  99.  
  100. #define EOF     (-1)
  101. #define NULL    0    /* I prefer NIL hrm */
  102. #define TRUE    1    /* I prefer YES hrm */
  103. #define FALSE   0    /* I perfer NO  hrm */
  104.  
  105. #define LMAX    512    /* Line Length */
  106. #define PMAX    256    /* Pattern Length */
  107.  
  108.     /*
  109.      * Pattern Tokens
  110.      */
  111.  
  112. #define CHAR    1
  113. #define BOL     2
  114. #define EOL     3
  115. #define ANY     4
  116. #define CLASS   5
  117. #define NCLASS  6
  118. #define STAR    7
  119. #define PLUS    8
  120. #define MINUS   9
  121. #define ALPHA   10
  122. #define DIGIT   11
  123. #define NALPHA  12
  124. #define PUNCT   13
  125. #define RANGE   14
  126. #define ENDPAT  15
  127.  
  128. /*
  129.  * GLOBAL Declarations
  130.  */
  131.  
  132.   char    *mbufcpy();    /* copy a buffer masking msbit of each byte */
  133.   int    strcmp();    /* compare 2 strings */
  134.  
  135.   char  *patt_str;    /* copy of the argv[] which holds specified pattern */
  136.   char    names[MAXENTRIES][NAMESIZE]; /* directory name entry array */
  137.   FCB    fcb, *fcb_ptr;    /* file control block stuff */
  138.   char    curr_disk;    /* currently selected default disk */
  139.   int    num_entries;    /* # of matched file entries in directory */
  140.   int    curr_entry;    /* current matched entry */
  141.  
  142.     /* These declarations from DECUS */
  143.  
  144.   BOOL   cflag;        /* count of matching lines flag */
  145.   BOOL   fflag;        /* file name for matching names flag */
  146.   BOOL   nflag;        /* line number flag */
  147.   BOOL   vflag;        /* inVert i.e. show non-matching lines flag */
  148.   BOOL   debug;        /* print Debugging information */
  149.  
  150.   int   nfile;
  151.   char  *pp;        /* pattern buffer pointer */
  152.   char  *file_name;
  153.   char  lbuf[LMAX];
  154.   char  pbuf[PMAX];    /* Pattern buffer */
  155.  
  156.  
  157. /*
  158.  * main()
  159.  *
  160.  * Collect the options and call the real worker routines
  161.  */
  162.  
  163. PROC
  164. main(argc, argv)
  165.   int argc;
  166.   char *argv[];
  167. {
  168.     FILE *f, fff;
  169.     BOOL gotpattern, gotcha;
  170.     char *p;
  171.     int c, i;
  172.  
  173. #ifndef MICROSHELL
  174.     dioinit(&argc, argv);
  175. #endif
  176.     f = &fff;
  177.     debug = cflag = nflag = vflag = fflag = NO;
  178.     if ( argc <= 1 )
  179.       usage("No arguments");
  180.     if ( argc == 2 && argv[1][0] == '?' && argv[1][1] == 0 ) {
  181.       help();
  182.       exit(1);
  183.       }
  184.     nfile = argc-1;    /* assume they are all file specifications for now */
  185.     gotpattern = NO;
  186.     for ( i = 1; i < argc; ++i ) {
  187.       p = argv[i];
  188.       if ( *p == '-' ) {
  189.         ++p;
  190.         while ( c = *p++ ) {
  191.           switch ( tolower(c ) ) {
  192.         case '?':
  193.               help();
  194.               break;
  195.  
  196.         case 'c':
  197.               cflag = YES;
  198.               break;
  199.  
  200.         case 'd':
  201.               debug = YES;
  202.               break;
  203.  
  204.         case 'f':
  205.               fflag = YES;
  206.               break;
  207.  
  208.         case 'n':
  209.               nflag = YES;
  210.               break;
  211.  
  212.         case 'v':
  213.               vflag = YES;
  214.               break;
  215.  
  216.           default:
  217.               usage("Unknown flag");
  218.           }
  219.         }
  220.       argv[i] = NIL;
  221.       --nfile;
  222.       }
  223.     else if ( ! gotpattern ) {
  224.       patt_str = p;
  225.       compile(p);
  226.       argv[i] = NIL;
  227.       gotpattern = YES;
  228.       --nfile;
  229.       }
  230.     }
  231.     if ( ! gotpattern )
  232.       usage("No pattern");
  233.     fflag ^= ( nfile > 0 );
  234.     for ( i = 1; i < argc; ++i  ) {
  235.       if ( p = argv[i] ) {
  236.         if ( fwild(p) == 0 )
  237.           printf("\nNo file: <%s>", p);
  238.         else {
  239.           for ( gotcha = NO; (file_name = fnext(f)); gotcha = YES )
  240.             grep(f, file_name);
  241.           if ( ! gotcha )
  242.             cant(p);
  243.           }
  244.         }
  245.       }
  246. #ifndef MICROSHELL
  247.     dioflush();
  248. #endif
  249. }
  250.  
  251. /*
  252.  * compile()
  253.  *
  254.  * Compile the pattern into global pbuf[]
  255.  */
  256.  
  257. PROC
  258. compile(source)
  259.   char     *source;    /* Pattern to compile        */
  260. {
  261.     char    *cclass();      /* Compile class routine    */
  262.  
  263.     char    *s;        /* Source string pointer    */
  264.     char    *lp;        /* Last pattern pointer        */
  265.     int    c;          /* Current character        */
  266.     int    o;          /* Temp                */
  267.     char    *spp;       /* Save beginning of pattern    */
  268.  
  269.     s = source;
  270.     if ( debug )
  271.       printf("Pattern = \"%s\"\n", s);
  272.     pp = pbuf;
  273.     while ( c = *s++ ) {
  274.       /*
  275.        * STAR, PLUS and MINUS are special.
  276.        */
  277.       if ( c == '*' || c == '+' || c == '-' ) {
  278.         if ( pp == pbuf || (o=pp[-1]) == BOL ||
  279.             o == EOL || o == STAR ||
  280.             o == PLUS || o == MINUS)
  281.           badpat("Illegal occurrance op.", source, s);
  282.         store(ENDPAT);
  283.         store(ENDPAT);
  284.         spp = pp;            /* Save pattern end    */
  285.         while ( --pp > lp )        /* Move pattern down    */
  286.           *pp = pp[-1];        /* one byte        */
  287.         *pp =   (c == '*') ? STAR :
  288.           (c == '-') ? MINUS : PLUS;
  289.         pp = spp;            /* Restore pattern end  */
  290.         continue;
  291.         }
  292.       /*
  293.        * All the rest.
  294.        */
  295.       lp = pp;            /* Remember start       */
  296.       switch ( c ) {
  297.  
  298.         case '^':
  299.             store(BOL);
  300.             break;
  301.  
  302.         case '$':
  303.             store(EOL);
  304.             break;
  305.  
  306.         case '.':
  307.             store(ANY);
  308.             break;
  309.  
  310.         case '[':
  311.             s = cclass(source, s);
  312.             break;
  313.  
  314.         case ':':
  315.             if ( *s ) {
  316.               c = *s++;
  317.               switch ( tolower(c) ) {
  318.  
  319.                 case 'a':
  320.                       store(ALPHA);
  321.                     break;
  322.  
  323.                 case 'd':
  324.                     store(DIGIT);
  325.                     break;
  326.  
  327.                 case 'n':
  328.                     store(NALPHA);
  329.                     break;
  330.  
  331.                 case '_':
  332.                     store(PUNCT);
  333.                     break;
  334.  
  335.                 default:
  336.                     badpat("Unknown : type", source, s);
  337.  
  338.                 }
  339.               break;
  340.             }
  341.           else
  342.             badpat("No : type", source, s);
  343.  
  344.         case '\\':
  345.             if ( *s )
  346.               c = *s++;
  347.  
  348.         default:
  349.             store(CHAR);
  350.             store(tolower(c));
  351.         }
  352.       }
  353.     store(ENDPAT);
  354.     store(0);                   /* Terminate string     */
  355.     if ( debug ) {
  356.       for ( lp = pbuf; lp < pp; ) {
  357.         if ( (c = (*lp++ & 0xff) ) < ' '  || c > 0x7f )
  358.           printf("\\%02x ", c);
  359.         else
  360.           printf("%c ", c);
  361.         }
  362.         printf("\n");
  363.       }
  364. }
  365.  
  366.  
  367. /*
  368.  * grep()
  369.  *
  370.  * Scan the file for the pattern in pbuf[]
  371.  */
  372.  
  373. PROC
  374. grep(fp, fn)
  375.   FILE        *fp;        /* File to process            */
  376.   char        *fn;        /* File name (for -f option)    */
  377. {
  378.     BOOL m;
  379.     int lno, count;
  380.  
  381.     lno = count = 0;
  382.     while ( fgetss(lbuf, LMAX, fp) ) {
  383.       ++lno;
  384.       if ( kbhit() && (getkey() == '\3') )    /* CTRL-C abort ? */
  385.         exit(1);
  386.       m = match();
  387.       if ( (m && ! vflag) || (! m && vflag) ) {
  388.         ++count;
  389.         if ( ! cflag ) {
  390.           if ( fflag && fn ) {
  391.         file(fn);
  392.         fn = NIL;
  393.             }
  394.           if ( nflag )
  395.         printf("%4d ", lno);
  396.           if ( lbuf[strlen(lbuf) - 1] == '\n' )
  397.             printf("%s", lbuf);
  398.           else
  399.             printf("%s\n", lbuf);
  400.           }
  401.         }
  402.       }
  403.     if ( cflag ) {
  404.       if ( fflag && fn )
  405.         file(fn);
  406.       printf("%d\n", count);
  407.       }
  408. }
  409.  
  410. /*
  411.  * cclass()
  412.  *
  413.  * Compile a class (within [])
  414.  */
  415.  
  416. char *
  417. cclass(source, src)
  418.   char    *source;    /* Pattern start -- for error msg.    */
  419.   char    *src;        /* Class start                */
  420. {
  421.     char    *s;    /* Source pointer            */
  422.     char    *cp;    /* Pattern start            */
  423.     int    c;    /* Current character            */
  424.     int    o;    /* Temp                    */
  425.  
  426.     s = src;
  427.     o = CLASS;
  428.     if ( *s == '^' ) {
  429.       ++s;
  430.       o = NCLASS;
  431.       }
  432.     store(o);
  433.     cp = pp;
  434.     store(0);                   /* Byte count       */
  435.     while ( (c = *s++) && c != ']' ) {
  436.       if ( c == '\\' ) {        /* Store quoted char    */
  437.         if ( (c = *s++) == '\0' ) /* Gotta get something  */
  438.           badpat("Class terminates badly", source, s);
  439.         else
  440.           store(tolower(c));
  441.         }
  442.       else if ( c == '-' && (pp - cp) > 1 && *s != ']' && *s != '\0' ) {
  443.         c = pp[-1];        /* Range start        */
  444.         pp[-1] = RANGE;    /* Range signal        */
  445.         store(c);        /* Re-store start    */
  446.         c = *s++;        /* Get end char and    */
  447.         store(tolower(c));    /* Store it        */
  448.         }
  449.       else {
  450.         store(tolower(c));    /* Store normal char    */
  451.         }
  452.       }
  453.     if ( c != ']' )
  454.       badpat("Unterminated class", source, s);
  455.     if ( (c = (pp - cp)) >= 256 )
  456.       badpat("Class too large", source, s);
  457.     if ( c == 0 )
  458.       badpat("Empty class", source, s);
  459.     *cp = c;
  460.     return (s);
  461. }
  462.  
  463. /*
  464.  * store()
  465.  *
  466.  * append sub-pattern to pattern if there is room
  467.  */
  468.  
  469. PROC
  470. store(op)
  471.   char op;
  472. {
  473.     if ( pp >= &pbuf[PMAX] )
  474.       error("Pattern too complex\n");
  475.     *pp++ = op;
  476. }
  477.  
  478.  
  479. /*
  480.  * badpat()
  481.  *
  482.  * Identify a problem with the search pattern
  483.  */
  484.  
  485. PROC
  486. badpat(message, source, stop)
  487.   char    *message;    /* Error message    */
  488.   char    *source;    /* Pattern start    */
  489.   char    *stop;        /* Pattern end        */
  490. {
  491.     int    c;
  492.  
  493.     printf("-GREP-E-%s, pattern is\"%s\"\n", message, source);
  494.     printf("-GREP-E-Stopped at byte %d, '%c'\n", stop-source, stop[-1]);
  495.     error("?GREP-E-Bad pattern\n");
  496. }
  497.  
  498. /*
  499.  * match()
  500.  *
  501.  * Match the current line (in lbuf[]), return YES if it does.
  502.  */
  503.  
  504. BOOL
  505. match()
  506. {
  507.     char *pmatch();
  508.  
  509.     char   *l;         /* Line pointer         */
  510.  
  511.     for ( l = lbuf; *l; l++ ) {
  512.       if ( pmatch(l, pbuf) != NIL )
  513.         return (YES);
  514.       }
  515.     return (NO);
  516. }
  517.  
  518.  
  519. /*
  520.  * attempt to match a pattern with a line of text
  521.  * if sucessful, return a pointer to the match
  522.  * else return NIL
  523.  *
  524.  * Uses RECURSION
  525.  */
  526.  
  527. char *
  528. pmatch(line, pattern)
  529.   char    *line;        /* (partial) line to match    */
  530.   char    *pattern;    /* (partial) pattern to match    */
  531. {
  532.     char    *l;    /* Current line pointer        */
  533.     char    *p;    /* Current pattern pointer    */
  534.     char    c;    /* Current character        */
  535.     char    *e;    /* End for STAR and PLUS match    */
  536.     int    op;    /* Pattern operation        */
  537.     int    n;    /* Class counter        */
  538.     char    *are;    /* Start of STAR match        */
  539.  
  540.     l = line;
  541.     if ( debug )
  542.       printf("pmatch(\"%s\")\n", line);
  543.     p = pattern;
  544.     while ( (op = *p++) != ENDPAT ) {
  545.       if ( debug )
  546.         printf("byte[%d] = %02x, '%c', op = %02x\n", l-line, *l, *l, op);
  547.       switch ( op ) {
  548.         case CHAR:
  549.             if ( tolower(*l++) != *p++ )
  550.               return (NIL);
  551.             break;
  552.  
  553.         case BOL:
  554.             if ( l != lbuf )
  555.               return (NIL);
  556.             break;
  557.  
  558.         case EOL:
  559.             if ( *l != '\0' )
  560.               return (NIL);
  561.             break;
  562.  
  563.         case ANY:
  564.             if ( *l++ == '\0' )
  565.               return (NIL);
  566.             break;
  567.  
  568.         case DIGIT:
  569.             c = *l++;
  570.             if ( ! isdigit(c) )
  571.               return (NIL);
  572.             break;
  573.  
  574.         case ALPHA:
  575.             c = tolower(*l++);
  576.             if ( ! islower(c) )
  577.               return (NIL);
  578.             break;
  579.  
  580.         case NALPHA:
  581.             c = tolower(*l++);
  582.             if ( ! islower(c) && ! isdigit(c) )
  583.               return (NIL);
  584.             break;
  585.  
  586.         case PUNCT:
  587.             c = *l++;
  588.             if ( c == '\0' || c > ' ' )
  589.               return (NIL);
  590.             break;
  591.  
  592.         case CLASS:
  593.         case NCLASS:
  594.             c = tolower(*l++);
  595.             n = *p++ & 0xff;
  596.             do {
  597.               if ( *p == RANGE ) {
  598.                 p += 3;
  599.                 n -= 2;
  600.                 if ( c >= p[-2] && c <= p[-1] )
  601.                   break;
  602.                 }
  603.               else if ( c == *p++ )
  604.                 break;
  605.               } while ( --n > 1 );
  606.             if ( (op == CLASS) == (n <= 1) )
  607.               return (NIL);
  608.             if ( op == CLASS )
  609.               p += n - 2;
  610.             break;
  611.  
  612.         case MINUS:
  613.             e = pmatch(l, p);       /* Look for a match     */
  614.             while ( *p++ != ENDPAT )  /* Skip over pattern    */
  615.               ;
  616.             if ( e )          /* Got a match?     */
  617.               l = e;      /* Yes, update string   */
  618.             break;          /* Always succeeds      */
  619.  
  620.         case PLUS:              /* One or more ...      */
  621.             if ( (l = pmatch(l, p) ) == 0 )
  622.               return (NIL);      /* Gotta have a match   */
  623.         case STAR:              /* Zero or more ...     */
  624.             are = l;        /* Remember line start  */
  625.             while ( *l && (e = pmatch(l, p)) )
  626.               l = e;      /* Get longest match    */
  627.             while ( *p++ != ENDPAT )  /* Skip over pattern    */
  628.               ;
  629.             while ( l >= are ) {      /* Try to match rest    */
  630.               if ( e = pmatch(l, p) )
  631.                 return (e);
  632.               --l;        /* Nope, try earlier    */
  633.               }
  634.             return (NIL);          /* Nothing else worked  */
  635.  
  636.         default:
  637.             printf("Bad op code %d\n", op);
  638.             error("Cannot happen -- match\n");
  639.         }
  640.       }
  641.     return (l);
  642. }
  643.  
  644. /*
  645.  * file()
  646.  *
  647.  * Identify the file under consideration
  648.  */
  649.  
  650. PROC
  651. file(s)
  652.   char *s;
  653. {
  654.     printf("File %s: Pattern \"%s\"\n", s, patt_str);
  655. }
  656.  
  657. /*
  658.  * cant()
  659.  *
  660.  * Identify the offending file specification
  661.  */
  662.  
  663. PROC
  664. cant(s)
  665.   char *s;
  666. {
  667.     printf("can't find any files matching <%s>\n", s);
  668.     exit(1);
  669. }
  670.  
  671. /*
  672.  * fwild()
  673.  *
  674.  * Construct an array of file names which match
  675.  * the wild card file specification in global array names[]
  676.  * return the number of entries made in names[]
  677.  */
  678.  
  679. fwild(wildname)
  680.   char *wildname;
  681. {
  682.     int    i, j, k;    /* general purpose indices */
  683.  
  684.     num_entries = 0;
  685.     setfcb(&fcb, wildname);
  686.     fcb.extent = '\0';    /* match only first extent */
  687.     fcb.s[0] = fcb.s[1] = '\0'; /* required by some versions of CP/M */
  688.     curr_disk = getdisk();
  689.     if ( ! fcb.drive ) /* no explicit drive spec, make it explicit */
  690.       fcb.drive = curr_disk + 1;
  691.     setdisk(fcb.drive-1);
  692.     setdma(TBUF);        /* set DMA address to tbuff */
  693.     for ( i = srch1st(&fcb); i != 255; i = srchnxt(&fcb),++num_entries ) {
  694.       if ( num_entries >= MAXENTRIES )
  695.         break;
  696.       fcb_ptr = (TBUF + 32*(i & 3));
  697.       fcb2nam(names[num_entries], fcb_ptr, fcb.drive);
  698.       }
  699.     if ( i != 255 )
  700.       printf("\nToo many files, only %d will be processed", MAXENTRIES);
  701.     if ( num_entries > 1 )
  702.       qsort(names, num_entries, NAMESIZE, &strcmp);
  703.     curr_entry = -1;    /* will be incremented BEFORE use */
  704.     if ( debug ) {
  705.       printf("\nDebug of fwild().. it found:\n");
  706.       for ( i = 0; i < num_entries; ++i )
  707.         printf("\n<%s>", names[i]);
  708.       printf("\n");
  709.       }
  710.     setdisk(curr_disk);    /* restore default disk drive */
  711.     return (num_entries);
  712. }
  713.  
  714. /*
  715.  * fnext()
  716.  *
  717.  * close current file -- if there is one open
  718.  * open next one
  719.  * and return a pointer to its name
  720.  */
  721.  
  722. char *
  723. fnext(f)
  724.   FILE *f;
  725. {
  726.     if ( curr_entry >= 0 )
  727.       fclose(f);
  728.     if ( ++curr_entry < num_entries ) {
  729.       if ( fopen(&names[curr_entry], f) < 0 )
  730.         return (NIL);
  731.       return (&names[curr_entry]);
  732.       }
  733.     return (NIL);
  734. }
  735.  
  736. /*
  737.  * error()
  738.  *
  739.  * print a message and die
  740.  */
  741.  
  742. PROC
  743. error(s)
  744. {
  745.     puts(s);
  746.     exit(1);
  747. }
  748.  
  749. /*
  750.  * fgetss()
  751.  *
  752.  * fgets() with a max size of buffer
  753.  * ignore buffer size issue for now (hrm)
  754.  */
  755.  
  756. char *
  757. fgetss(buf, bufsiz, f)
  758.   char *buf;
  759.   int bufsiz;
  760.   FILE *f;
  761. {
  762.  
  763.     return (fgets(buf, f));
  764. }
  765.  
  766. /*
  767.  * fcb2nam()
  768.  *
  769.  * extract the file name from fcb f and put it in string n
  770.  * pre-pend a drive specifier
  771.  */
  772.  
  773. PROC
  774. fcb2nam(n, f, d)
  775.   char *n;        /* name */
  776.   FCB *f;        /* file control block */
  777.   char d;        /* drive */
  778. {
  779.     char c;
  780.     int i;
  781.  
  782.     *n++ = d + '@';
  783.     *n++ = ':';
  784.     for ( i = 0; i < NAMSIZ; ++i, ++n )
  785.       if ( (c = f->name[i]) == ' ' )
  786.         break;
  787.       else
  788.         *n = c;
  789.     *n++ = '.';
  790.     for ( i = 0; i < TYPSIZ; ++i, ++n )
  791.       if ( (c = (f->type[i] & 0x7f)) == ' ' )
  792.         break;
  793.       else
  794.         *n = c;
  795.     *n = '\0';
  796. }
  797.  
  798. /*
  799.  * usage()
  800.  *
  801.  * Give an error message and "how things should be invoked"
  802.  */
  803.  
  804. PROC
  805. usage(s)
  806.   char    *s;
  807. {
  808.     printf("?GREP-E-%s\n", s);
  809.     printf("Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  810.     exit(1);
  811. }
  812.  
  813.  
  814. /*
  815.  * help()
  816.  *
  817.  * Show instructions for use
  818.  */
  819.  
  820. PROC
  821. help()
  822. {
  823.  
  824.     pscrt("grep searches a file for a given pattern.  Execute by\n");
  825.     pscrt("\tgrep [flags] regular_expression file_list\n\n");
  826.     pscrt("Flags are single characters preceeded by '-':\n");
  827.     pscrt("\t-c\tOnly a count of matching lines is printed\n");
  828.     pscrt("\t-f\tPrint file name for matching lines switch, see below\n");
  829.     pscrt("\t-n\tEach line is preceeded by its line number\n");
  830.     pscrt("\t-v\tOnly print non-matching lines\n\n");
  831.     pscrt("The file_list is a list of files");
  832.     pscrt(" (wildcards are acceptable).\n");
  833.     pscrt("If no files are given, input comes from the terminal.");
  834.     pscrt("  There is no prompting.\n");
  835.     pscrt("The file name is normally printed if there is a file given.\n");
  836.     pscrt("The -f flag reverses this action");
  837.     pscrt(" (print name no file, not if more).\n\n");
  838.     pscrt("The regular_expression defines the pattern to search for.");
  839.     pscrt("  Upper- and\n");
  840.     pscrt("lower-case are always ignored.  Blank lines never match.");
  841.     pscrt("  The expression\n");
  842.     pscrt("should be quoted to make it a single argv[].\n");
  843.     pscrt("x\tAn ordinary character (not mentioned below)");
  844.     pscrt(" matches that character.\n");
  845.     pscrt("'\\'\tThe backslash quotes any character.");
  846.     pscrt("  \"\\$\" matches a dollar-sign.\n");
  847.     pscrt("'^'\tA circumflex at the beginning of an expression\n");
  848.     pscrt("\tmatches the beginning of a line.\n");
  849.     pscrt("'$'\tA dollar-sign at the end of an expression matches the");
  850.     pscrt(" end of a line.\n");
  851.     pscrt("'.'\tA period matches any character except \"new-line\".\n\n");
  852.     pscrt("\tA colon matches a class of characters described below\n\n");
  853.     pscrt("\t\t\":a\"\tmatches any alphabetic\n");
  854.     pscrt("\t\t\":d\"\tmatches digits,\n");
  855.     pscrt("\t\t\":n\"\tmatches alphanumerics\n");
  856.     pscrt("\t\t\":_\"\tmatches spaces, tabs, and\n");
  857.     pscrt("\t\t\tother control characters, such as new-line.\n");
  858.     pscrt("'*'\tAn expression followed by an asterisk matches zero or\n");
  859.     pscrt("\tmore occurrances of that expression:\n\n");
  860.     pscrt("\t\t\"fo*\" matches \"f\", \"fo\" \"foo\", etc.\n\n");
  861.     pscrt("'+'\tAn expression followed by a plus sign matches one\n");
  862.     pscrt("\tor more occurrances of that expression:\n\n");
  863.     pscrt("\t\t\"fo+\" matches \"fo\", etc.\n\n");
  864.     pscrt("'-'\tAn expression followed by a minus sign optionally\n");
  865.     pscrt("\tmatches the expression.\n");
  866.     pscrt("'[]'\tA string enclosed in square brackets matches any\n");
  867.     pscrt("\t\tcharacter in that string, but no others.\n\n");
  868.     pscrt("\tIf the first character in the string is a circumflex,\n");
  869.     pscrt("\tthe expression matches any character except \"new-line\"\n");
  870.     pscrt("\tand the characters in the string.  For example, \"[xyz]\"\n");
  871.     pscrt("\tmatches \"xx\" and \"zyx\", while \"[^xyz]\"\n");
  872.     pscrt("\tmatches \"abc\" but not \"axb\".\n");
  873.     pscrt("\tA range of characters may be specified by two characters\n");
  874.     pscrt("\tseperated by \"-\".\n");
  875.     pscrt("\tNote that,");
  876.     pscrt(" [a-z] matches alphabetics, while [z-a] never matches.\n\n");
  877.     pscrt("The concatenation of regular expressions");
  878.     pscrt(" is a regular expression.\n");
  879. }
  880.  
  881.  
  882. /*
  883.  * put a string to the crt (really standard out)
  884.  * count the newlines.  If CRT size is reached, prompt for continuation
  885.  * then continue.
  886.  */
  887.  
  888. #define MAXCRT 22
  889.  
  890. PROC
  891. pscrt(s)
  892.   char *s;
  893. {
  894.     int c;
  895.     char *count;
  896.  
  897.     count = "\0";    /* This is a fakeout to achieve STATIC */
  898.     for ( ; *s; ++s ) {
  899.       putchar(*s);
  900.       if ( *s == '\n' && ++*count >= MAXCRT ) {
  901.         *count = '\0';
  902.         puts("--STRIKE SPACE BAR TO CONTINUE");
  903.         getkey();
  904.         putchar('\r');
  905.         }
  906.       }
  907. }
  908. ny ch