home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / pc / unix / dgrep.arc / DGREP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-03-16  |  21.5 KB  |  866 lines

  1. /**********************************************************************\
  2.  *
  3.  *    DGREP.C
  4.  *
  5.  * Main module for dgrep. Input, output, search strategy selection
  6.  * etc. are done here.
  7.  * For output we use fputs, because it makes possible to use own
  8.  * putstr with Turbo C and MSC under MS-DOS (smaller exe-file).
  9.  *
  10.  * Author: Jarmo Ruuth 6-Feb-1988
  11.  *
  12.  * Copyright (C) 1988-90 by Jarmo Ruuth
  13.  * May be freely copied for any non-commercial usage.
  14. \**********************************************************************/
  15.  
  16. #include <fcntl.h>
  17. #include "system.h"
  18. #include "dfa.h"
  19. #include "dfaregex.h"
  20. #include "bm.h"
  21.  
  22. #define VERSION        "1.71"
  23.  
  24. typedef enum {
  25.     STOP,
  26.     OK
  27. } status_t;
  28.  
  29. typedef enum {
  30.     NONE,        /* no file name displayed */
  31.     BEFORE_LINE,    /* name displayed before each matching line (in same line) */
  32.     BLOCK        /* name displayed only once before matching lines */
  33. } show_fname_t;
  34.  
  35. /* flags and local variables */
  36.  
  37. #ifdef TEST
  38. int            debug        = FALSE;
  39. #endif
  40. static char        nonmatch    = FALSE;
  41. static char        count        = FALSE;
  42. static char        names        = FALSE;
  43. static char        number        = FALSE;
  44. static char        ignorecase    = FALSE;
  45. static char        regmust_only    = FALSE;
  46. static char        exact        = FALSE;
  47. static char        silent        = FALSE;
  48. static char        no_boymoore    = FALSE;
  49. static char        touch        = FALSE;
  50. static char        block_mode    = FALSE;
  51. static int        leading_context    = 0;
  52. static int        trailing_context= 0;
  53.  
  54. static ulong        matchcount;
  55. static ulong        linecount;
  56. static ulong*        linecount_ptr    = NULL;
  57.  
  58. static int        waiting_lines    = 0;
  59. static int        type_bits    = RE_SEARCH;
  60. static char        file_not_found    = FALSE;
  61. static char        touch_failed    = FALSE;
  62. static char        was_match    = FALSE;
  63. static show_fname_t    show_fname    = NONE;
  64. static char        fname_shown    = FALSE;
  65. static char        first_match    = TRUE;
  66. static char*        path        = "";    /* current path and filename */
  67. static int        handle        = 0;    /* current file handle */
  68. static char*        expr        = NULL;
  69. static char*        regmust    = NULL;
  70. static int        regmustlen;
  71. static char        use_normal;
  72.  
  73. /* By default, use time() as a random number generator */
  74. #define drandom()    ((unsigned)time(NULL))
  75.  
  76. #if defined(__TURBOC__)
  77.  
  78. /* Use faster direct dos system calls */
  79.  
  80. #define    open(p,a)    _open(p,O_RDONLY|O_DENYALL)
  81. #define    close(h)    _close(h)
  82. #define    read(h,b,n)    _read(h,b,n)
  83.  
  84. #undef drandom
  85. #define drandom()    get_sec()
  86. extern unsigned get_sec(void);
  87.  
  88. #elif defined(M_I86) && defined(MSDOS) && !defined(OS2)    /* Microsoft C */
  89.  
  90. /* Microsoft C defines MSDOS even in OS/2!, so you need to define
  91.    OS2 yourself. */
  92.  
  93. #include <dos.h>
  94.  
  95. static int    msc_h, msc_cnt;
  96.  
  97. #define    open(p,a)    (_dos_open(p,O_RDONLY,&msc_h)==0 ? msc_h : ERROR)
  98. #define    close(h)    _dos_close(h)
  99. #define    read(h,b,n)    (_dos_read(h,(void far *)(b),n,&msc_cnt)==0 \
  100.             ? msc_cnt : ERROR)
  101. #endif
  102.  
  103. /* Funtext quotations are found from the pep[1-4]grep.doc texts written
  104.    by James A. Woods. Texts came with the fastgrep archive. (Woods is also
  105.    responsible for the original idea to combine bm-search with egrep.) */
  106. static char* funtext[] = {
  107.     
  108.    "The chief defect of Henry King\n"
  109.    "Was chewing little bits of string.\n"
  110.    "\t-- Hilaire Belloc, Cautionary Tales [1907]\n",
  111.  
  112.    "Attempt the end, and never stand to doubt\n"
  113.    "Nothing's so hard but search will find it out.\n"
  114.    "\t-- Robert Herrick, Hesperides [1648]\n",
  115.  
  116.    "Gratiano speaks an infinite deal of nothing, more than any man in all\n"
  117.    "of Venice.  His reasons are as two grains of wheat hid in two bushels of\n"
  118.    "chaff:  you shall seek all day ere you find them, they are not worth\n"
  119.    "the search.\n"
  120.    "\t-- Shakespeare, Merchant of Venice\n",
  121.  
  122.    "Reach out and Boyer-Moore Egrep Someone\n"
  123.    "\t-- James A. Woods\n",
  124.  
  125.    "I need very little,\n"
  126.    "  and of that little,\n"
  127.    "     I need very little.\n"
  128.    "\t-- St. Francis of Assisi\n",
  129.  
  130.    "How long a time lies in one little word!\n"
  131.    "\t-- Shakespeare, Richard II, I, iii\n",
  132.  
  133.    "Fine words butter no parsnips.\n"
  134.    "\t-- Southern proverb\n"
  135. };
  136.  
  137. #define NFUNTEXT    (sizeof(funtext) / sizeof(funtext[0]))
  138.  
  139. static char* long_usage_line = {
  140. "Usage: dgrep [options] {-f expression file | [-e] expression} [file...]\n"
  141. };
  142.  
  143. static char* short_usage_line = {
  144. "Version " VERSION ", dgrep -h for full help\n"
  145. };
  146.  
  147. static char* options1 = {
  148. "Options: -An  n lines after the matching line are printed\n"
  149.       "\t -Bn  n lines before the matching line are printed\n"
  150.       "\t -b   filename is displayed only once before matches\n"
  151.       "\t -d   only dfa is used for searching\n"
  152.       "\t -c   only a count of matching lines is printed\n"
  153.       "\t -i   case insensitive match\n"
  154.       "\t -l   only names of files with matching lines are printed\n"
  155.       "\t -n   each line is preceded by its relative line number in file\n"
  156.       "\t -s   silent mode, nothing is printed except error messages\n"
  157. };
  158.  
  159. /* split options for MSC */
  160. static char* options2 = {
  161. #ifdef TOUCH
  162.       "\t -t   all files that contain matches are touched\n"
  163. #endif
  164.       "\t -v   all lines but those matching are printed\n"
  165.       "\t -x   exact, all characters in expression are taken literally\n"
  166.       "\t -1-9 1-9 lines before and after the matching line are printed\n"
  167.       "\t -e expression, useful when expression begins with -\n"
  168.       "\t -f file that contains expression\n"
  169. };
  170.  
  171. static char* regexps = {
  172. "Regular expressions:\t\t\t"        "."    "\tany single character\n"
  173. "*"    "\tzero or more repeats\t\t"    "(...)"    "\tgrouping\n"
  174. "+"    "\tone or more repeats\t\t"    "^"    "\tbeginning of line\n"
  175. "?"    "\tzero or one repeat\t\t"    "$"    "\tend of line\n"
  176. "[...]"    "\tany character in set\t\t"    "\\c"    "\tquote special character c\n"
  177. "[^...]""\tany character not in set\t"    "|"    "\talternative (\"or\")\n"
  178. };
  179.  
  180. /**********************************************************************
  181.  *
  182.  *    short_usage
  183.  *
  184.  * Exits with options help message and funtext.
  185.  */
  186. static void short_usage(void)
  187. {
  188.     fputs(long_usage_line, stdout);
  189.     fputs(short_usage_line, stdout);
  190.     fputs(options1, stdout);
  191.     fputs(options2, stdout);
  192.     fputs(funtext[drandom() % NFUNTEXT], stdout);
  193.     exit(2);
  194. }
  195.  
  196. /**********************************************************************
  197.  *
  198.  *    long_usage
  199.  *
  200.  * Exits with long help message.
  201.  */
  202. static void long_usage(void)
  203. {
  204.     fputs(long_usage_line, stdout);
  205.     fputs(options1, stdout);
  206.     fputs(options2, stdout);
  207.     fputs(regexps, stdout);
  208.     exit(2);
  209. }
  210.  
  211. /**********************************************************************
  212.  *
  213.  *    quote_expr
  214.  */
  215. static char* quote_expr(REG1 char* expr, unsigned len)
  216. {
  217.     REG2 char*    p;
  218.     REG3 char*    ptr;
  219.     
  220.     p = malloc(2 * len + 1);
  221.     if (p == NULL)
  222.         error("Out of memory", 3);
  223.     ptr = p;
  224.     while (*expr) {
  225.         *p++ = '\\';
  226.         *p++ = *expr++;
  227.     }
  228.     *p = '\0';
  229.     return ptr;
  230. }
  231.  
  232. /**********************************************************************
  233.  *
  234.  *    str_toupper
  235.  */
  236. static void str_toupper(REG1 uchar* s)
  237. {
  238.     REG2 unsigned ch;
  239.     
  240.     for (; (ch = *s) != 0; s++) {
  241.         if (islower(ch))
  242.             *s = toupper(ch);
  243.     }
  244. }
  245.  
  246. /**********************************************************************
  247.  *
  248.  *    memrchr
  249.  */
  250. static char* memrchr(REG1 char* b, REG3 char c, REG2 unsigned n)
  251. {
  252.     for (; n--; b--) {
  253.         if (*b == c)
  254.             return b;
  255.     }
  256.     return NULL;
  257. }
  258.  
  259. /**********************************************************************
  260.  *
  261.  *    show_number
  262.  */
  263. static void show_number(ulong number)
  264. {
  265.     char    number_buffer[10];
  266.  
  267.     if (show_fname == BEFORE_LINE)
  268.         fputs(":", stdout);
  269.     fputs(ultoa(number, number_buffer, 10), stdout);
  270. }
  271.  
  272. /**********************************************************************
  273.  *
  274.  *    show_file
  275.  */
  276. #define show_file(p)    fputs(p, stdout)
  277.  
  278. /**********************************************************************
  279.  *
  280.  *    show_line
  281.  *
  282.  * Shows line followed by '\n'.
  283.  */
  284. static void show_line(char* line, FILE *f)
  285. {
  286.     fputs(line, f);
  287.     fputs("\n", f);
  288. }
  289.  
  290. #ifdef TOUCH
  291. /**********************************************************************
  292.  *
  293.  *    touch_file
  294.  */
  295. static void touch_file(char* fname, int h)
  296. {
  297.     if (!set_file_time(fname, h)) {
  298.         fputs("Warning: Can't touch file ", stderr);
  299.         show_line(fname, stderr);
  300.         touch_failed = TRUE;
  301.     }
  302. }
  303. #endif    /* TOUCH */
  304.  
  305. /**********************************************************************
  306.  *
  307.  *    print_line
  308.  */
  309. static char* print_line(char* beg, REG1 char* end, ulong lineno)
  310. {
  311.     REG3 int    c;
  312.     
  313.     c = *end;
  314.     *end = '\0';
  315.     if (show_fname == BEFORE_LINE)
  316.         show_file(path);
  317.     else if (show_fname == BLOCK && !fname_shown) {
  318.         fputs("*** File ", stdout);
  319.         fputs(path, stdout);
  320.         fputs(":\n", stdout);
  321.         fname_shown = TRUE;
  322.     }
  323.     if (number)
  324.         show_number(lineno);
  325.     if (show_fname == BEFORE_LINE || number)
  326.         fputs(":", stdout);
  327.     show_line(beg, stdout);
  328.     *end = c;
  329.     return end + NEOL(end);
  330. }
  331.  
  332. /**********************************************************************
  333.  *
  334.  *    get_leading_bytes
  335.  */
  336. static int get_leading_bytes(char* bufbeg, char* bufend, int* nlines)
  337. {
  338.     int    nfound = 0;
  339.     char*    ptr = bufend;
  340.     
  341.     for (; ; ptr--) {
  342.         if (ptr <= bufbeg) {
  343.             *nlines = nfound;
  344.             return bufend - bufbeg + 1;
  345.         }
  346.         if (*ptr == EOL1) {
  347.             nfound++;
  348.             if (nfound > *nlines)
  349.                 break;
  350.         }
  351.     }
  352.     *nlines = nfound - 1;
  353.     ptr += NEOL(ptr);
  354.     return bufend - ptr + 1;
  355. }
  356.  
  357. /**********************************************************************
  358.  *
  359.  *    print_context
  360.  */
  361. static int print_context(char* bufbeg, char* bufend, int nlines, ulong lineno)
  362. {
  363.     char*    ptr;
  364.     char*    linebeg;
  365.  
  366.     ptr = linebeg = bufbeg;
  367.     for (; nlines && ptr <= bufend; ptr++)
  368.         if (*ptr == EOL1) {
  369.             print_line(linebeg, ptr, lineno++);
  370.             nlines--;
  371.             linebeg = ptr + NEOL(ptr);
  372.         }
  373.     return nlines;
  374. }
  375.  
  376. /**********************************************************************
  377.  *
  378.  *    print_trailing_context
  379.  */
  380. #define print_trailing_context(p1, p2) \
  381.     print_context((p1), (p2), trailing_context, linecount+1)
  382.  
  383. /**********************************************************************
  384.  *
  385.  *    print_leading_context
  386.  */
  387. static void print_leading_context(char* bufbeg, char* bufend)
  388. {
  389.     int    bytes;
  390.     int    nlines;
  391.     
  392.     if (bufbeg >= bufend)
  393.         return;
  394.     nlines = leading_context;
  395.     bytes = get_leading_bytes(bufbeg, bufend, &nlines);
  396.     print_context(bufend-bytes+1, bufend, leading_context,
  397.         linecount-nlines);
  398. }
  399.  
  400. /**********************************************************************
  401.  *
  402.  *    check flags
  403.  */
  404. static status_t check_flags(char* beg, char* end, char* bufend)
  405. {
  406.     was_match = TRUE;
  407. #ifdef TOUCH
  408.     if (touch)
  409.         touch_file(path, handle);
  410. #endif
  411.     if (names) {
  412.         if (!silent) {
  413.             show_file(path);
  414.             fputs("\n", stdout);
  415.         }
  416.         return STOP;
  417.     }
  418.     if (count) {
  419.         matchcount++;
  420.         return OK;
  421.     }
  422.     if (silent)
  423.         return OK;
  424.     if (!first_match && (leading_context || trailing_context))
  425.         fputs("----------\n", stdout);
  426.     first_match = FALSE;
  427.     if (leading_context)
  428.         print_leading_context(buffer, beg-1);
  429.     print_line(beg, end, linecount);
  430.     if (trailing_context)
  431.         waiting_lines = print_trailing_context(end+NEOL(end), bufend);
  432.     if (number)
  433.         linecount++;
  434.     return OK;
  435. }
  436.  
  437. /**********************************************************************
  438.  *
  439.  *    normal_dgrep
  440.  *
  441.  * When use_normal is TRUE, use this. This version separates
  442.  * lines from buffer and then searches pattern from that line.
  443.  */
  444. static int normal_dgrep(char* buf, char* bufend, REG4 int bufsize)
  445. {
  446.     REG3 char*    line = buf;    /* current line */
  447.     REG2 char*    le;        /* line end */
  448.     REG1 char*    match;
  449.  
  450.     while ((le = memchr(line, EOL1, bufsize)) != NULL) {
  451.         if (regmust) {
  452.             match = boyer_moore(line,le,regmust,regmustlen);
  453.             if (match != NULL && !regmust_only)
  454.                 match = reg_exec(line, le, NULL);
  455.         } else
  456.             match = reg_exec(line, le, NULL);
  457.         if ((match && !nonmatch) || (!match && nonmatch)) {
  458.             if (check_flags(line, le, bufend) == STOP)
  459.                 return -1;
  460.         } else
  461.             linecount++;
  462.         le += NEOL(le);
  463.         if ((bufsize -= (le-line)) <= 0)
  464.             return 0;
  465.         line = le;
  466.     }
  467.     return bufsize;
  468. }
  469.  
  470. /**********************************************************************
  471.  *
  472.  *    fast_dgrep
  473.  *
  474.  * Searches pattern from the whole buffer, and when a match is found, 
  475.  * makes a line from the match position.
  476.  */
  477. static int fast_dgrep(char* buf, REG3 char* bufend, int bufsize)
  478. {
  479.     REG1 char*    matchptr = buf;
  480.     REG2 char*    linebeg;    /* beginning of current line */
  481.  
  482.     for (;;) {
  483.         if (regmust)
  484.             matchptr=boyer_moore(matchptr,bufend,regmust,regmustlen);
  485.         else {
  486.             matchptr = reg_exec(matchptr, bufend, linecount_ptr);
  487.             if (matchptr > buf && matchptr[0] == EOL1
  488.                 && matchptr[-1] != EOL2)
  489.                 matchptr--;
  490.         }
  491.         if (matchptr == NULL)
  492.             break;
  493.         if ((linebeg=memrchr(matchptr, EOL2, matchptr-buf)) == NULL)
  494.             linebeg = buf-1;
  495.         ++linebeg;
  496.         /* below matchptr points end of line */
  497.         matchptr = memchr(matchptr, EOL1, bufend-matchptr);
  498.         if (matchptr == NULL)
  499.             matchptr = bufend;
  500.         if (regmust_only || !regmust        /* match found or ...*/
  501.            || reg_exec(linebeg,matchptr,NULL) != NULL)    /* verify ok */
  502.             if (check_flags(linebeg, matchptr, bufend) == STOP)
  503.                 return -1;
  504.         matchptr += NEOL(matchptr);
  505.         if (matchptr > bufend)
  506.             break;
  507.     }
  508.     return (buf+bufsize)-(bufend+NEOL(bufend));
  509. }
  510.  
  511. /**********************************************************************
  512.  *
  513.  *    dgrep_buffer
  514.  */
  515. static int dgrep_buffer(char* buf, int bufsize)
  516. {
  517.     char*    bufend;
  518.     
  519.     /* bufend is last EOL1 in buffer */
  520.     if ((bufend=memrchr(buf+bufsize-1, EOL1, bufsize)) == NULL) {
  521.         fputs("Warning: No line separator in buffer", stderr);
  522.         if (show_fname != NONE) {
  523.             fputs(" in file ", stderr);
  524.             show_line(path, stderr);
  525.         } else
  526.             fputs("\n", stderr);
  527.         return bufsize;
  528.     }
  529.     if (waiting_lines) {
  530.         print_context(buf, bufend, waiting_lines, 
  531.             linecount+trailing_context-waiting_lines+1);
  532.         waiting_lines = 0;
  533.     }
  534.     return use_normal ? normal_dgrep(buf, bufend, bufsize)
  535.               : fast_dgrep(buf, bufend, bufsize);
  536. }
  537.  
  538. /**********************************************************************
  539.  *
  540.  *    add_last_newline_if
  541.  *
  542.  * Checks that buffer ends with an EOL character. If it doesn't, adds one
  543.  * at the end of buffer and returns 1. Otherwise returns 0. Routine
  544.  * assemes that there is enough space for the added EOL character and
  545.  * that bufsize is at least one.
  546.  */
  547. static int add_last_newline_if(char* buffer, int bufsize)
  548. {
  549. #if EOL1 == EOL2
  550.     if (buffer[bufsize-1] == EOL1)
  551.         return 0;
  552.     else {
  553.         buffer[bufsize] = EOL1;
  554.         return 1;
  555.     }
  556. #else
  557.     /* a more complicated case, check if last char is EOL1 */
  558.     if (buffer[bufsize-1] == EOL1)
  559.         return 0;
  560.     /* check if one char (that can't be EOL1) */
  561.     if (bufsize == 1) {
  562.         buffer[bufsize] = EOL1;
  563.         return 1;
  564.     }
  565.     /* check if EOL1-EOL2-pair at the end */
  566.     if (buffer[bufsize-2] == EOL1 && buffer[bufsize-1] == EOL2)
  567.         return 0;
  568.     /* otherwise there isn't terminating EOL1 */
  569.     buffer[bufsize] = EOL1;
  570.     return 1;
  571. #endif
  572. }
  573.  
  574. /**********************************************************************
  575.  *
  576.  *    align
  577.  *
  578.  * If the buffer size is larger than ALIGN, align it. Using alignment we
  579.  * can always read full I/O device blocks which potentially makes reading
  580.  * faster. If ALIGN is 1 then no alignment is done.
  581.  */
  582. static int align(int bufsize)
  583. {
  584.     if (bufsize < ALIGN)
  585.         return bufsize;
  586.     else
  587.         return (bufsize / ALIGN) * ALIGN;
  588. }
  589.  
  590. /**********************************************************************
  591.  *
  592.  *    dgrep
  593.  *
  594.  * Greps previously opened handle h.
  595.  */
  596. static void dgrep(int h)
  597. {
  598.     REG1 int    bufsize;
  599.     REG2 int    nleftover = 0;
  600.     REG3 int    leading_bytes = 0;
  601.     int        nlines;
  602.     int        nread;
  603.  
  604.     linecount = 1L;        /* first line number is 1 */
  605.     matchcount = 0L;
  606.     waiting_lines = 0;
  607.     fname_shown = FALSE;
  608.     if (show_fname == BLOCK)    /* reset context match flag */
  609.         first_match = TRUE;
  610.     nread = align(maxbuf);
  611.     while ((bufsize = read(h, buffer+leading_bytes+nleftover, nread)) > 0)
  612.     {
  613.         /* update nread to contain all bytes in the buffer */
  614.         nread += leading_bytes+nleftover;
  615.         bufsize += nleftover;
  616.         if (bufsize + leading_bytes < nread)    /* not full buffer */
  617.             bufsize += add_last_newline_if(buffer, bufsize + leading_bytes);
  618.         nleftover = dgrep_buffer(buffer+leading_bytes, bufsize);
  619.             if (nleftover < 0)
  620.                 break;
  621.             else if (nleftover == bufsize)
  622.                 nleftover = 0;
  623.             else {
  624.                 bufsize += leading_bytes;
  625.             if (leading_context && bufsize == nread) {
  626.                 nlines = leading_context;
  627.                 leading_bytes = 
  628.                   get_leading_bytes(
  629.                       buffer,
  630.                       buffer+bufsize-nleftover-1,
  631.                       &nlines);
  632.             } else
  633.                 leading_bytes = 0;
  634.             memcpy(buffer, buffer+bufsize-leading_bytes-nleftover,
  635.                 leading_bytes+nleftover);
  636.         }
  637.         nread = align(maxbuf-leading_bytes-nleftover);
  638.     }
  639.     if (nleftover > 0) {
  640.         buffer[nleftover++] = EOL1;
  641.         dgrep_buffer(buffer, nleftover);
  642.     }
  643.     if (!silent && count && !names) {
  644.         if (show_fname != NONE)
  645.             show_file(path);
  646.         show_number(matchcount);
  647.         fputs("\n", stdout);
  648.     }
  649. }
  650.  
  651. /**********************************************************************
  652.  *
  653.  *    dgrep_file
  654.  *
  655.  * Greps file defined in src.
  656.  */
  657. static void dgrep_file(REG1 char* src)
  658. {
  659.     REG2 int h;    /* file handle */
  660.  
  661.     path = src;    /* for filename display */
  662.     if ((h=open(src,O_RDONLY|O_BINARY)) != ERROR) {
  663.         handle = h;
  664.         dgrep(h);
  665.         close(h);
  666.     } else {
  667.         fputs("Warning: Can't open file ", stderr);
  668.         show_line(src, stderr);
  669.         file_not_found = TRUE;
  670.     }
  671. }
  672.  
  673. /**********************************************************************
  674.  *
  675.  *    read_exp
  676.  *
  677.  * Reads an expression from a file.
  678.  */
  679. static uchar* read_exp(REG1 char* str)
  680. {
  681.     REG2 int    bufsize;
  682.     REG3 int    h, len;
  683.  
  684.     if ((h = open(str, O_RDONLY|O_BINARY)) == ERROR)
  685.         error("Can't open expression file",2);
  686.     if ((bufsize = read(h, buffer, maxbuf)) == 0)
  687.         error("Empty expression file",2);
  688.     if (bufsize == ERROR)
  689.         error("Error when reading expression file",3);
  690.     buffer[bufsize] = '\0';
  691.     if ((str = memchr(buffer,EOL1,bufsize)) != NULL)
  692.         *str = '\0';
  693.     len = strlen(buffer);
  694.     if (len == 0)
  695.         error("Empty expression in expression file",2);
  696.     if ((str = malloc(len+1)) == NULL)
  697.         error("Out of memory",3);
  698.     return strcpy(str, buffer);
  699. }
  700.  
  701. /**********************************************************************
  702.  *
  703.  *    get_args
  704.  */
  705. static int get_args(REG1 int argc, char* argv[])
  706. {
  707.     REG2 int    opt;
  708.     extern int    optind, opterr;
  709.     extern char*    optarg;
  710.     extern    int    getopt(int argc, char* argv[], char* optionS);
  711.  
  712.     opterr = FALSE;    /* handle errors ourselves */
  713.     while ((opt = getopt(argc, argv, "bdchilnrstvx123456789A:B:e:f:D")) != EOF) {
  714.         switch (opt) {
  715.             case 'A':
  716.                 trailing_context = (unsigned)atoi(optarg);
  717.                 break;
  718.             case 'B':
  719.                 leading_context = (unsigned)atoi(optarg);
  720.                 break;
  721.             case 'b':
  722.                 block_mode = TRUE;
  723.                 break;
  724.             case 'd':
  725.                 no_boymoore = TRUE;
  726.                 break;
  727.             case 'c':
  728.                 count = TRUE;
  729.                 break;
  730.             case 'h':
  731.                 long_usage();    /* and exit */
  732.             case 'i':
  733.                 ignorecase = TRUE;
  734.                 type_bits |= RE_IGNORECASE;
  735.                 break;
  736.             case 'l':
  737.                 names = TRUE;
  738.                 break;
  739.             case 'n':
  740.                 number = TRUE;
  741.                 linecount_ptr = &linecount;
  742.                 break;
  743.             case 's':
  744.                 silent = TRUE;
  745.                 break;
  746.             case 't':
  747.                 touch = names = TRUE;
  748.                 break;
  749.             case 'v':
  750.                 nonmatch = TRUE;
  751.                 break;
  752.             case 'x':
  753.                 exact = TRUE;
  754.                 break;
  755.             case 'e':
  756.                 expr = optarg;
  757.                 break;
  758.             case 'f':
  759.                 expr = read_exp(optarg);
  760.                 break;
  761.             case '1':
  762.             case '2':
  763.             case '3':
  764.             case '4':
  765.             case '5':
  766.             case '6':
  767.             case '7':
  768.             case '8':
  769.             case '9':
  770.                 leading_context = trailing_context = opt - '0';
  771.                 break;
  772. #ifdef TEST
  773.             case 'D':
  774.                 debug++;
  775.                 break;
  776. #endif
  777.             default:
  778.                 error("Invalid command line option, "
  779.                     "dgrep -h for help", 2);
  780.         }
  781.     }
  782.     if (count || names || silent || touch)
  783.         leading_context = trailing_context = 0;
  784.     if (names || touch || silent)
  785.         block_mode = FALSE;
  786.     return optind;
  787. }
  788.  
  789. /**********************************************************************
  790.  *
  791.  *    main
  792.  */
  793. int main(REG1 int argc, char* argv[])
  794. {
  795.     REG2 int    optind;
  796.     REG3 char*    s;
  797.     REG4 int    len, nfile;
  798.     
  799. #ifdef PROFILE
  800.     prof_start(argv[0]);
  801. #endif
  802.     if (argc == 1)
  803.         short_usage();
  804.     alloc_buffer();    /* Allocate I/O buffer */
  805.     optind = get_args (argc, argv);
  806.     if (optind >= argc && !expr)
  807.         short_usage();
  808.     if (!expr)
  809.         expr = argv[optind++];
  810. #ifdef BSD
  811.     /* It is faster to count lines with dfa in Bsd. (Bsd doesn't
  812.        have system memchr, and my version is too slow?) In PC
  813.        current method is faster. I don't know about other systems. */
  814.     if (number)
  815.         no_boymoore = TRUE;
  816. #endif
  817.     len = strlen(expr);
  818.     if (exact && (no_boymoore || len > MAXREGMUST)) {
  819.         /* Bm can handle only MAXREGMUST long patterns with
  820.            N+M worst case. So we verify match with reg_exec,
  821.            which does it in N+M time. (We use bm with the
  822.            regmust pattern from reg_comp.) */
  823.         expr = quote_expr(expr, len);
  824.         exact = FALSE;
  825.     }
  826.     if (!exact) {
  827.         s = reg_comp_eol(expr, EOL1, EOL2, type_bits);
  828.         if (s != NULL)
  829.             error(s, 2);
  830.     }
  831.     if (!no_boymoore)
  832.         regmust = exact ? expr : rbuf.regmust;
  833.     if (regmust) {
  834.         if (ignorecase) {
  835.             str_toupper(regmust);
  836.             str_toupper(expr);
  837.         }
  838.         regmust_only = (strcmp(regmust,expr) == 0);
  839.         regmustlen = strlen(regmust);
  840.         if (!regmust_only && regmustlen <= 1)
  841.             regmust = NULL;
  842.         else
  843.             gosper(regmust, regmustlen, ignorecase);
  844.     }
  845.     use_normal = nonmatch || (regmust && number);
  846.     nfile = argc - optind;
  847.     if (nfile > 0) {
  848.         if (block_mode)
  849.             show_fname = BLOCK;
  850.         else
  851.             show_fname = BEFORE_LINE;
  852.         while (optind < argc)
  853.             dgrep_file(argv[optind++]);
  854.     } else
  855.         dgrep(fileno(stdin));
  856. #ifdef TEST
  857.     if (debug) {
  858.         printf("maxbuf = %d\n", maxbuf);
  859.         show_dfa_report();
  860.         show_boyer_moore_report();
  861.         fflush(stdout);
  862.     }
  863. #endif
  864.     return (file_not_found || touch_failed) ? 2 : was_match ? 0 : 1;
  865. }
  866.