OSDN Git Service

2e0558f36cf4fb34e6a02696cbe920544a0bca95
[android-x86/external-toybox.git] / toys / posix / grep.c
1 /* grep.c - print lines what match given regular expression
2  *
3  * Copyright 2013 CE Strake <strake888 at gmail.com>
4  *
5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html
6  *
7  * TODO: -ABC
8
9 USE_GREP(NEWTOY(grep, "A#ZzEFHabhinorsvwclqe*f*m#x[!wx][!EFw]", TOYFLAG_BIN))
10 USE_EGREP(OLDTOY(egrep, grep, TOYFLAG_BIN))
11 USE_FGREP(OLDTOY(fgrep, grep, TOYFLAG_BIN))
12
13 config GREP
14   bool "grep"
15   default y
16   help
17     usage: grep [-EFivwcloqsHbhn] [-A NUM] [-m MAX] [-e REGEX]... [-f REGFILE] [FILE]...
18
19     Show lines matching regular expressions. If no -e, first argument is
20     regular expression to match. With no files (or "-" filename) read stdin.
21     Returns 0 if matched, 1 if no match found.
22
23     -e  Regex to match. (May be repeated.)
24     -f  File containing regular expressions to match.
25
26     match type:
27     -A  NUM lines after match
28     -E  extended regex syntax    -F  fixed (match literal string)
29     -i  case insensitive         -m  stop after this many lines matched
30     -r  recursive (on dir)       -v  invert match
31     -w  whole word (implies -E)  -x  whole line
32     -z  input NUL terminated
33
34     display modes: (default: matched line)
35     -c  count of matching lines  -l  show matching filenames
36     -o  only matching part       -q  quiet (errors only)
37     -s  silent (no error msg)    -Z  output NUL terminated
38
39     output prefix (default: filename if checking more than 1 file)
40     -H  force filename           -b  byte offset of match
41     -h  hide filename            -n  line number of match
42
43 config EGREP
44   bool
45   default y
46   depends on GREP
47
48 config FGREP
49   bool
50   default y
51   depends on GREP
52 */
53
54 #define FOR_grep
55 #include "toys.h"
56 #include <regex.h>
57
58 GLOBALS(
59   long m;
60   struct arg_list *f;
61   struct arg_list *e;
62   long a;
63 )
64
65 // Show matches in one file
66 static void do_grep(int fd, char *name)
67 {
68   FILE *file = fdopen(fd, "r");
69   long offset = 0, after = 0;
70   int lcount = 0, mcount = 0;
71   char *bars = 0, indelim = '\n' * !(toys.optflags&FLAG_z),
72        outdelim = '\n' * !(toys.optflags&FLAG_Z);
73
74   if (!fd) name = "(standard input)";
75
76   if (!file) {
77     perror_msg_raw(name);
78     return;
79   }
80
81   // Loop through lines of input
82   for (;;) {
83     char *line = 0, *start;
84     regmatch_t matches;
85     size_t unused;
86     long len;
87     int mmatch = 0;
88
89     lcount++;
90     if (0 > (len = getdelim(&line, &unused, indelim, file))) break;
91     if (line[len-1] == indelim) line[len-1] = 0;
92
93     start = line;
94
95     // Loop through matches in this line
96     do {
97       int rc = 0, skip = 0;
98
99       // Handle non-regex matches
100       if (toys.optflags & FLAG_F) {
101         struct arg_list *seek, fseek;
102         char *s = 0;
103
104         for (seek = TT.e; seek; seek = seek->next) {
105           if (toys.optflags & FLAG_x) {
106             int i = (toys.optflags & FLAG_i);
107
108             if ((i ? strcasecmp : strcmp)(seek->arg, line)) s = line;
109           } else if (!*seek->arg) {
110             seek = &fseek;
111             fseek.arg = s = line;
112             break;
113           }
114           if (toys.optflags & FLAG_i) {
115             long ll = strlen(seek->arg);;
116
117             // Alas, posix hasn't got strcasestr()
118             for (s = line; *s; s++) if (!strncasecmp(s, seek->arg, ll)) break;
119             if (!*s) s = 0;
120           } else s = strstr(line, seek->arg);
121           if (s) break;
122         }
123
124         if (s) {
125           matches.rm_so = (s-line);
126           skip = matches.rm_eo = (s-line)+strlen(seek->arg);
127         } else rc = 1;
128       } else {
129         rc = regexec((regex_t *)toybuf, start, 1, &matches,
130                      start==line ? 0 : REG_NOTBOL);
131         skip = matches.rm_eo;
132       }
133
134       if (toys.optflags & FLAG_x)
135         if (matches.rm_so || line[matches.rm_eo]) rc = 1;
136
137       if (!rc && (toys.optflags & FLAG_w)) {
138         char c = 0;
139
140         if ((start+matches.rm_so)!=line) {
141           c = start[matches.rm_so-1];
142           if (!isalnum(c) && c != '_') c = 0;
143         }
144         if (!c) {
145           c = start[matches.rm_eo];
146           if (!isalnum(c) && c != '_') c = 0;
147         }
148         if (c) {
149           start += matches.rm_so+1;
150
151           continue;
152         }
153       }
154
155       if (toys.optflags & FLAG_v) {
156         if (toys.optflags & FLAG_o) {
157           if (rc) skip = matches.rm_eo = strlen(start);
158           else if (!matches.rm_so) {
159             start += skip;
160             continue;
161           } else matches.rm_eo = matches.rm_so;
162         } else {
163           if (!rc) break;
164           matches.rm_eo = strlen(start);
165         }
166         matches.rm_so = 0;
167       } else if (rc) break;
168
169       if (bars) {
170         xputs(bars);
171         bars = 0;
172       }
173       mmatch++;
174       toys.exitval = 0;
175       if (toys.optflags & FLAG_q) xexit();
176       if (toys.optflags & FLAG_l) {
177         printf("%s%c", name, outdelim);
178         free(line);
179         fclose(file);
180         return;
181       }
182       if (toys.optflags & FLAG_o)
183         if (matches.rm_eo == matches.rm_so)
184           break;
185
186       if (!(toys.optflags & FLAG_c)) {
187         if (toys.optflags & FLAG_H) printf("%s:", name);
188         if (toys.optflags & FLAG_n) printf("%d:", lcount);
189         if (toys.optflags & FLAG_b)
190           printf("%ld:", offset + (start-line) +
191               ((toys.optflags & FLAG_o) ? matches.rm_so : 0));
192         if (!(toys.optflags & FLAG_o)) {
193           xprintf("%s%c", line, outdelim);
194           if (TT.a) after = TT.a;
195         } else {
196           xprintf("%.*s%c", (int)(matches.rm_eo-matches.rm_so),
197                   start+matches.rm_so, outdelim);
198         }
199       }
200
201       start += skip;
202       if (!(toys.optflags & FLAG_o)) break;
203     } while (*start);
204     offset += len;
205
206     if (mmatch) mcount++;
207     else if (after) {
208       if (after>0) {
209         xprintf("%s%c", line, outdelim);
210         if (!--after) after = -1;
211       } else bars = "--";
212     }
213     free(line);
214
215     if ((toys.optflags & FLAG_m) && mcount >= TT.m) break;
216   }
217
218   if (toys.optflags & FLAG_c) {
219     if (toys.optflags & FLAG_H) printf("%s:", name);
220     xprintf("%d%c", mcount, outdelim);
221   }
222
223   // loopfiles will also close the fd, but this frees an (opaque) struct.
224   fclose(file);
225 }
226
227 static void parse_regex(void)
228 {
229   struct arg_list *al, *new, *list = NULL;
230   long len = 0;
231   char *s, *ss;
232
233   // Add all -f lines to -e list. (Yes, this is leaking allocation context for
234   // exit to free. Not supporting nofork for this command any time soon.)
235   al = TT.f ? TT.f : TT.e;
236   while (al) {
237     if (TT.f) s = ss = xreadfile(al->arg, 0, 0);
238     else s = ss = al->arg;
239
240     // Split lines at \n, add individual lines to new list.
241     do {
242       ss = strchr(s, '\n');
243       if (ss) *(ss++) = 0;
244       new = xmalloc(sizeof(struct arg_list));
245       new->next = list;
246       new->arg = s;
247       list = new;
248       s = ss;
249     } while (ss && *s);
250
251     // Advance, when we run out of -f switch to -e.
252     al = al->next;
253     if (!al && TT.f) {
254       TT.f = 0;
255       al = TT.e;
256     }
257   }
258   TT.e = list;
259
260   if (!(toys.optflags & FLAG_F)) {
261     char *regstr;
262     int i;
263
264     // Convert strings to one big regex
265     for (al = TT.e; al; al = al->next)
266       len += strlen(al->arg)+1+!(toys.optflags & FLAG_E);
267
268     regstr = s = xmalloc(len);
269     for (al = TT.e; al; al = al->next) {
270       s = stpcpy(s, al->arg);
271       if (!(toys.optflags & FLAG_E)) *(s++) = '\\';
272       *(s++) = '|';
273     }
274     *(s-=(1+!(toys.optflags & FLAG_E))) = 0;
275
276     i = regcomp((regex_t *)toybuf, regstr,
277                 ((toys.optflags & FLAG_E) ? REG_EXTENDED : 0) |
278                 ((toys.optflags & FLAG_i) ? REG_ICASE    : 0));
279
280     if (i) {
281       regerror(i, (regex_t *)toybuf, toybuf+sizeof(regex_t),
282                sizeof(toybuf)-sizeof(regex_t));
283       error_exit("bad REGEX: %s", toybuf);
284     }
285   }
286 }
287
288 static int do_grep_r(struct dirtree *new)
289 {
290   char *name;
291
292   if (new->parent && !dirtree_notdotdot(new)) return 0;
293   if (S_ISDIR(new->st.st_mode)) return DIRTREE_RECURSE;
294
295   // "grep -r onefile" doesn't show filenames, but "grep -r onedir" should.
296   if (new->parent && !(toys.optflags & FLAG_h)) toys.optflags |= FLAG_H;
297
298   name = dirtree_path(new, 0);
299   do_grep(openat(dirtree_parentfd(new), new->name, 0), name);
300   free(name);
301
302   return 0;
303 }
304
305 void grep_main(void)
306 {
307   char **ss = toys.optargs;
308
309   // Handle egrep and fgrep
310   if (*toys.which->name == 'e') toys.optflags |= FLAG_E;
311   if (*toys.which->name == 'f') toys.optflags |= FLAG_F;
312
313   if (!TT.e && !TT.f) {
314     if (!*ss) error_exit("no REGEX");
315     TT.e = xzalloc(sizeof(struct arg_list));
316     TT.e->arg = *(ss++);
317     toys.optc--;
318   }
319
320   parse_regex();
321
322   if (!(toys.optflags & FLAG_h) && toys.optc>1) toys.optflags |= FLAG_H;
323
324   toys.exitval = 1;
325   if (toys.optflags & FLAG_s) {
326     close(2);
327     xopen("/dev/null", O_RDWR);
328   }
329
330   if (toys.optflags & FLAG_r) {
331     // Iterate through -r arguments. Use "." as default if none provided.
332     for (ss = *ss ? ss : (char *[]){".", 0}; *ss; ss++) {
333       if (!strcmp(*ss, "-")) do_grep(0, *ss);
334       else dirtree_read(*ss, do_grep_r);
335     }
336   } else loopfiles_rw(ss, O_RDONLY, 0, 1, do_grep);
337 }