xref: /third_party/toybox/toys/posix/grep.c (revision 0f66f451)
10f66f451Sopenharmony_ci/* grep.c - show lines matching regular expressions
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2013 CE Strake <strake888 at gmail.com>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * Posix doesn't even specify -r, documenting deviations from it is silly.
80f66f451Sopenharmony_ci* echo hello | grep -w ''
90f66f451Sopenharmony_ci* echo '' | grep -w ''
100f66f451Sopenharmony_ci* echo hello | grep -f </dev/null
110f66f451Sopenharmony_ci*
120f66f451Sopenharmony_ci
130f66f451Sopenharmony_ciUSE_GREP(NEWTOY(grep, "(line-buffered)(color):;(exclude-dir)*S(exclude)*M(include)*ZzEFHIab(byte-offset)h(no-filename)ino(only-matching)rRsvwcl(files-with-matches)q(quiet)(silent)e*f*C#B#A#m#x[!wx][!EFw]", TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
140f66f451Sopenharmony_ciUSE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
150f66f451Sopenharmony_ciUSE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN|TOYFLAG_ARGFAIL(2)))
160f66f451Sopenharmony_ci
170f66f451Sopenharmony_ciconfig GREP
180f66f451Sopenharmony_ci  bool "grep"
190f66f451Sopenharmony_ci  default y
200f66f451Sopenharmony_ci  help
210f66f451Sopenharmony_ci    usage: grep [-EFrivwcloqsHbhn] [-ABC NUM] [-m MAX] [-e REGEX]... [-MS PATTERN]... [-f REGFILE] [FILE]...
220f66f451Sopenharmony_ci
230f66f451Sopenharmony_ci    Show lines matching regular expressions. If no -e, first argument is
240f66f451Sopenharmony_ci    regular expression to match. With no files (or "-" filename) read stdin.
250f66f451Sopenharmony_ci    Returns 0 if matched, 1 if no match found, 2 for command errors.
260f66f451Sopenharmony_ci
270f66f451Sopenharmony_ci    -e  Regex to match. (May be repeated.)
280f66f451Sopenharmony_ci    -f  File listing regular expressions to match.
290f66f451Sopenharmony_ci
300f66f451Sopenharmony_ci    file search:
310f66f451Sopenharmony_ci    -r  Recurse into subdirectories (defaults FILE to ".")
320f66f451Sopenharmony_ci    -R  Recurse into subdirectories and symlinks to directories
330f66f451Sopenharmony_ci    -M  Match filename pattern (--include)
340f66f451Sopenharmony_ci    -S  Skip filename pattern (--exclude)
350f66f451Sopenharmony_ci    --exclude-dir=PATTERN  Skip directory pattern
360f66f451Sopenharmony_ci    -I  Ignore binary files
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci    match type:
390f66f451Sopenharmony_ci    -A  Show NUM lines after     -B  Show NUM lines before match
400f66f451Sopenharmony_ci    -C  NUM lines context (A+B)  -E  extended regex syntax
410f66f451Sopenharmony_ci    -F  fixed (literal match)    -a  always text (not binary)
420f66f451Sopenharmony_ci    -i  case insensitive         -m  match MAX many lines
430f66f451Sopenharmony_ci    -v  invert match             -w  whole word (implies -E)
440f66f451Sopenharmony_ci    -x  whole line               -z  input NUL terminated
450f66f451Sopenharmony_ci
460f66f451Sopenharmony_ci    display modes: (default: matched line)
470f66f451Sopenharmony_ci    -c  count of matching lines  -l  show only matching filenames
480f66f451Sopenharmony_ci    -o  only matching part       -q  quiet (errors only)
490f66f451Sopenharmony_ci    -s  silent (no error msg)    -Z  output NUL terminated
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_ci    output prefix (default: filename if checking more than 1 file)
520f66f451Sopenharmony_ci    -H  force filename           -b  byte offset of match
530f66f451Sopenharmony_ci    -h  hide filename            -n  line number of match
540f66f451Sopenharmony_ci
550f66f451Sopenharmony_ciconfig EGREP
560f66f451Sopenharmony_ci  bool
570f66f451Sopenharmony_ci  default y
580f66f451Sopenharmony_ci  depends on GREP
590f66f451Sopenharmony_ci
600f66f451Sopenharmony_ciconfig FGREP
610f66f451Sopenharmony_ci  bool
620f66f451Sopenharmony_ci  default y
630f66f451Sopenharmony_ci  depends on GREP
640f66f451Sopenharmony_ci*/
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_ci#define FOR_grep
670f66f451Sopenharmony_ci#include "toys.h"
680f66f451Sopenharmony_ci#include <regex.h>
690f66f451Sopenharmony_ci
700f66f451Sopenharmony_ciGLOBALS(
710f66f451Sopenharmony_ci  long m, A, B, C;
720f66f451Sopenharmony_ci  struct arg_list *f, *e, *M, *S, *exclude_dir;
730f66f451Sopenharmony_ci  char *color;
740f66f451Sopenharmony_ci
750f66f451Sopenharmony_ci  char *purple, *cyan, *red, *green, *grey;
760f66f451Sopenharmony_ci  struct double_list *reg;
770f66f451Sopenharmony_ci  char indelim, outdelim;
780f66f451Sopenharmony_ci  int found, tried;
790f66f451Sopenharmony_ci)
800f66f451Sopenharmony_ci
810f66f451Sopenharmony_cistruct reg {
820f66f451Sopenharmony_ci  struct reg *next, *prev;
830f66f451Sopenharmony_ci  int rc;
840f66f451Sopenharmony_ci  regex_t r;
850f66f451Sopenharmony_ci  regmatch_t m;
860f66f451Sopenharmony_ci};
870f66f451Sopenharmony_ci
880f66f451Sopenharmony_cistatic void numdash(long num, char dash)
890f66f451Sopenharmony_ci{
900f66f451Sopenharmony_ci  printf("%s%ld%s%c", TT.green, num, TT.cyan, dash);
910f66f451Sopenharmony_ci}
920f66f451Sopenharmony_ci
930f66f451Sopenharmony_ci// Emit line with various potential prefixes and delimiter
940f66f451Sopenharmony_cistatic void outline(char *line, char dash, char *name, long lcount, long bcount,
950f66f451Sopenharmony_ci  unsigned trim)
960f66f451Sopenharmony_ci{
970f66f451Sopenharmony_ci  if (!trim && FLAG(o)) return;
980f66f451Sopenharmony_ci  if (name && FLAG(H)) printf("%s%s%s%c", TT.purple, name, TT.cyan, dash);
990f66f451Sopenharmony_ci  if (FLAG(c)) {
1000f66f451Sopenharmony_ci    printf("%s%ld", TT.grey, lcount);
1010f66f451Sopenharmony_ci    xputc(TT.outdelim);
1020f66f451Sopenharmony_ci  } else if (lcount && FLAG(n)) numdash(lcount, dash);
1030f66f451Sopenharmony_ci  if (bcount && FLAG(b)) numdash(bcount-1, dash);
1040f66f451Sopenharmony_ci  if (line) {
1050f66f451Sopenharmony_ci    if (FLAG(color)) xputsn(FLAG(o) ? TT.red : TT.grey);
1060f66f451Sopenharmony_ci    // support embedded NUL bytes in output
1070f66f451Sopenharmony_ci    xputsl(line, trim);
1080f66f451Sopenharmony_ci    xputc(TT.outdelim);
1090f66f451Sopenharmony_ci  }
1100f66f451Sopenharmony_ci}
1110f66f451Sopenharmony_ci
1120f66f451Sopenharmony_ci// Show matches in one file
1130f66f451Sopenharmony_cistatic void do_grep(int fd, char *name)
1140f66f451Sopenharmony_ci{
1150f66f451Sopenharmony_ci  long lcount = 0, mcount = 0, offset = 0, after = 0, before = 0;
1160f66f451Sopenharmony_ci  struct double_list *dlb = 0;
1170f66f451Sopenharmony_ci  char *bars = 0;
1180f66f451Sopenharmony_ci  FILE *file;
1190f66f451Sopenharmony_ci  int bin = 0;
1200f66f451Sopenharmony_ci
1210f66f451Sopenharmony_ci  if (!FLAG(r)) TT.tried++;
1220f66f451Sopenharmony_ci  if (!fd) name = "(standard input)";
1230f66f451Sopenharmony_ci
1240f66f451Sopenharmony_ci  // Only run binary file check on lseekable files.
1250f66f451Sopenharmony_ci  if (!FLAG(a) && !lseek(fd, 0, SEEK_CUR)) {
1260f66f451Sopenharmony_ci    char buf[256];
1270f66f451Sopenharmony_ci    int len, i = 0;
1280f66f451Sopenharmony_ci    unsigned wc;
1290f66f451Sopenharmony_ci
1300f66f451Sopenharmony_ci    // If the first 256 bytes don't parse as utf8, call it binary.
1310f66f451Sopenharmony_ci    if (0<(len = read(fd, buf, 256))) {
1320f66f451Sopenharmony_ci      lseek(fd, -len, SEEK_CUR);
1330f66f451Sopenharmony_ci      while (i<len) {
1340f66f451Sopenharmony_ci        bin = utf8towc(&wc, buf+i, len-i);
1350f66f451Sopenharmony_ci        if (bin == -2) i = len;
1360f66f451Sopenharmony_ci        if (bin<1) break;
1370f66f451Sopenharmony_ci        i += bin;
1380f66f451Sopenharmony_ci      }
1390f66f451Sopenharmony_ci      bin = i!=len;
1400f66f451Sopenharmony_ci    }
1410f66f451Sopenharmony_ci    if (bin && FLAG(I)) return;
1420f66f451Sopenharmony_ci  }
1430f66f451Sopenharmony_ci
1440f66f451Sopenharmony_ci  if (!(file = fdopen(fd, "r"))) return perror_msg("%s", name);
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_ci  // Loop through lines of input
1470f66f451Sopenharmony_ci  for (;;) {
1480f66f451Sopenharmony_ci    char *line = 0, *start;
1490f66f451Sopenharmony_ci    struct reg *shoe;
1500f66f451Sopenharmony_ci    size_t ulen;
1510f66f451Sopenharmony_ci    long len;
1520f66f451Sopenharmony_ci    int matched = 0, rc = 1;
1530f66f451Sopenharmony_ci
1540f66f451Sopenharmony_ci    // get next line, check and trim delimiter
1550f66f451Sopenharmony_ci    lcount++;
1560f66f451Sopenharmony_ci    errno = 0;
1570f66f451Sopenharmony_ci    ulen = len = getdelim(&line, &ulen, TT.indelim, file);
1580f66f451Sopenharmony_ci    if (errno) perror_msg("%s", name);
1590f66f451Sopenharmony_ci    if (len<1) break;
1600f66f451Sopenharmony_ci    if (line[ulen-1] == TT.indelim) line[--ulen] = 0;
1610f66f451Sopenharmony_ci
1620f66f451Sopenharmony_ci    // Prepare for next line
1630f66f451Sopenharmony_ci    start = line;
1640f66f451Sopenharmony_ci    if (TT.reg) for (shoe = (void *)TT.reg; shoe; shoe = shoe->next)
1650f66f451Sopenharmony_ci      shoe->rc = 0;
1660f66f451Sopenharmony_ci
1670f66f451Sopenharmony_ci    // Loop to handle multiple matches in same line
1680f66f451Sopenharmony_ci    do {
1690f66f451Sopenharmony_ci      regmatch_t *mm = (void *)toybuf;
1700f66f451Sopenharmony_ci
1710f66f451Sopenharmony_ci      // Handle "fixed" (literal) matches
1720f66f451Sopenharmony_ci      if (FLAG(F)) {
1730f66f451Sopenharmony_ci        struct arg_list *seek, fseek;
1740f66f451Sopenharmony_ci        char *s = 0;
1750f66f451Sopenharmony_ci
1760f66f451Sopenharmony_ci        for (seek = TT.e; seek; seek = seek->next) {
1770f66f451Sopenharmony_ci          if (FLAG(x)) {
1780f66f451Sopenharmony_ci            if (!(FLAG(i) ? strcasecmp : strcmp)(seek->arg, line)) s = line;
1790f66f451Sopenharmony_ci          } else if (!*seek->arg) {
1800f66f451Sopenharmony_ci            // No need to set fseek.next because this will match every line.
1810f66f451Sopenharmony_ci            seek = &fseek;
1820f66f451Sopenharmony_ci            fseek.arg = s = line;
1830f66f451Sopenharmony_ci          } else if (FLAG(i)) s = strcasestr(start, seek->arg);
1840f66f451Sopenharmony_ci          else s = strstr(start, seek->arg);
1850f66f451Sopenharmony_ci
1860f66f451Sopenharmony_ci          if (s) break;
1870f66f451Sopenharmony_ci        }
1880f66f451Sopenharmony_ci
1890f66f451Sopenharmony_ci        if (s) {
1900f66f451Sopenharmony_ci          rc = 0;
1910f66f451Sopenharmony_ci          mm->rm_so = (s-start);
1920f66f451Sopenharmony_ci          mm->rm_eo = (s-start)+strlen(seek->arg);
1930f66f451Sopenharmony_ci        } else rc = 1;
1940f66f451Sopenharmony_ci
1950f66f451Sopenharmony_ci      // Handle regex matches
1960f66f451Sopenharmony_ci      } else {
1970f66f451Sopenharmony_ci        int baseline = mm->rm_eo;
1980f66f451Sopenharmony_ci
1990f66f451Sopenharmony_ci        mm->rm_so = mm->rm_eo = INT_MAX;
2000f66f451Sopenharmony_ci        rc = 1;
2010f66f451Sopenharmony_ci        for (shoe = (void *)TT.reg; shoe; shoe = shoe->next) {
2020f66f451Sopenharmony_ci
2030f66f451Sopenharmony_ci          // Do we need to re-check this regex?
2040f66f451Sopenharmony_ci          if (!shoe->rc) {
2050f66f451Sopenharmony_ci            shoe->m.rm_so -= baseline;
2060f66f451Sopenharmony_ci            shoe->m.rm_eo -= baseline;
2070f66f451Sopenharmony_ci            if (!matched || shoe->m.rm_so<0)
2080f66f451Sopenharmony_ci              shoe->rc = regexec0(&shoe->r, start, ulen-(start-line), 1,
2090f66f451Sopenharmony_ci                                  &shoe->m, start==line ? 0 : REG_NOTBOL);
2100f66f451Sopenharmony_ci          }
2110f66f451Sopenharmony_ci
2120f66f451Sopenharmony_ci          // If we got a match, is it a _better_ match?
2130f66f451Sopenharmony_ci          if (!shoe->rc && (shoe->m.rm_so < mm->rm_so ||
2140f66f451Sopenharmony_ci              (shoe->m.rm_so == mm->rm_so && shoe->m.rm_eo >= mm->rm_eo)))
2150f66f451Sopenharmony_ci          {
2160f66f451Sopenharmony_ci            mm = &shoe->m;
2170f66f451Sopenharmony_ci            rc = 0;
2180f66f451Sopenharmony_ci          }
2190f66f451Sopenharmony_ci        }
2200f66f451Sopenharmony_ci      }
2210f66f451Sopenharmony_ci
2220f66f451Sopenharmony_ci      if (!rc && FLAG(x))
2230f66f451Sopenharmony_ci        if (mm->rm_so || line[mm->rm_eo]) rc = 1;
2240f66f451Sopenharmony_ci
2250f66f451Sopenharmony_ci      if (!rc && FLAG(w)) {
2260f66f451Sopenharmony_ci        char c = 0;
2270f66f451Sopenharmony_ci
2280f66f451Sopenharmony_ci        if ((start+mm->rm_so)!=line) {
2290f66f451Sopenharmony_ci          c = start[mm->rm_so-1];
2300f66f451Sopenharmony_ci          if (!isalnum(c) && c != '_') c = 0;
2310f66f451Sopenharmony_ci        }
2320f66f451Sopenharmony_ci        if (!c) {
2330f66f451Sopenharmony_ci          c = start[mm->rm_eo];
2340f66f451Sopenharmony_ci          if (!isalnum(c) && c != '_') c = 0;
2350f66f451Sopenharmony_ci        }
2360f66f451Sopenharmony_ci        if (c) {
2370f66f451Sopenharmony_ci          start += mm->rm_so+1;
2380f66f451Sopenharmony_ci          continue;
2390f66f451Sopenharmony_ci        }
2400f66f451Sopenharmony_ci      }
2410f66f451Sopenharmony_ci
2420f66f451Sopenharmony_ci      if (FLAG(v)) {
2430f66f451Sopenharmony_ci        if (FLAG(o)) {
2440f66f451Sopenharmony_ci          if (rc) {
2450f66f451Sopenharmony_ci            mm->rm_so = 0;
2460f66f451Sopenharmony_ci            mm->rm_eo = ulen-(start-line);
2470f66f451Sopenharmony_ci          } else if (!mm->rm_so) {
2480f66f451Sopenharmony_ci            start += mm->rm_eo;
2490f66f451Sopenharmony_ci            continue;
2500f66f451Sopenharmony_ci          } else mm->rm_eo = mm->rm_so;
2510f66f451Sopenharmony_ci        } else {
2520f66f451Sopenharmony_ci          if (!rc) break;
2530f66f451Sopenharmony_ci          mm->rm_eo = ulen-(start-line);
2540f66f451Sopenharmony_ci        }
2550f66f451Sopenharmony_ci        mm->rm_so = 0;
2560f66f451Sopenharmony_ci      } else if (rc) break;
2570f66f451Sopenharmony_ci
2580f66f451Sopenharmony_ci      // At least one line we didn't print since match while -ABC active
2590f66f451Sopenharmony_ci      if (bars) {
2600f66f451Sopenharmony_ci        xputs(bars);
2610f66f451Sopenharmony_ci        bars = 0;
2620f66f451Sopenharmony_ci      }
2630f66f451Sopenharmony_ci      matched++;
2640f66f451Sopenharmony_ci      TT.found = 1;
2650f66f451Sopenharmony_ci      if (FLAG(q)) {
2660f66f451Sopenharmony_ci        toys.exitval = 0;
2670f66f451Sopenharmony_ci        xexit();
2680f66f451Sopenharmony_ci      }
2690f66f451Sopenharmony_ci      if (FLAG(l)) {
2700f66f451Sopenharmony_ci        xprintf("%s%c", name, TT.outdelim);
2710f66f451Sopenharmony_ci        free(line);
2720f66f451Sopenharmony_ci        fclose(file);
2730f66f451Sopenharmony_ci        return;
2740f66f451Sopenharmony_ci      }
2750f66f451Sopenharmony_ci
2760f66f451Sopenharmony_ci      if (!FLAG(c)) {
2770f66f451Sopenharmony_ci        long bcount = 1 + offset + (start-line) + (FLAG(o) ? mm->rm_so : 0);
2780f66f451Sopenharmony_ci
2790f66f451Sopenharmony_ci        if (bin) printf("Binary file %s matches\n", name);
2800f66f451Sopenharmony_ci        else if (FLAG(o))
2810f66f451Sopenharmony_ci          outline(start+mm->rm_so, ':', name, lcount, bcount,
2820f66f451Sopenharmony_ci                  mm->rm_eo-mm->rm_so);
2830f66f451Sopenharmony_ci        else {
2840f66f451Sopenharmony_ci          while (dlb) {
2850f66f451Sopenharmony_ci            struct double_list *dl = dlist_pop(&dlb);
2860f66f451Sopenharmony_ci            unsigned *uu = (void *)(dl->data+(strlen(dl->data)|3)+1);
2870f66f451Sopenharmony_ci
2880f66f451Sopenharmony_ci            outline(dl->data, '-', name, lcount-before, uu[0]+1, uu[1]);
2890f66f451Sopenharmony_ci            free(dl->data);
2900f66f451Sopenharmony_ci            free(dl);
2910f66f451Sopenharmony_ci            before--;
2920f66f451Sopenharmony_ci          }
2930f66f451Sopenharmony_ci
2940f66f451Sopenharmony_ci          if (matched==1)
2950f66f451Sopenharmony_ci            outline(FLAG(color) ? 0 : line, ':', name, lcount, bcount, ulen);
2960f66f451Sopenharmony_ci          if (FLAG(color)) {
2970f66f451Sopenharmony_ci            xputsn(TT.grey);
2980f66f451Sopenharmony_ci            if (mm->rm_so) xputsl(line, mm->rm_so);
2990f66f451Sopenharmony_ci            xputsn(TT.red);
3000f66f451Sopenharmony_ci            xputsl(line+mm->rm_so, mm->rm_eo-mm->rm_so);
3010f66f451Sopenharmony_ci          }
3020f66f451Sopenharmony_ci
3030f66f451Sopenharmony_ci          if (TT.A) after = TT.A+1;
3040f66f451Sopenharmony_ci        }
3050f66f451Sopenharmony_ci      }
3060f66f451Sopenharmony_ci
3070f66f451Sopenharmony_ci      start += mm->rm_eo;
3080f66f451Sopenharmony_ci      if (mm->rm_so == mm->rm_eo) break;
3090f66f451Sopenharmony_ci      if (!FLAG(o) && FLAG(color)) break;
3100f66f451Sopenharmony_ci    } while (*start);
3110f66f451Sopenharmony_ci    offset += len;
3120f66f451Sopenharmony_ci
3130f66f451Sopenharmony_ci    if (matched) {
3140f66f451Sopenharmony_ci      // Finish off pending line color fragment.
3150f66f451Sopenharmony_ci      if (FLAG(color) && !FLAG(o)) {
3160f66f451Sopenharmony_ci        xputsn(TT.grey);
3170f66f451Sopenharmony_ci        if (ulen > start-line) xputsl(start, ulen-(start-line));
3180f66f451Sopenharmony_ci        xputc(TT.outdelim);
3190f66f451Sopenharmony_ci      }
3200f66f451Sopenharmony_ci      mcount++;
3210f66f451Sopenharmony_ci    } else {
3220f66f451Sopenharmony_ci      int discard = (after || TT.B);
3230f66f451Sopenharmony_ci
3240f66f451Sopenharmony_ci      if (after && --after) {
3250f66f451Sopenharmony_ci        outline(line, '-', name, lcount, 0, ulen);
3260f66f451Sopenharmony_ci        discard = 0;
3270f66f451Sopenharmony_ci      }
3280f66f451Sopenharmony_ci      if (discard && TT.B) {
3290f66f451Sopenharmony_ci        unsigned *uu, ul = (ulen|3)+1;
3300f66f451Sopenharmony_ci
3310f66f451Sopenharmony_ci        line = xrealloc(line, ul+8);
3320f66f451Sopenharmony_ci        uu = (void *)(line+ul);
3330f66f451Sopenharmony_ci        uu[0] = offset-len;
3340f66f451Sopenharmony_ci        uu[1] = ulen;
3350f66f451Sopenharmony_ci        dlist_add(&dlb, line);
3360f66f451Sopenharmony_ci        line = 0;
3370f66f451Sopenharmony_ci        if (++before>TT.B) {
3380f66f451Sopenharmony_ci          struct double_list *dl;
3390f66f451Sopenharmony_ci
3400f66f451Sopenharmony_ci          dl = dlist_pop(&dlb);
3410f66f451Sopenharmony_ci          free(dl->data);
3420f66f451Sopenharmony_ci          free(dl);
3430f66f451Sopenharmony_ci          before--;
3440f66f451Sopenharmony_ci        } else discard = 0;
3450f66f451Sopenharmony_ci      }
3460f66f451Sopenharmony_ci      // If we discarded a line while displaying context, show bars before next
3470f66f451Sopenharmony_ci      // line (but don't show them now in case that was last match in file)
3480f66f451Sopenharmony_ci      if (discard && mcount) bars = "--";
3490f66f451Sopenharmony_ci    }
3500f66f451Sopenharmony_ci    free(line);
3510f66f451Sopenharmony_ci
3520f66f451Sopenharmony_ci    if (FLAG(m) && mcount >= TT.m) break;
3530f66f451Sopenharmony_ci  }
3540f66f451Sopenharmony_ci
3550f66f451Sopenharmony_ci  if (FLAG(c)) outline(0, ':', name, mcount, 0, 1);
3560f66f451Sopenharmony_ci
3570f66f451Sopenharmony_ci  // loopfiles will also close the fd, but this frees an (opaque) struct.
3580f66f451Sopenharmony_ci  fclose(file);
3590f66f451Sopenharmony_ci  while (dlb) {
3600f66f451Sopenharmony_ci    struct double_list *dl = dlist_pop(&dlb);
3610f66f451Sopenharmony_ci
3620f66f451Sopenharmony_ci    free(dl->data);
3630f66f451Sopenharmony_ci    free(dl);
3640f66f451Sopenharmony_ci  }
3650f66f451Sopenharmony_ci}
3660f66f451Sopenharmony_ci
3670f66f451Sopenharmony_cistatic void parse_regex(void)
3680f66f451Sopenharmony_ci{
3690f66f451Sopenharmony_ci  struct arg_list *al, *new, *list = NULL;
3700f66f451Sopenharmony_ci  char *s, *ss;
3710f66f451Sopenharmony_ci
3720f66f451Sopenharmony_ci  // Add all -f lines to -e list. (Yes, this is leaking allocation context for
3730f66f451Sopenharmony_ci  // exit to free. Not supporting nofork for this command any time soon.)
3740f66f451Sopenharmony_ci  al = TT.f ? TT.f : TT.e;
3750f66f451Sopenharmony_ci  while (al) {
3760f66f451Sopenharmony_ci    if (TT.f) s = ss = xreadfile(al->arg, 0, 0);
3770f66f451Sopenharmony_ci    else s = ss = al->arg;
3780f66f451Sopenharmony_ci
3790f66f451Sopenharmony_ci    // Split lines at \n, add individual lines to new list.
3800f66f451Sopenharmony_ci    do {
3810f66f451Sopenharmony_ci// TODO: NUL terminated input shouldn't split -e at \n
3820f66f451Sopenharmony_ci      ss = strchr(s, '\n');
3830f66f451Sopenharmony_ci      if (ss) *(ss++) = 0;
3840f66f451Sopenharmony_ci      new = xmalloc(sizeof(struct arg_list));
3850f66f451Sopenharmony_ci      new->next = list;
3860f66f451Sopenharmony_ci      new->arg = s;
3870f66f451Sopenharmony_ci      list = new;
3880f66f451Sopenharmony_ci      s = ss;
3890f66f451Sopenharmony_ci    } while (ss && *s);
3900f66f451Sopenharmony_ci
3910f66f451Sopenharmony_ci    // Advance, when we run out of -f switch to -e.
3920f66f451Sopenharmony_ci    al = al->next;
3930f66f451Sopenharmony_ci    if (!al && TT.f) {
3940f66f451Sopenharmony_ci      TT.f = 0;
3950f66f451Sopenharmony_ci      al = TT.e;
3960f66f451Sopenharmony_ci    }
3970f66f451Sopenharmony_ci  }
3980f66f451Sopenharmony_ci  TT.e = list;
3990f66f451Sopenharmony_ci
4000f66f451Sopenharmony_ci  if (!FLAG(F)) {
4010f66f451Sopenharmony_ci    // Convert regex list
4020f66f451Sopenharmony_ci    for (al = TT.e; al; al = al->next) {
4030f66f451Sopenharmony_ci      struct reg *shoe;
4040f66f451Sopenharmony_ci
4050f66f451Sopenharmony_ci      if (FLAG(o) && !*al->arg) continue;
4060f66f451Sopenharmony_ci      dlist_add_nomalloc(&TT.reg, (void *)(shoe = xmalloc(sizeof(struct reg))));
4070f66f451Sopenharmony_ci      xregcomp(&shoe->r, al->arg,
4080f66f451Sopenharmony_ci               (REG_EXTENDED*!!FLAG(E))|(REG_ICASE*!!FLAG(i)));
4090f66f451Sopenharmony_ci    }
4100f66f451Sopenharmony_ci    dlist_terminate(TT.reg);
4110f66f451Sopenharmony_ci  }
4120f66f451Sopenharmony_ci}
4130f66f451Sopenharmony_ci
4140f66f451Sopenharmony_cistatic int do_grep_r(struct dirtree *new)
4150f66f451Sopenharmony_ci{
4160f66f451Sopenharmony_ci  struct arg_list *al;
4170f66f451Sopenharmony_ci  char *name;
4180f66f451Sopenharmony_ci
4190f66f451Sopenharmony_ci  if (!new->parent) TT.tried++;
4200f66f451Sopenharmony_ci  if (!dirtree_notdotdot(new)) return 0;
4210f66f451Sopenharmony_ci  if (S_ISDIR(new->st.st_mode)) {
4220f66f451Sopenharmony_ci    for (al = TT.exclude_dir; al; al = al->next)
4230f66f451Sopenharmony_ci      if (!fnmatch(al->arg, new->name, 0)) return 0;
4240f66f451Sopenharmony_ci    return DIRTREE_RECURSE|(FLAG(R)?DIRTREE_SYMFOLLOW:0);
4250f66f451Sopenharmony_ci  }
4260f66f451Sopenharmony_ci  if (TT.S || TT.M) {
4270f66f451Sopenharmony_ci    for (al = TT.S; al; al = al->next)
4280f66f451Sopenharmony_ci      if (!fnmatch(al->arg, new->name, 0)) return 0;
4290f66f451Sopenharmony_ci
4300f66f451Sopenharmony_ci    if (TT.M) {
4310f66f451Sopenharmony_ci      for (al = TT.M; al; al = al->next)
4320f66f451Sopenharmony_ci        if (!fnmatch(al->arg, new->name, 0)) break;
4330f66f451Sopenharmony_ci
4340f66f451Sopenharmony_ci      if (!al) return 0;
4350f66f451Sopenharmony_ci    }
4360f66f451Sopenharmony_ci  }
4370f66f451Sopenharmony_ci
4380f66f451Sopenharmony_ci  // "grep -r onefile" doesn't show filenames, but "grep -r onedir" should.
4390f66f451Sopenharmony_ci  if (new->parent && !FLAG(h)) toys.optflags |= FLAG_H;
4400f66f451Sopenharmony_ci
4410f66f451Sopenharmony_ci  name = dirtree_path(new, 0);
4420f66f451Sopenharmony_ci  do_grep(openat(dirtree_parentfd(new), new->name, 0), name);
4430f66f451Sopenharmony_ci  free(name);
4440f66f451Sopenharmony_ci
4450f66f451Sopenharmony_ci  return 0;
4460f66f451Sopenharmony_ci}
4470f66f451Sopenharmony_ci
4480f66f451Sopenharmony_civoid grep_main(void)
4490f66f451Sopenharmony_ci{
4500f66f451Sopenharmony_ci  char **ss = toys.optargs;
4510f66f451Sopenharmony_ci
4520f66f451Sopenharmony_ci  if (FLAG(color) && (!TT.color || !strcmp(TT.color, "auto")) && !isatty(1))
4530f66f451Sopenharmony_ci    toys.optflags &= ~FLAG_color;
4540f66f451Sopenharmony_ci
4550f66f451Sopenharmony_ci  if (FLAG(color)) {
4560f66f451Sopenharmony_ci    TT.purple = "\033[35m";
4570f66f451Sopenharmony_ci    TT.cyan = "\033[36m";
4580f66f451Sopenharmony_ci    TT.red = "\033[1;31m";
4590f66f451Sopenharmony_ci    TT.green = "\033[32m";
4600f66f451Sopenharmony_ci    TT.grey = "\033[0m";
4610f66f451Sopenharmony_ci  } else TT.purple = TT.cyan = TT.red = TT.green = TT.grey = "";
4620f66f451Sopenharmony_ci
4630f66f451Sopenharmony_ci  if (FLAG(R)) toys.optflags |= FLAG_r;
4640f66f451Sopenharmony_ci
4650f66f451Sopenharmony_ci  // Grep exits with 2 for errors
4660f66f451Sopenharmony_ci  toys.exitval = 2;
4670f66f451Sopenharmony_ci
4680f66f451Sopenharmony_ci  if (!TT.A) TT.A = TT.C;
4690f66f451Sopenharmony_ci  if (!TT.B) TT.B = TT.C;
4700f66f451Sopenharmony_ci
4710f66f451Sopenharmony_ci  TT.indelim = '\n' * !FLAG(z);
4720f66f451Sopenharmony_ci  TT.outdelim = '\n' * !FLAG(Z);
4730f66f451Sopenharmony_ci
4740f66f451Sopenharmony_ci  // Handle egrep and fgrep
4750f66f451Sopenharmony_ci  if (*toys.which->name == 'e') toys.optflags |= FLAG_E;
4760f66f451Sopenharmony_ci  if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
4770f66f451Sopenharmony_ci
4780f66f451Sopenharmony_ci  if (!TT.e && !TT.f) {
4790f66f451Sopenharmony_ci    if (!*ss) error_exit("no REGEX");
4800f66f451Sopenharmony_ci    TT.e = xzalloc(sizeof(struct arg_list));
4810f66f451Sopenharmony_ci    TT.e->arg = *(ss++);
4820f66f451Sopenharmony_ci    toys.optc--;
4830f66f451Sopenharmony_ci  }
4840f66f451Sopenharmony_ci
4850f66f451Sopenharmony_ci  parse_regex();
4860f66f451Sopenharmony_ci
4870f66f451Sopenharmony_ci  if (!FLAG(h) && toys.optc>1) toys.optflags |= FLAG_H;
4880f66f451Sopenharmony_ci
4890f66f451Sopenharmony_ci  if (FLAG(s)) {
4900f66f451Sopenharmony_ci    close(2);
4910f66f451Sopenharmony_ci    xopen_stdio("/dev/null", O_RDWR);
4920f66f451Sopenharmony_ci  }
4930f66f451Sopenharmony_ci
4940f66f451Sopenharmony_ci  if (FLAG(r)) {
4950f66f451Sopenharmony_ci    // Iterate through -r arguments. Use "." as default if none provided.
4960f66f451Sopenharmony_ci    for (ss = *ss ? ss : (char *[]){".", 0}; *ss; ss++) {
4970f66f451Sopenharmony_ci      if (!strcmp(*ss, "-")) do_grep(0, *ss);
4980f66f451Sopenharmony_ci      else dirtree_read(*ss, do_grep_r);
4990f66f451Sopenharmony_ci    }
5000f66f451Sopenharmony_ci  } else loopfiles_rw(ss, O_RDONLY|WARN_ONLY, 0, do_grep);
5010f66f451Sopenharmony_ci  if (TT.tried >= toys.optc || (FLAG(q)&&TT.found)) toys.exitval = !TT.found;
5020f66f451Sopenharmony_ci}
503