1e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake/* grep.c - print lines what match given regular expression
2e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake *
3e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake * Copyright 2013 CE Strake <strake888 at gmail.com>
4e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake *
5dad378bb17f1910014383bf196e87028e98d81d6Rob Landley * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html
673e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley *
773e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley * TODO: -ABC
8e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
936aed25f3f54f7871d5810adfe99d96944e0afb4Rob LandleyUSE_GREP(NEWTOY(grep, "C#B#A#ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
10c49b9a97559d2009e9076e063b2e9acbc13fb9efRob LandleyUSE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
11c49b9a97559d2009e9076e063b2e9acbc13fb9efRob LandleyUSE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
12e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
13e999ca008416e3d41c1079bcb4d151b43c95dc3aStrakeconfig GREP
14e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake  bool "grep"
15205b496e42ceb72bf0755fec4f4675a467c401e1Rob Landley  default y
16e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake  help
17b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley    usage: grep [-EFivwcloqsHbhn] [-A NUM] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
18f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
19f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    Show lines matching regular expressions. If no -e, first argument is
20f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    regular expression to match. With no files (or "-" filename) read stdin.
21f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    Returns 0 if matched, 1 if no match found.
22f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
23f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -e  Regex to match. (May be repeated.)
24f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -f  File containing regular expressions to match.
25f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
26f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    match type:
2736aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -A  Show NUM lines after     -B  Show NUM lines before match
2836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -C  NUM lines context (A+B)  -E  extended regex syntax
2936aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -F  fixed (literal match)    -i  case insensitive
3036aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -m  match MAX many lines     -r  recursive (on dir)
3136aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -v  invert match             -w  whole word (implies -E)
3236aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    -x  whole line               -z  input NUL terminated
33f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
34f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    display modes: (default: matched line)
35f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -c  count of matching lines  -l  show matching filenames
36f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -o  only matching part       -q  quiet (errors only)
37d3657e9e8846e11f376fcb6fae60b15dae853479Rob Landley    -s  silent (no error msg)    -Z  output NUL terminated
38f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
39d3657e9e8846e11f376fcb6fae60b15dae853479Rob Landley    output prefix (default: filename if checking more than 1 file)
40f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -H  force filename           -b  byte offset of match
41f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley    -h  hide filename            -n  line number of match
42c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley
43c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landleyconfig EGREP
44c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  bool
45c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  default y
46c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  depends on GREP
47c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley
48c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landleyconfig FGREP
49c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  bool
50c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  default y
51c49b9a97559d2009e9076e063b2e9acbc13fb9efRob Landley  depends on GREP
52e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake*/
53e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
54e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake#define FOR_grep
55e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake#include "toys.h"
56e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake#include <regex.h>
57e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
58e999ca008416e3d41c1079bcb4d151b43c95dc3aStrakeGLOBALS(
59dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  long m;
60dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  struct arg_list *f;
61dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  struct arg_list *e;
62b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley  long a;
6336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley  long b;
6436aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley  long c;
65c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley
66c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  char indelim, outdelim;
67e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake)
68e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
69c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley// Emit line with various potential prefixes and delimiter
70c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landleystatic void outline(char *line, char dash, char *name, long lcount, long bcount,
71c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  int trim)
72c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley{
73c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  if (name && (toys.optflags&FLAG_H)) printf("%s%c", name, dash);
74c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  if (!line || (lcount && (toys.optflags&FLAG_n)))
75c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley    printf("%ld%c", lcount, line ? dash : TT.outdelim);
76c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  if (bcount && (toys.optflags&FLAG_b)) printf("%ld%c", bcount-1, dash);
7793e27d0d4723d57082463c4d70e6443060a8732bRob Landley  if (line) xprintf("%.*s%c", trim ? trim : INT_MAX/2, line, TT.outdelim);
78c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley}
79c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley
8073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley// Show matches in one file
81f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landleystatic void do_grep(int fd, char *name)
82f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley{
8336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley  struct double_list *dlb = 0;
84dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  FILE *file = fdopen(fd, "r");
85c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  long lcount = 0, mcount = 0, offset = 0, after = 0, before = 0;
86c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  char *bars = 0;
87e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
8831f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley  if (!fd) name = "(standard input)";
8931f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley
90dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  if (!file) {
91d3a435e53c94ec25b4ae5fa2614f49ef8884e08aRob Landley    perror_msg_raw(name);
92dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    return;
93dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  }
94dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
9573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley  // Loop through lines of input
96e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake  for (;;) {
976c64f5f186d26d4c95d408979d33831935e026f1Rob Landley    char *line = 0, *start;
9873e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    regmatch_t matches;
99dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    size_t unused;
100dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    long len;
101030970bd70e14849b0f2598bfa0982402fc7e2e7Rob Landley    int mmatch = 0;
102e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
103dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    lcount++;
104c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley    if (0 > (len = getdelim(&line, &unused, TT.indelim, file))) break;
105c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley    if (line[len-1] == TT.indelim) line[len-1] = 0;
106dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
107dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    start = line;
108e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
10973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    // Loop through matches in this line
11073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    do {
111dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      int rc = 0, skip = 0;
112dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
11373e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley      // Handle non-regex matches
114dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      if (toys.optflags & FLAG_F) {
115fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley        struct arg_list *seek, fseek;
116dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley        char *s = 0;
117dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
118dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley        for (seek = TT.e; seek; seek = seek->next) {
119e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley          if (toys.optflags & FLAG_x) {
120e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley            int i = (toys.optflags & FLAG_i);
121e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley
122e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley            if ((i ? strcasecmp : strcmp)(seek->arg, line)) s = line;
123e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley          } else if (!*seek->arg) {
124fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley            seek = &fseek;
125fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley            fseek.arg = s = line;
126fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley            break;
127fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley          }
128f435f0412aa4ca631aa178d10ed33008e34f37cbRob Landley          if (toys.optflags & FLAG_i) s = strnstr(line, seek->arg);
129f435f0412aa4ca631aa178d10ed33008e34f37cbRob Landley          else s = strstr(line, seek->arg);
130dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley          if (s) break;
131dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley        }
132dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
133dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley        if (s) {
13473e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          matches.rm_so = (s-line);
13573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          skip = matches.rm_eo = (s-line)+strlen(seek->arg);
136dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley        } else rc = 1;
137dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      } else {
13873e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        rc = regexec((regex_t *)toybuf, start, 1, &matches,
139dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley                     start==line ? 0 : REG_NOTBOL);
14073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        skip = matches.rm_eo;
141dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      }
142dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
143dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      if (toys.optflags & FLAG_x)
14473e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        if (matches.rm_so || line[matches.rm_eo]) rc = 1;
14573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley
14673e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley      if (!rc && (toys.optflags & FLAG_w)) {
14773e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        char c = 0;
14873e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley
14973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        if ((start+matches.rm_so)!=line) {
15073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          c = start[matches.rm_so-1];
15173e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          if (!isalnum(c) && c != '_') c = 0;
15273e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        }
15373e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        if (!c) {
15473e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          c = start[matches.rm_eo];
15573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          if (!isalnum(c) && c != '_') c = 0;
15673e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        }
15773e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        if (c) {
15873e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          start += matches.rm_so+1;
15973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley
16073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          continue;
16173e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        }
16273e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley      }
163dad378bb17f1910014383bf196e87028e98d81d6Rob Landley
164dad378bb17f1910014383bf196e87028e98d81d6Rob Landley      if (toys.optflags & FLAG_v) {
165dad378bb17f1910014383bf196e87028e98d81d6Rob Landley        if (toys.optflags & FLAG_o) {
16673e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          if (rc) skip = matches.rm_eo = strlen(start);
16773e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          else if (!matches.rm_so) {
168dad378bb17f1910014383bf196e87028e98d81d6Rob Landley            start += skip;
169dad378bb17f1910014383bf196e87028e98d81d6Rob Landley            continue;
17073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          } else matches.rm_eo = matches.rm_so;
171dad378bb17f1910014383bf196e87028e98d81d6Rob Landley        } else {
172dad378bb17f1910014383bf196e87028e98d81d6Rob Landley          if (!rc) break;
17373e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley          matches.rm_eo = strlen(start);
174dad378bb17f1910014383bf196e87028e98d81d6Rob Landley        }
17573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        matches.rm_so = 0;
176dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      } else if (rc) break;
1776c64f5f186d26d4c95d408979d33831935e026f1Rob Landley
17836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      // At least one line we didn't print since match while -ABC active
179b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley      if (bars) {
180b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley        xputs(bars);
181b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley        bars = 0;
182b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley      }
183030970bd70e14849b0f2598bfa0982402fc7e2e7Rob Landley      mmatch++;
184e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley      toys.exitval = 0;
185e6e685cdb8f6e30607c25dcac5f8dc93d4230c20Rob Landley      if (toys.optflags & FLAG_q) xexit();
186dad378bb17f1910014383bf196e87028e98d81d6Rob Landley      if (toys.optflags & FLAG_l) {
187c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley        xprintf("%s%c", name, TT.outdelim);
1886c64f5f186d26d4c95d408979d33831935e026f1Rob Landley        free(line);
1896c64f5f186d26d4c95d408979d33831935e026f1Rob Landley        fclose(file);
1906c64f5f186d26d4c95d408979d33831935e026f1Rob Landley        return;
191dad378bb17f1910014383bf196e87028e98d81d6Rob Landley      }
192fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley      if (toys.optflags & FLAG_o)
19373e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley        if (matches.rm_eo == matches.rm_so)
194fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley          break;
195fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
1966c64f5f186d26d4c95d408979d33831935e026f1Rob Landley      if (!(toys.optflags & FLAG_c)) {
197c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley        long bcount = 1 + offset + (start-line) +
198c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley          ((toys.optflags & FLAG_o) ? matches.rm_so : 0);
199c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley
200b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley        if (!(toys.optflags & FLAG_o)) {
20136aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          while (dlb) {
20236aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley            struct double_list *dl = dlist_pop(&dlb);
20336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley
204c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley            outline(dl->data, '-', name, lcount-before, 0, 0);
20536aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley            free(dl->data);
20636aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley            free(dl);
20736aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley            before--;
20836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          }
20936aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley
210c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley          outline(line, ':', name, lcount, bcount, 0);
21136aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          if (TT.a) after = TT.a+1;
212c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley        } else outline(start+matches.rm_so, ':', name, lcount, bcount,
213c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley                       matches.rm_eo-matches.rm_so);
214e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake      }
215dad378bb17f1910014383bf196e87028e98d81d6Rob Landley
216dad378bb17f1910014383bf196e87028e98d81d6Rob Landley      start += skip;
21773e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley      if (!(toys.optflags & FLAG_o)) break;
21873e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    } while (*start);
219dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    offset += len;
2206c64f5f186d26d4c95d408979d33831935e026f1Rob Landley
221b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley    if (mmatch) mcount++;
22236aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley    else {
22336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      int discard = (after || TT.b);
22436aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley
22536aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      if (after && --after) {
226c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley        outline(line, '-', name, lcount, 0, 0);
22736aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley        discard = 0;
22836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      }
22936aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      if (discard && TT.b) {
23036aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley        dlist_add(&dlb, line);
23136aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley        line = 0;
23236aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley        if (++before>TT.b) {
23336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          struct double_list *dl;
23436aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley
23536aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          dl = dlist_pop(&dlb);
23636aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          free(dl->data);
23736aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          free(dl);
23836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley          before--;
23936aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley        } else discard = 0;
24036aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      }
241c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley      // If we discarded a line while displaying context, show bars before next
242c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley      // line (but don't show them now in case that was last match in file)
24336aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley      if (discard && mcount) bars = "--";
244b97d8211fa5c382f32e9a9f606a8566bd2ec09fbRob Landley    }
245dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    free(line);
246dad378bb17f1910014383bf196e87028e98d81d6Rob Landley
247dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    if ((toys.optflags & FLAG_m) && mcount >= TT.m) break;
248dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  }
249e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
250c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  if (toys.optflags & FLAG_c) outline(0, ':', name, mcount, 0, 0);
251e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
252dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  // loopfiles will also close the fd, but this frees an (opaque) struct.
253dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  fclose(file);
254e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake}
255e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
256dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landleystatic void parse_regex(void)
257f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley{
25831f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley  struct arg_list *al, *new, *list = NULL;
259dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  long len = 0;
260dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  char *s, *ss;
261dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
262dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  // Add all -f lines to -e list. (Yes, this is leaking allocation context for
263dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  // exit to free. Not supporting nofork for this command any time soon.)
26431f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley  al = TT.f ? TT.f : TT.e;
26531f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley  while (al) {
266dc3731783ead154d5a0d8d318566468474b43013Rob Landley    if (TT.f) s = ss = xreadfile(al->arg, 0, 0);
26731f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    else s = ss = al->arg;
268dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
26973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    // Split lines at \n, add individual lines to new list.
270fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley    do {
271dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      ss = strchr(s, '\n');
27231f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      if (ss) *(ss++) = 0;
27331f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      new = xmalloc(sizeof(struct arg_list));
27431f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      new->next = list;
27531f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      new->arg = s;
27631f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      list = new;
277dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      s = ss;
278fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley    } while (ss && *s);
27973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley
28073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    // Advance, when we run out of -f switch to -e.
28131f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    al = al->next;
28231f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    if (!al && TT.f) {
28331f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      TT.f = 0;
28431f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      al = TT.e;
28531f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    }
286e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake  }
28731f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley  TT.e = list;
288f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley
289dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  if (!(toys.optflags & FLAG_F)) {
290fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley    char *regstr;
29173e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    int i;
292e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
29331f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    // Convert strings to one big regex
2943ad73e1344afa7812671d08456591b8cde952775William Haddon    for (al = TT.e; al; al = al->next)
2953ad73e1344afa7812671d08456591b8cde952775William Haddon      len += strlen(al->arg)+1+!(toys.optflags & FLAG_E);
296e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
297fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley    regstr = s = xmalloc(len);
298dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    for (al = TT.e; al; al = al->next) {
299dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      s = stpcpy(s, al->arg);
30031f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley      if (!(toys.optflags & FLAG_E)) *(s++) = '\\';
301dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      *(s++) = '|';
302e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake    }
30331f07104445bc0c0cbc942d86ee9d31d08c344d8Rob Landley    *(s-=(1+!(toys.optflags & FLAG_E))) = 0;
304e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
30573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    i = regcomp((regex_t *)toybuf, regstr,
306dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley                ((toys.optflags & FLAG_E) ? REG_EXTENDED : 0) |
307dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley                ((toys.optflags & FLAG_i) ? REG_ICASE    : 0));
3087b7b284ce4c1bd53d442927d9a570fcf84e0187cStrake
30973e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    if (i) {
31073e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley      regerror(i, (regex_t *)toybuf, toybuf+sizeof(regex_t),
311dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley               sizeof(toybuf)-sizeof(regex_t));
312dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley      error_exit("bad REGEX: %s", toybuf);
313dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    }
314e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake  }
315e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake}
316e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
317fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landleystatic int do_grep_r(struct dirtree *new)
318fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley{
319fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  char *name;
320fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
321fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  if (new->parent && !dirtree_notdotdot(new)) return 0;
322fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  if (S_ISDIR(new->st.st_mode)) return DIRTREE_RECURSE;
323fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
324fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  // "grep -r onefile" doesn't show filenames, but "grep -r onedir" should.
325fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  if (new->parent && !(toys.optflags & FLAG_h)) toys.optflags |= FLAG_H;
326fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
327fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  name = dirtree_path(new, 0);
328fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  do_grep(openat(dirtree_parentfd(new), new->name, 0), name);
329fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  free(name);
330fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
331fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  return 0;
332fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley}
333fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
334f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landleyvoid grep_main(void)
335f97eaf158aa0baf868f0b19d192f7d302c1484e9Rob Landley{
336adcbaf530f3ec351ec2932ebe9b77140f79eda4aRob Landley  char **ss = toys.optargs;
337fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
33836aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley  if (!TT.a) TT.a = TT.c;
33936aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley  if (!TT.b) TT.b = TT.c;
34036aed25f3f54f7871d5810adfe99d96944e0afb4Rob Landley
341c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  TT.indelim = '\n' * !(toys.optflags&FLAG_z);
342c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley  TT.outdelim = '\n' * !(toys.optflags&FLAG_Z);
343c02619bbc0acbb2b4588933ec1bbb7b7026adbb6Rob Landley
344dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  // Handle egrep and fgrep
34573e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley  if (*toys.which->name == 'e') toys.optflags |= FLAG_E;
346dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
3471fa68247345fa6e2cefe4fb4d88ef75f614b18c4M. Farkas-Dyck
348dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  if (!TT.e && !TT.f) {
349adcbaf530f3ec351ec2932ebe9b77140f79eda4aRob Landley    if (!*ss) error_exit("no REGEX");
350dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    TT.e = xzalloc(sizeof(struct arg_list));
351adcbaf530f3ec351ec2932ebe9b77140f79eda4aRob Landley    TT.e->arg = *(ss++);
352dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley    toys.optc--;
353dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  }
354dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley
355dd2d23930241a30a8eb4f0fc9d70bc86c4a6cb6eRob Landley  parse_regex();
356e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
357fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  if (!(toys.optflags & FLAG_h) && toys.optc>1) toys.optflags |= FLAG_H;
358e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake
359174ba2bed2f33fe90926b08a3e9ec4e3ce7febabStrake  toys.exitval = 1;
360dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  if (toys.optflags & FLAG_s) {
361dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    close(2);
362dad378bb17f1910014383bf196e87028e98d81d6Rob Landley    xopen("/dev/null", O_RDWR);
363dad378bb17f1910014383bf196e87028e98d81d6Rob Landley  }
364fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley
365fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley  if (toys.optflags & FLAG_r) {
36673e3a644c1af8e28ca45c3b5169dee8acc06b179Rob Landley    // Iterate through -r arguments. Use "." as default if none provided.
367adcbaf530f3ec351ec2932ebe9b77140f79eda4aRob Landley    for (ss = *ss ? ss : (char *[]){".", 0}; *ss; ss++) {
368fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley      if (!strcmp(*ss, "-")) do_grep(0, *ss);
369fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley      else dirtree_read(*ss, do_grep_r);
370fce85e9d287cef03777d3c2d97b2a0b43242ee21Rob Landley    }
37194983f2dddde86e8f121abe97a28aa1f6c87a3dbRob Landley  } else loopfiles_rw(ss, O_RDONLY, 0, 1, do_grep);
372e999ca008416e3d41c1079bcb4d151b43c95dc3aStrake}
373