grep.c revision 1.3.40.1 1 1.3.40.1 bouyer /* $NetBSD: grep.c,v 1.3.40.1 2011/02/17 12:00:55 bouyer Exp $ */
2 1.3.40.1 bouyer /* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
3 1.3.40.1 bouyer /* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
4 1.2 cjep
5 1.1 cjep /*-
6 1.3.40.1 bouyer * Copyright (c) 1999 James Howard and Dag-Erling Codan Smrgrav
7 1.3.40.1 bouyer * Copyright (C) 2008-2009 Gabor Kovesdan <gabor (at) FreeBSD.org>
8 1.1 cjep * All rights reserved.
9 1.1 cjep *
10 1.1 cjep * Redistribution and use in source and binary forms, with or without
11 1.1 cjep * modification, are permitted provided that the following conditions
12 1.1 cjep * are met:
13 1.1 cjep * 1. Redistributions of source code must retain the above copyright
14 1.1 cjep * notice, this list of conditions and the following disclaimer.
15 1.1 cjep * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 cjep * notice, this list of conditions and the following disclaimer in the
17 1.1 cjep * documentation and/or other materials provided with the distribution.
18 1.1 cjep *
19 1.1 cjep * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 1.1 cjep * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 1.1 cjep * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 1.1 cjep * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 1.1 cjep * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 1.1 cjep * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 1.1 cjep * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 1.1 cjep * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 1.1 cjep * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 1.1 cjep * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 cjep * SUCH DAMAGE.
30 1.1 cjep */
31 1.2 cjep
32 1.2 cjep #include <sys/cdefs.h>
33 1.3.40.1 bouyer __RCSID("$NetBSD: grep.c,v 1.3.40.1 2011/02/17 12:00:55 bouyer Exp $");
34 1.1 cjep
35 1.1 cjep #include <sys/stat.h>
36 1.3.40.1 bouyer #include <sys/types.h>
37 1.1 cjep
38 1.3.40.1 bouyer #include <ctype.h>
39 1.1 cjep #include <err.h>
40 1.1 cjep #include <errno.h>
41 1.1 cjep #include <getopt.h>
42 1.2 cjep #include <limits.h>
43 1.3.40.1 bouyer #include <libgen.h>
44 1.3.40.1 bouyer #include <locale.h>
45 1.3.40.1 bouyer #include <stdbool.h>
46 1.1 cjep #include <stdio.h>
47 1.1 cjep #include <stdlib.h>
48 1.1 cjep #include <string.h>
49 1.1 cjep #include <unistd.h>
50 1.1 cjep
51 1.1 cjep #include "grep.h"
52 1.1 cjep
53 1.3.40.1 bouyer #ifndef WITHOUT_NLS
54 1.3.40.1 bouyer #include <nl_types.h>
55 1.3.40.1 bouyer nl_catd catalog;
56 1.3.40.1 bouyer #endif
57 1.3.40.1 bouyer
58 1.3.40.1 bouyer /*
59 1.3.40.1 bouyer * Default messags to use when NLS is disabled or no catalogue
60 1.3.40.1 bouyer * is found.
61 1.2 cjep */
62 1.3.40.1 bouyer const char *errstr[] = {
63 1.3.40.1 bouyer "",
64 1.3.40.1 bouyer /* 1*/ "(standard input)",
65 1.3.40.1 bouyer /* 2*/ "cannot read bzip2 compressed file",
66 1.3.40.1 bouyer /* 3*/ "unknown %s option",
67 1.3.40.1 bouyer /* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
68 1.3.40.1 bouyer /* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
69 1.3.40.1 bouyer /* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
70 1.3.40.1 bouyer /* 7*/ "\t[--null] [pattern] [file ...]\n",
71 1.3.40.1 bouyer /* 8*/ "Binary file %s matches\n",
72 1.3.40.1 bouyer /* 9*/ "%s (BSD grep) %s\n",
73 1.3.40.1 bouyer };
74 1.2 cjep
75 1.1 cjep /* Flags passed to regcomp() and regexec() */
76 1.3.40.1 bouyer int cflags = 0;
77 1.3.40.1 bouyer int eflags = REG_STARTEND;
78 1.3.40.1 bouyer
79 1.3.40.1 bouyer /* Shortcut for matching all cases like empty regex */
80 1.3.40.1 bouyer bool matchall;
81 1.1 cjep
82 1.3.40.1 bouyer /* Searching patterns */
83 1.3.40.1 bouyer unsigned int patterns, pattern_sz;
84 1.3.40.1 bouyer char **pattern;
85 1.3.40.1 bouyer regex_t *r_pattern;
86 1.3.40.1 bouyer fastgrep_t *fg_pattern;
87 1.3.40.1 bouyer
88 1.3.40.1 bouyer /* Filename exclusion/inclusion patterns */
89 1.3.40.1 bouyer unsigned int fpatterns, fpattern_sz;
90 1.3.40.1 bouyer unsigned int dpatterns, dpattern_sz;
91 1.3.40.1 bouyer struct epat *dpattern, *fpattern;
92 1.1 cjep
93 1.1 cjep /* For regex errors */
94 1.3.40.1 bouyer char re_error[RE_ERROR_BUF + 1];
95 1.1 cjep
96 1.1 cjep /* Command-line flags */
97 1.3.40.1 bouyer unsigned long long Aflag; /* -A x: print x lines trailing each match */
98 1.3.40.1 bouyer unsigned long long Bflag; /* -B x: print x lines leading each match */
99 1.3.40.1 bouyer bool Hflag; /* -H: always print file name */
100 1.3.40.1 bouyer bool Lflag; /* -L: only show names of files with no matches */
101 1.3.40.1 bouyer bool bflag; /* -b: show block numbers for each match */
102 1.3.40.1 bouyer bool cflag; /* -c: only show a count of matching lines */
103 1.3.40.1 bouyer bool hflag; /* -h: don't print filename headers */
104 1.3.40.1 bouyer bool iflag; /* -i: ignore case */
105 1.3.40.1 bouyer bool lflag; /* -l: only show names of files with matches */
106 1.3.40.1 bouyer bool mflag; /* -m x: stop reading the files after x matches */
107 1.3.40.1 bouyer unsigned long long mcount; /* count for -m */
108 1.3.40.1 bouyer bool nflag; /* -n: show line numbers in front of matching lines */
109 1.3.40.1 bouyer bool oflag; /* -o: print only matching part */
110 1.3.40.1 bouyer bool qflag; /* -q: quiet mode (don't output anything) */
111 1.3.40.1 bouyer bool sflag; /* -s: silent mode (ignore errors) */
112 1.3.40.1 bouyer bool vflag; /* -v: only show non-matching lines */
113 1.3.40.1 bouyer bool wflag; /* -w: pattern must start and end on word boundaries */
114 1.3.40.1 bouyer bool xflag; /* -x: pattern must match entire line */
115 1.3.40.1 bouyer bool lbflag; /* --line-buffered */
116 1.3.40.1 bouyer bool nullflag; /* --null */
117 1.3.40.1 bouyer char *label; /* --label */
118 1.3.40.1 bouyer const char *color; /* --color */
119 1.3.40.1 bouyer int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
120 1.3.40.1 bouyer int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
121 1.3.40.1 bouyer int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
122 1.3.40.1 bouyer int devbehave = DEV_READ; /* -D: handling of devices */
123 1.3.40.1 bouyer int dirbehave = DIR_READ; /* -dRr: handling of directories */
124 1.3.40.1 bouyer int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
125 1.3.40.1 bouyer
126 1.3.40.1 bouyer bool dexclude, dinclude; /* --exclude-dir and --include-dir */
127 1.3.40.1 bouyer bool fexclude, finclude; /* --exclude and --include */
128 1.2 cjep
129 1.2 cjep enum {
130 1.2 cjep BIN_OPT = CHAR_MAX + 1,
131 1.3.40.1 bouyer COLOR_OPT,
132 1.2 cjep HELP_OPT,
133 1.2 cjep MMAP_OPT,
134 1.3.40.1 bouyer LINEBUF_OPT,
135 1.3.40.1 bouyer LABEL_OPT,
136 1.3.40.1 bouyer NULL_OPT,
137 1.3.40.1 bouyer R_EXCLUDE_OPT,
138 1.3.40.1 bouyer R_INCLUDE_OPT,
139 1.3.40.1 bouyer R_DEXCLUDE_OPT,
140 1.3.40.1 bouyer R_DINCLUDE_OPT
141 1.2 cjep };
142 1.1 cjep
143 1.3.40.1 bouyer static inline const char *init_color(const char *);
144 1.3.40.1 bouyer
145 1.1 cjep /* Housekeeping */
146 1.3.40.1 bouyer bool first = true; /* flag whether we are processing the first match */
147 1.3.40.1 bouyer bool prev; /* flag whether or not the previous line matched */
148 1.3.40.1 bouyer int tail; /* lines left to print */
149 1.3.40.1 bouyer bool notfound; /* file not found */
150 1.3.40.1 bouyer
151 1.3.40.1 bouyer extern char *__progname;
152 1.1 cjep
153 1.3.40.1 bouyer /*
154 1.3.40.1 bouyer * Prints usage information and returns 2.
155 1.3.40.1 bouyer */
156 1.1 cjep static void
157 1.1 cjep usage(void)
158 1.1 cjep {
159 1.3.40.1 bouyer fprintf(stderr, getstr(4), __progname);
160 1.3.40.1 bouyer fprintf(stderr, "%s", getstr(5));
161 1.3.40.1 bouyer fprintf(stderr, "%s", getstr(5));
162 1.3.40.1 bouyer fprintf(stderr, "%s", getstr(6));
163 1.3.40.1 bouyer fprintf(stderr, "%s", getstr(7));
164 1.1 cjep exit(2);
165 1.1 cjep }
166 1.1 cjep
167 1.3.40.1 bouyer static const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
168 1.1 cjep
169 1.2 cjep struct option long_options[] =
170 1.1 cjep {
171 1.3.40.1 bouyer {"binary-files", required_argument, NULL, BIN_OPT},
172 1.3.40.1 bouyer {"help", no_argument, NULL, HELP_OPT},
173 1.3.40.1 bouyer {"mmap", no_argument, NULL, MMAP_OPT},
174 1.3.40.1 bouyer {"line-buffered", no_argument, NULL, LINEBUF_OPT},
175 1.3.40.1 bouyer {"label", required_argument, NULL, LABEL_OPT},
176 1.3.40.1 bouyer {"null", no_argument, NULL, NULL_OPT},
177 1.3.40.1 bouyer {"color", optional_argument, NULL, COLOR_OPT},
178 1.3.40.1 bouyer {"colour", optional_argument, NULL, COLOR_OPT},
179 1.3.40.1 bouyer {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
180 1.3.40.1 bouyer {"include", required_argument, NULL, R_INCLUDE_OPT},
181 1.3.40.1 bouyer {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
182 1.3.40.1 bouyer {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
183 1.3.40.1 bouyer {"after-context", required_argument, NULL, 'A'},
184 1.3.40.1 bouyer {"text", no_argument, NULL, 'a'},
185 1.3.40.1 bouyer {"before-context", required_argument, NULL, 'B'},
186 1.3.40.1 bouyer {"byte-offset", no_argument, NULL, 'b'},
187 1.3.40.1 bouyer {"context", optional_argument, NULL, 'C'},
188 1.3.40.1 bouyer {"count", no_argument, NULL, 'c'},
189 1.3.40.1 bouyer {"devices", required_argument, NULL, 'D'},
190 1.3.40.1 bouyer {"directories", required_argument, NULL, 'd'},
191 1.3.40.1 bouyer {"extended-regexp", no_argument, NULL, 'E'},
192 1.3.40.1 bouyer {"regexp", required_argument, NULL, 'e'},
193 1.3.40.1 bouyer {"fixed-strings", no_argument, NULL, 'F'},
194 1.3.40.1 bouyer {"file", required_argument, NULL, 'f'},
195 1.3.40.1 bouyer {"basic-regexp", no_argument, NULL, 'G'},
196 1.3.40.1 bouyer {"no-filename", no_argument, NULL, 'h'},
197 1.3.40.1 bouyer {"with-filename", no_argument, NULL, 'H'},
198 1.3.40.1 bouyer {"ignore-case", no_argument, NULL, 'i'},
199 1.3.40.1 bouyer {"bz2decompress", no_argument, NULL, 'J'},
200 1.3.40.1 bouyer {"files-with-matches", no_argument, NULL, 'l'},
201 1.3.40.1 bouyer {"files-without-match", no_argument, NULL, 'L'},
202 1.3.40.1 bouyer {"max-count", required_argument, NULL, 'm'},
203 1.3.40.1 bouyer {"line-number", no_argument, NULL, 'n'},
204 1.3.40.1 bouyer {"only-matching", no_argument, NULL, 'o'},
205 1.3.40.1 bouyer {"quiet", no_argument, NULL, 'q'},
206 1.3.40.1 bouyer {"silent", no_argument, NULL, 'q'},
207 1.3.40.1 bouyer {"recursive", no_argument, NULL, 'r'},
208 1.3.40.1 bouyer {"no-messages", no_argument, NULL, 's'},
209 1.3.40.1 bouyer {"binary", no_argument, NULL, 'U'},
210 1.3.40.1 bouyer {"unix-byte-offsets", no_argument, NULL, 'u'},
211 1.3.40.1 bouyer {"invert-match", no_argument, NULL, 'v'},
212 1.3.40.1 bouyer {"version", no_argument, NULL, 'V'},
213 1.3.40.1 bouyer {"word-regexp", no_argument, NULL, 'w'},
214 1.3.40.1 bouyer {"line-regexp", no_argument, NULL, 'x'},
215 1.3.40.1 bouyer {"decompress", no_argument, NULL, 'Z'},
216 1.3.40.1 bouyer {NULL, no_argument, NULL, 0}
217 1.1 cjep };
218 1.1 cjep
219 1.3.40.1 bouyer /*
220 1.3.40.1 bouyer * Adds a searching pattern to the internal array.
221 1.3.40.1 bouyer */
222 1.1 cjep static void
223 1.1 cjep add_pattern(char *pat, size_t len)
224 1.1 cjep {
225 1.3.40.1 bouyer
226 1.3.40.1 bouyer /* Check if we can do a shortcut */
227 1.1 cjep if (len == 0 || matchall) {
228 1.3.40.1 bouyer matchall = true;
229 1.1 cjep return;
230 1.1 cjep }
231 1.3.40.1 bouyer /* Increase size if necessary */
232 1.1 cjep if (patterns == pattern_sz) {
233 1.1 cjep pattern_sz *= 2;
234 1.3.40.1 bouyer pattern = grep_realloc(pattern, ++pattern_sz *
235 1.3.40.1 bouyer sizeof(*pattern));
236 1.1 cjep }
237 1.3.40.1 bouyer if (len > 0 && pat[len - 1] == '\n')
238 1.1 cjep --len;
239 1.3.40.1 bouyer /* pat may not be NUL-terminated */
240 1.2 cjep pattern[patterns] = grep_malloc(len + 1);
241 1.3.40.1 bouyer memcpy(pattern[patterns], pat, len);
242 1.1 cjep pattern[patterns][len] = '\0';
243 1.1 cjep ++patterns;
244 1.1 cjep }
245 1.1 cjep
246 1.3.40.1 bouyer /*
247 1.3.40.1 bouyer * Adds a file include/exclude pattern to the internal array.
248 1.3.40.1 bouyer */
249 1.1 cjep static void
250 1.3.40.1 bouyer add_fpattern(const char *pat, int mode)
251 1.3.40.1 bouyer {
252 1.3.40.1 bouyer
253 1.3.40.1 bouyer /* Increase size if necessary */
254 1.3.40.1 bouyer if (fpatterns == fpattern_sz) {
255 1.3.40.1 bouyer fpattern_sz *= 2;
256 1.3.40.1 bouyer fpattern = grep_realloc(fpattern, ++fpattern_sz *
257 1.3.40.1 bouyer sizeof(struct epat));
258 1.3.40.1 bouyer }
259 1.3.40.1 bouyer fpattern[fpatterns].pat = grep_strdup(pat);
260 1.3.40.1 bouyer fpattern[fpatterns].mode = mode;
261 1.3.40.1 bouyer ++fpatterns;
262 1.3.40.1 bouyer }
263 1.3.40.1 bouyer
264 1.3.40.1 bouyer /*
265 1.3.40.1 bouyer * Adds a directory include/exclude pattern to the internal array.
266 1.3.40.1 bouyer */
267 1.3.40.1 bouyer static void
268 1.3.40.1 bouyer add_dpattern(const char *pat, int mode)
269 1.3.40.1 bouyer {
270 1.3.40.1 bouyer
271 1.3.40.1 bouyer /* Increase size if necessary */
272 1.3.40.1 bouyer if (dpatterns == dpattern_sz) {
273 1.3.40.1 bouyer dpattern_sz *= 2;
274 1.3.40.1 bouyer dpattern = grep_realloc(dpattern, ++dpattern_sz *
275 1.3.40.1 bouyer sizeof(struct epat));
276 1.3.40.1 bouyer }
277 1.3.40.1 bouyer dpattern[dpatterns].pat = grep_strdup(pat);
278 1.3.40.1 bouyer dpattern[dpatterns].mode = mode;
279 1.3.40.1 bouyer ++dpatterns;
280 1.3.40.1 bouyer }
281 1.3.40.1 bouyer
282 1.3.40.1 bouyer /*
283 1.3.40.1 bouyer * Reads searching patterns from a file and adds them with add_pattern().
284 1.3.40.1 bouyer */
285 1.3.40.1 bouyer static void
286 1.3.40.1 bouyer read_patterns(const char *fn)
287 1.1 cjep {
288 1.1 cjep FILE *f;
289 1.1 cjep char *line;
290 1.1 cjep size_t len;
291 1.1 cjep
292 1.1 cjep if ((f = fopen(fn, "r")) == NULL)
293 1.2 cjep err(2, "%s", fn);
294 1.3.40.1 bouyer while ((line = fgetln(f, &len)) != NULL)
295 1.3.40.1 bouyer add_pattern(line, *line == '\n' ? 0 : len);
296 1.1 cjep if (ferror(f))
297 1.2 cjep err(2, "%s", fn);
298 1.1 cjep fclose(f);
299 1.1 cjep }
300 1.1 cjep
301 1.3.40.1 bouyer static inline const char *
302 1.3.40.1 bouyer init_color(const char *d)
303 1.2 cjep {
304 1.3.40.1 bouyer char *c;
305 1.2 cjep
306 1.3.40.1 bouyer c = getenv("GREP_COLOR");
307 1.3.40.1 bouyer return (c != NULL ? c : d);
308 1.2 cjep }
309 1.2 cjep
310 1.1 cjep int
311 1.1 cjep main(int argc, char *argv[])
312 1.1 cjep {
313 1.3.40.1 bouyer char **aargv, **eargv, *eopts;
314 1.3.40.1 bouyer char *ep;
315 1.3.40.1 bouyer unsigned long long l;
316 1.3.40.1 bouyer unsigned int aargc, eargc, i;
317 1.3.40.1 bouyer int c, lastc, needpattern, newarg, prevoptind;
318 1.3.40.1 bouyer
319 1.3.40.1 bouyer setlocale(LC_ALL, "");
320 1.3.40.1 bouyer
321 1.3.40.1 bouyer #ifndef WITHOUT_NLS
322 1.3.40.1 bouyer catalog = catopen("grep", NL_CAT_LOCALE);
323 1.3.40.1 bouyer #endif
324 1.3.40.1 bouyer
325 1.3.40.1 bouyer /* Check what is the program name of the binary. In this
326 1.3.40.1 bouyer way we can have all the funcionalities in one binary
327 1.3.40.1 bouyer without the need of scripting and using ugly hacks. */
328 1.3.40.1 bouyer switch (__progname[0]) {
329 1.2 cjep case 'e':
330 1.3.40.1 bouyer grepbehave = GREP_EXTENDED;
331 1.2 cjep break;
332 1.2 cjep case 'f':
333 1.3.40.1 bouyer grepbehave = GREP_FIXED;
334 1.2 cjep break;
335 1.2 cjep case 'g':
336 1.3.40.1 bouyer grepbehave = GREP_BASIC;
337 1.2 cjep break;
338 1.2 cjep case 'z':
339 1.3.40.1 bouyer filebehave = FILE_GZIP;
340 1.3.40.1 bouyer switch(__progname[1]) {
341 1.2 cjep case 'e':
342 1.3.40.1 bouyer grepbehave = GREP_EXTENDED;
343 1.2 cjep break;
344 1.2 cjep case 'f':
345 1.3.40.1 bouyer grepbehave = GREP_FIXED;
346 1.2 cjep break;
347 1.2 cjep case 'g':
348 1.3.40.1 bouyer grepbehave = GREP_BASIC;
349 1.2 cjep break;
350 1.2 cjep }
351 1.2 cjep break;
352 1.2 cjep }
353 1.1 cjep
354 1.3.40.1 bouyer lastc = '\0';
355 1.3.40.1 bouyer newarg = 1;
356 1.3.40.1 bouyer prevoptind = 1;
357 1.3.40.1 bouyer needpattern = 1;
358 1.3.40.1 bouyer
359 1.3.40.1 bouyer eopts = getenv("GREP_OPTIONS");
360 1.3.40.1 bouyer
361 1.3.40.1 bouyer /* support for extra arguments in GREP_OPTIONS */
362 1.3.40.1 bouyer eargc = 0;
363 1.3.40.1 bouyer if (eopts != NULL) {
364 1.3.40.1 bouyer char *str;
365 1.3.40.1 bouyer
366 1.3.40.1 bouyer /* make an estimation of how many extra arguments we have */
367 1.3.40.1 bouyer for (unsigned int j = 0; j < strlen(eopts); j++)
368 1.3.40.1 bouyer if (eopts[j] == ' ')
369 1.3.40.1 bouyer eargc++;
370 1.3.40.1 bouyer
371 1.3.40.1 bouyer eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
372 1.3.40.1 bouyer
373 1.3.40.1 bouyer eargc = 0;
374 1.3.40.1 bouyer /* parse extra arguments */
375 1.3.40.1 bouyer while ((str = strsep(&eopts, " ")) != NULL)
376 1.3.40.1 bouyer eargv[eargc++] = grep_strdup(str);
377 1.3.40.1 bouyer
378 1.3.40.1 bouyer aargv = (char **)grep_calloc(eargc + argc + 1,
379 1.3.40.1 bouyer sizeof(char *));
380 1.3.40.1 bouyer
381 1.3.40.1 bouyer aargv[0] = argv[0];
382 1.3.40.1 bouyer for (i = 0; i < eargc; i++)
383 1.3.40.1 bouyer aargv[i + 1] = eargv[i];
384 1.3.40.1 bouyer for (int j = 1; j < argc; j++, i++)
385 1.3.40.1 bouyer aargv[i + 1] = argv[j];
386 1.3.40.1 bouyer
387 1.3.40.1 bouyer aargc = eargc + argc;
388 1.3.40.1 bouyer } else {
389 1.3.40.1 bouyer aargv = argv;
390 1.3.40.1 bouyer aargc = argc;
391 1.3.40.1 bouyer }
392 1.2 cjep
393 1.3.40.1 bouyer while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
394 1.3.40.1 bouyer -1)) {
395 1.3.40.1 bouyer switch (c) {
396 1.3.40.1 bouyer case '0': case '1': case '2': case '3': case '4':
397 1.3.40.1 bouyer case '5': case '6': case '7': case '8': case '9':
398 1.3.40.1 bouyer if (newarg || !isdigit(lastc))
399 1.3.40.1 bouyer Aflag = 0;
400 1.3.40.1 bouyer else if (Aflag > LLONG_MAX / 10) {
401 1.3.40.1 bouyer errno = ERANGE;
402 1.3.40.1 bouyer err(2, NULL);
403 1.3.40.1 bouyer }
404 1.3.40.1 bouyer Aflag = Bflag = (Aflag * 10) + (c - '0');
405 1.1 cjep break;
406 1.1 cjep case 'C':
407 1.3.40.1 bouyer if (optarg == NULL) {
408 1.1 cjep Aflag = Bflag = 2;
409 1.3.40.1 bouyer break;
410 1.2 cjep }
411 1.3.40.1 bouyer /* FALLTHROUGH */
412 1.3.40.1 bouyer case 'A':
413 1.3.40.1 bouyer /* FALLTHROUGH */
414 1.3.40.1 bouyer case 'B':
415 1.3.40.1 bouyer errno = 0;
416 1.3.40.1 bouyer l = strtoull(optarg, &ep, 10);
417 1.3.40.1 bouyer if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
418 1.3.40.1 bouyer ((errno == EINVAL) && (l == 0)))
419 1.3.40.1 bouyer err(2, NULL);
420 1.3.40.1 bouyer else if (ep[0] != '\0') {
421 1.3.40.1 bouyer errno = EINVAL;
422 1.3.40.1 bouyer err(2, NULL);
423 1.3.40.1 bouyer }
424 1.3.40.1 bouyer if (c == 'A')
425 1.3.40.1 bouyer Aflag = l;
426 1.3.40.1 bouyer else if (c == 'B')
427 1.3.40.1 bouyer Bflag = l;
428 1.3.40.1 bouyer else
429 1.3.40.1 bouyer Aflag = Bflag = l;
430 1.1 cjep break;
431 1.1 cjep case 'a':
432 1.3.40.1 bouyer binbehave = BINFILE_TEXT;
433 1.1 cjep break;
434 1.1 cjep case 'b':
435 1.3.40.1 bouyer bflag = true;
436 1.1 cjep break;
437 1.1 cjep case 'c':
438 1.3.40.1 bouyer cflag = true;
439 1.3.40.1 bouyer break;
440 1.3.40.1 bouyer case 'D':
441 1.3.40.1 bouyer if (strcasecmp(optarg, "skip") == 0)
442 1.3.40.1 bouyer devbehave = DEV_SKIP;
443 1.3.40.1 bouyer else if (strcasecmp(optarg, "read") == 0)
444 1.3.40.1 bouyer devbehave = DEV_READ;
445 1.3.40.1 bouyer else
446 1.3.40.1 bouyer errx(2, getstr(3), "--devices");
447 1.1 cjep break;
448 1.2 cjep case 'd':
449 1.3.40.1 bouyer if (strcasecmp("recurse", optarg) == 0) {
450 1.3.40.1 bouyer Hflag = true;
451 1.3.40.1 bouyer dirbehave = DIR_RECURSE;
452 1.3.40.1 bouyer } else if (strcasecmp("skip", optarg) == 0)
453 1.3.40.1 bouyer dirbehave = DIR_SKIP;
454 1.3.40.1 bouyer else if (strcasecmp("read", optarg) == 0)
455 1.3.40.1 bouyer dirbehave = DIR_READ;
456 1.3.40.1 bouyer else
457 1.3.40.1 bouyer errx(2, getstr(3), "--directories");
458 1.3.40.1 bouyer break;
459 1.3.40.1 bouyer case 'E':
460 1.3.40.1 bouyer grepbehave = GREP_EXTENDED;
461 1.2 cjep break;
462 1.1 cjep case 'e':
463 1.1 cjep add_pattern(optarg, strlen(optarg));
464 1.3.40.1 bouyer needpattern = 0;
465 1.3.40.1 bouyer break;
466 1.3.40.1 bouyer case 'F':
467 1.3.40.1 bouyer grepbehave = GREP_FIXED;
468 1.1 cjep break;
469 1.1 cjep case 'f':
470 1.1 cjep read_patterns(optarg);
471 1.3.40.1 bouyer needpattern = 0;
472 1.3.40.1 bouyer break;
473 1.3.40.1 bouyer case 'G':
474 1.3.40.1 bouyer grepbehave = GREP_BASIC;
475 1.3.40.1 bouyer break;
476 1.3.40.1 bouyer case 'H':
477 1.3.40.1 bouyer Hflag = true;
478 1.1 cjep break;
479 1.1 cjep case 'h':
480 1.3.40.1 bouyer Hflag = false;
481 1.3.40.1 bouyer hflag = true;
482 1.3.40.1 bouyer break;
483 1.3.40.1 bouyer case 'I':
484 1.3.40.1 bouyer binbehave = BINFILE_SKIP;
485 1.1 cjep break;
486 1.1 cjep case 'i':
487 1.1 cjep case 'y':
488 1.3.40.1 bouyer iflag = true;
489 1.1 cjep cflags |= REG_ICASE;
490 1.1 cjep break;
491 1.3.40.1 bouyer case 'J':
492 1.3.40.1 bouyer filebehave = FILE_BZIP;
493 1.3.40.1 bouyer break;
494 1.3.40.1 bouyer case 'L':
495 1.3.40.1 bouyer lflag = false;
496 1.3.40.1 bouyer Lflag = true;
497 1.3.40.1 bouyer break;
498 1.1 cjep case 'l':
499 1.3.40.1 bouyer Lflag = false;
500 1.3.40.1 bouyer lflag = true;
501 1.1 cjep break;
502 1.2 cjep case 'm':
503 1.3.40.1 bouyer mflag = true;
504 1.3.40.1 bouyer errno = 0;
505 1.3.40.1 bouyer mcount = strtoull(optarg, &ep, 10);
506 1.3.40.1 bouyer if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
507 1.3.40.1 bouyer ((errno == EINVAL) && (mcount == 0)))
508 1.3.40.1 bouyer err(2, NULL);
509 1.3.40.1 bouyer else if (ep[0] != '\0') {
510 1.3.40.1 bouyer errno = EINVAL;
511 1.3.40.1 bouyer err(2, NULL);
512 1.3.40.1 bouyer }
513 1.2 cjep break;
514 1.1 cjep case 'n':
515 1.3.40.1 bouyer nflag = true;
516 1.3.40.1 bouyer break;
517 1.3.40.1 bouyer case 'O':
518 1.3.40.1 bouyer linkbehave = LINK_EXPLICIT;
519 1.1 cjep break;
520 1.1 cjep case 'o':
521 1.3.40.1 bouyer oflag = true;
522 1.3.40.1 bouyer break;
523 1.3.40.1 bouyer case 'p':
524 1.3.40.1 bouyer linkbehave = LINK_SKIP;
525 1.1 cjep break;
526 1.1 cjep case 'q':
527 1.3.40.1 bouyer qflag = true;
528 1.3.40.1 bouyer break;
529 1.3.40.1 bouyer case 'S':
530 1.3.40.1 bouyer linkbehave = LINK_READ;
531 1.3.40.1 bouyer break;
532 1.3.40.1 bouyer case 'R':
533 1.3.40.1 bouyer case 'r':
534 1.3.40.1 bouyer dirbehave = DIR_RECURSE;
535 1.3.40.1 bouyer Hflag = true;
536 1.1 cjep break;
537 1.1 cjep case 's':
538 1.3.40.1 bouyer sflag = true;
539 1.3.40.1 bouyer break;
540 1.3.40.1 bouyer case 'U':
541 1.3.40.1 bouyer binbehave = BINFILE_BIN;
542 1.1 cjep break;
543 1.3.40.1 bouyer case 'u':
544 1.3.40.1 bouyer case MMAP_OPT:
545 1.3.40.1 bouyer /* noop, compatibility */
546 1.3.40.1 bouyer break;
547 1.3.40.1 bouyer case 'V':
548 1.3.40.1 bouyer printf(getstr(9), __progname, VERSION);
549 1.3.40.1 bouyer exit(0);
550 1.1 cjep case 'v':
551 1.3.40.1 bouyer vflag = true;
552 1.1 cjep break;
553 1.1 cjep case 'w':
554 1.3.40.1 bouyer wflag = true;
555 1.1 cjep break;
556 1.1 cjep case 'x':
557 1.3.40.1 bouyer xflag = true;
558 1.1 cjep break;
559 1.3.40.1 bouyer case 'Z':
560 1.3.40.1 bouyer filebehave = FILE_GZIP;
561 1.2 cjep break;
562 1.2 cjep case BIN_OPT:
563 1.3.40.1 bouyer if (strcasecmp("binary", optarg) == 0)
564 1.3.40.1 bouyer binbehave = BINFILE_BIN;
565 1.3.40.1 bouyer else if (strcasecmp("without-match", optarg) == 0)
566 1.3.40.1 bouyer binbehave = BINFILE_SKIP;
567 1.3.40.1 bouyer else if (strcasecmp("text", optarg) == 0)
568 1.3.40.1 bouyer binbehave = BINFILE_TEXT;
569 1.3.40.1 bouyer else
570 1.3.40.1 bouyer errx(2, getstr(3), "--binary-files");
571 1.2 cjep break;
572 1.3.40.1 bouyer case COLOR_OPT:
573 1.3.40.1 bouyer color = NULL;
574 1.3.40.1 bouyer if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
575 1.3.40.1 bouyer strcasecmp("tty", optarg) == 0 ||
576 1.3.40.1 bouyer strcasecmp("if-tty", optarg) == 0) {
577 1.3.40.1 bouyer char *term;
578 1.3.40.1 bouyer
579 1.3.40.1 bouyer term = getenv("TERM");
580 1.3.40.1 bouyer if (isatty(STDOUT_FILENO) && term != NULL &&
581 1.3.40.1 bouyer strcasecmp(term, "dumb") != 0)
582 1.3.40.1 bouyer color = init_color("01;31");
583 1.3.40.1 bouyer } else if (strcasecmp("always", optarg) == 0 ||
584 1.3.40.1 bouyer strcasecmp("yes", optarg) == 0 ||
585 1.3.40.1 bouyer strcasecmp("force", optarg) == 0) {
586 1.3.40.1 bouyer color = init_color("01;31");
587 1.3.40.1 bouyer } else if (strcasecmp("never", optarg) != 0 &&
588 1.3.40.1 bouyer strcasecmp("none", optarg) != 0 &&
589 1.3.40.1 bouyer strcasecmp("no", optarg) != 0)
590 1.3.40.1 bouyer errx(2, getstr(3), "--color");
591 1.2 cjep break;
592 1.3.40.1 bouyer case LABEL_OPT:
593 1.3.40.1 bouyer label = optarg;
594 1.2 cjep break;
595 1.2 cjep case LINEBUF_OPT:
596 1.3.40.1 bouyer lbflag = true;
597 1.3.40.1 bouyer break;
598 1.3.40.1 bouyer case NULL_OPT:
599 1.3.40.1 bouyer nullflag = true;
600 1.3.40.1 bouyer break;
601 1.3.40.1 bouyer case R_INCLUDE_OPT:
602 1.3.40.1 bouyer finclude = true;
603 1.3.40.1 bouyer add_fpattern(optarg, INCL_PAT);
604 1.3.40.1 bouyer break;
605 1.3.40.1 bouyer case R_EXCLUDE_OPT:
606 1.3.40.1 bouyer fexclude = true;
607 1.3.40.1 bouyer add_fpattern(optarg, EXCL_PAT);
608 1.3.40.1 bouyer break;
609 1.3.40.1 bouyer case R_DINCLUDE_OPT:
610 1.3.40.1 bouyer dinclude = true;
611 1.3.40.1 bouyer add_dpattern(optarg, INCL_PAT);
612 1.3.40.1 bouyer break;
613 1.3.40.1 bouyer case R_DEXCLUDE_OPT:
614 1.3.40.1 bouyer dexclude = true;
615 1.3.40.1 bouyer add_dpattern(optarg, EXCL_PAT);
616 1.2 cjep break;
617 1.2 cjep case HELP_OPT:
618 1.1 cjep default:
619 1.1 cjep usage();
620 1.1 cjep }
621 1.3.40.1 bouyer lastc = c;
622 1.3.40.1 bouyer newarg = optind != prevoptind;
623 1.3.40.1 bouyer prevoptind = optind;
624 1.1 cjep }
625 1.3.40.1 bouyer aargc -= optind;
626 1.3.40.1 bouyer aargv += optind;
627 1.1 cjep
628 1.3.40.1 bouyer /* Fail if we don't have any pattern */
629 1.3.40.1 bouyer if (aargc == 0 && needpattern)
630 1.1 cjep usage();
631 1.3.40.1 bouyer
632 1.3.40.1 bouyer /* Process patterns from command line */
633 1.3.40.1 bouyer if (aargc != 0 && needpattern) {
634 1.3.40.1 bouyer add_pattern(*aargv, strlen(*aargv));
635 1.3.40.1 bouyer --aargc;
636 1.3.40.1 bouyer ++aargv;
637 1.1 cjep }
638 1.1 cjep
639 1.3.40.1 bouyer switch (grepbehave) {
640 1.3.40.1 bouyer case GREP_FIXED:
641 1.3.40.1 bouyer case GREP_BASIC:
642 1.3.40.1 bouyer break;
643 1.3.40.1 bouyer case GREP_EXTENDED:
644 1.2 cjep cflags |= REG_EXTENDED;
645 1.3.40.1 bouyer break;
646 1.3.40.1 bouyer default:
647 1.3.40.1 bouyer /* NOTREACHED */
648 1.3.40.1 bouyer usage();
649 1.1 cjep }
650 1.1 cjep
651 1.3.40.1 bouyer fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
652 1.3.40.1 bouyer r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
653 1.3.40.1 bouyer /*
654 1.3.40.1 bouyer * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
655 1.3.40.1 bouyer * Optimizations should be done there.
656 1.3.40.1 bouyer */
657 1.3.40.1 bouyer /* Check if cheating is allowed (always is for fgrep). */
658 1.3.40.1 bouyer if (grepbehave == GREP_FIXED) {
659 1.3.40.1 bouyer for (i = 0; i < patterns; ++i)
660 1.3.40.1 bouyer fgrepcomp(&fg_pattern[i], pattern[i]);
661 1.3.40.1 bouyer } else {
662 1.3.40.1 bouyer for (i = 0; i < patterns; ++i) {
663 1.3.40.1 bouyer if (fastcomp(&fg_pattern[i], pattern[i])) {
664 1.3.40.1 bouyer /* Fall back to full regex library */
665 1.3.40.1 bouyer c = regcomp(&r_pattern[i], pattern[i], cflags);
666 1.3.40.1 bouyer if (c != 0) {
667 1.3.40.1 bouyer regerror(c, &r_pattern[i], re_error,
668 1.3.40.1 bouyer RE_ERROR_BUF);
669 1.3.40.1 bouyer errx(2, "%s", re_error);
670 1.3.40.1 bouyer }
671 1.3.40.1 bouyer }
672 1.3.40.1 bouyer }
673 1.3.40.1 bouyer }
674 1.2 cjep
675 1.2 cjep if (lbflag)
676 1.2 cjep setlinebuf(stdout);
677 1.2 cjep
678 1.3.40.1 bouyer if ((aargc == 0 || aargc == 1) && !Hflag)
679 1.3.40.1 bouyer hflag = true;
680 1.3.40.1 bouyer
681 1.3.40.1 bouyer if (aargc == 0)
682 1.3.40.1 bouyer exit(!procfile("-"));
683 1.3.40.1 bouyer
684 1.3.40.1 bouyer if (dirbehave == DIR_RECURSE)
685 1.3.40.1 bouyer c = grep_tree(aargv);
686 1.1 cjep else
687 1.3.40.1 bouyer for (c = 0; aargc--; ++aargv) {
688 1.3.40.1 bouyer if ((finclude || fexclude) && !file_matching(*aargv))
689 1.3.40.1 bouyer continue;
690 1.3.40.1 bouyer c+= procfile(*aargv);
691 1.3.40.1 bouyer }
692 1.1 cjep
693 1.3.40.1 bouyer #ifndef WITHOUT_NLS
694 1.3.40.1 bouyer catclose(catalog);
695 1.3.40.1 bouyer #endif
696 1.3.40.1 bouyer
697 1.3.40.1 bouyer /* Find out the correct return value according to the
698 1.3.40.1 bouyer results and the command line option. */
699 1.3.40.1 bouyer exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
700 1.1 cjep }
701