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