grep.c revision 210622
1210389Sgabor/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */ 2210389Sgabor 3210389Sgabor/*- 4210389Sgabor * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav 5210389Sgabor * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> 6210389Sgabor * All rights reserved. 7210389Sgabor * 8210389Sgabor * Redistribution and use in source and binary forms, with or without 9210389Sgabor * modification, are permitted provided that the following conditions 10210389Sgabor * are met: 11210389Sgabor * 1. Redistributions of source code must retain the above copyright 12210389Sgabor * notice, this list of conditions and the following disclaimer. 13210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright 14210389Sgabor * notice, this list of conditions and the following disclaimer in the 15210389Sgabor * documentation and/or other materials provided with the distribution. 16210389Sgabor * 17210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20210389Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27210389Sgabor * SUCH DAMAGE. 28210389Sgabor */ 29210389Sgabor 30210389Sgabor#include <sys/cdefs.h> 31210389Sgabor__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 210622 2010-07-29 18:02:57Z gabor $"); 32210389Sgabor 33210389Sgabor#include <sys/stat.h> 34210389Sgabor#include <sys/types.h> 35210389Sgabor 36210389Sgabor#include <ctype.h> 37210389Sgabor#include <err.h> 38210389Sgabor#include <errno.h> 39210389Sgabor#include <getopt.h> 40210389Sgabor#include <limits.h> 41210389Sgabor#include <libgen.h> 42210389Sgabor#include <locale.h> 43210389Sgabor#include <stdbool.h> 44210389Sgabor#include <stdio.h> 45210389Sgabor#include <stdlib.h> 46210389Sgabor#include <string.h> 47210389Sgabor#include <unistd.h> 48210389Sgabor 49210389Sgabor#include "grep.h" 50210389Sgabor 51210389Sgabor#ifndef WITHOUT_NLS 52210389Sgabor#include <nl_types.h> 53210389Sgabornl_catd catalog; 54210389Sgabor#endif 55210389Sgabor 56210389Sgabor/* 57210389Sgabor * Default messags to use when NLS is disabled or no catalogue 58210389Sgabor * is found. 59210389Sgabor */ 60210389Sgaborconst char *errstr[] = { 61210389Sgabor "", 62210389Sgabor/* 1*/ "(standard input)", 63210389Sgabor/* 2*/ "cannot read bzip2 compressed file", 64210622Sgabor/* 3*/ "unknown %s option", 65210389Sgabor/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", 66210389Sgabor/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", 67210389Sgabor/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", 68210389Sgabor/* 7*/ "\t[--null] [pattern] [file ...]\n", 69210622Sgabor/* 8*/ "Binary file %s matches\n", 70210622Sgabor/* 9*/ "%s (BSD grep) %s\n", 71210389Sgabor}; 72210389Sgabor 73210389Sgabor/* Flags passed to regcomp() and regexec() */ 74210389Sgaborint cflags = 0; 75210389Sgaborint eflags = REG_STARTEND; 76210389Sgabor 77210389Sgabor/* Shortcut for matching all cases like empty regex */ 78210389Sgaborbool matchall; 79210389Sgabor 80210389Sgabor/* Searching patterns */ 81210389Sgaborunsigned int patterns, pattern_sz; 82210389Sgaborchar **pattern; 83210389Sgaborregex_t *r_pattern; 84210389Sgaborfastgrep_t *fg_pattern; 85210389Sgabor 86210389Sgabor/* Filename exclusion/inclusion patterns */ 87210578Sgaborunsigned int fpatterns, fpattern_sz; 88210578Sgaborunsigned int dpatterns, dpattern_sz; 89210578Sgaborstruct epat *dpattern, *fpattern; 90210389Sgabor 91210389Sgabor/* For regex errors */ 92210389Sgaborchar re_error[RE_ERROR_BUF + 1]; 93210389Sgabor 94210389Sgabor/* Command-line flags */ 95210389Sgaborunsigned long long Aflag; /* -A x: print x lines trailing each match */ 96210389Sgaborunsigned long long Bflag; /* -B x: print x lines leading each match */ 97210389Sgaborbool Hflag; /* -H: always print file name */ 98210389Sgaborbool Lflag; /* -L: only show names of files with no matches */ 99210389Sgaborbool bflag; /* -b: show block numbers for each match */ 100210389Sgaborbool cflag; /* -c: only show a count of matching lines */ 101210389Sgaborbool hflag; /* -h: don't print filename headers */ 102210389Sgaborbool iflag; /* -i: ignore case */ 103210389Sgaborbool lflag; /* -l: only show names of files with matches */ 104210389Sgaborbool mflag; /* -m x: stop reading the files after x matches */ 105210389Sgaborunsigned long long mcount; /* count for -m */ 106210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 107210389Sgaborbool oflag; /* -o: print only matching part */ 108210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 109210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 110210389Sgaborbool vflag; /* -v: only show non-matching lines */ 111210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 112210389Sgaborbool xflag; /* -x: pattern must match entire line */ 113210389Sgaborbool lbflag; /* --line-buffered */ 114210389Sgaborbool nullflag; /* --null */ 115210389Sgaborchar *label; /* --label */ 116210461Sgaborconst char *color; /* --color */ 117210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 118210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 119210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 120210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 121210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 122210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 123210389Sgabor 124210578Sgaborbool dexclude, dinclude; /* --exclude amd --include */ 125210578Sgaborbool fexclude, finclude; /* --exclude-dir and --include-dir */ 126210578Sgabor 127210389Sgaborenum { 128210389Sgabor BIN_OPT = CHAR_MAX + 1, 129210389Sgabor COLOR_OPT, 130210389Sgabor HELP_OPT, 131210389Sgabor MMAP_OPT, 132210389Sgabor LINEBUF_OPT, 133210389Sgabor LABEL_OPT, 134210389Sgabor NULL_OPT, 135210389Sgabor R_EXCLUDE_OPT, 136210389Sgabor R_INCLUDE_OPT, 137210389Sgabor R_DEXCLUDE_OPT, 138210389Sgabor R_DINCLUDE_OPT 139210389Sgabor}; 140210389Sgabor 141210461Sgaborstatic inline const char *init_color(const char *); 142210461Sgabor 143210389Sgabor/* Housekeeping */ 144210389Sgaborbool first = true; /* flag whether we are processing the first match */ 145210389Sgaborbool prev; /* flag whether or not the previous line matched */ 146210389Sgaborint tail; /* lines left to print */ 147210389Sgaborbool notfound; /* file not found */ 148210389Sgabor 149210389Sgaborextern char *__progname; 150210389Sgabor 151210389Sgabor/* 152210389Sgabor * Prints usage information and returns 2. 153210389Sgabor */ 154210389Sgaborstatic void 155210389Sgaborusage(void) 156210389Sgabor{ 157210389Sgabor fprintf(stderr, getstr(4), __progname); 158210389Sgabor fprintf(stderr, "%s", getstr(5)); 159210389Sgabor fprintf(stderr, "%s", getstr(5)); 160210389Sgabor fprintf(stderr, "%s", getstr(6)); 161210389Sgabor fprintf(stderr, "%s", getstr(7)); 162210389Sgabor exit(2); 163210389Sgabor} 164210389Sgabor 165210389Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy"; 166210389Sgabor 167210389Sgaborstruct option long_options[] = 168210389Sgabor{ 169210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 170210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 171210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 172210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 173210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 174210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 175210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 176210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 177210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 178210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 179210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 180210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 181210389Sgabor {"after-context", required_argument, NULL, 'A'}, 182210389Sgabor {"text", no_argument, NULL, 'a'}, 183210389Sgabor {"before-context", required_argument, NULL, 'B'}, 184210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 185210389Sgabor {"context", optional_argument, NULL, 'C'}, 186210389Sgabor {"count", no_argument, NULL, 'c'}, 187210389Sgabor {"devices", required_argument, NULL, 'D'}, 188210389Sgabor {"directories", required_argument, NULL, 'd'}, 189210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 190210389Sgabor {"regexp", required_argument, NULL, 'e'}, 191210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 192210389Sgabor {"file", required_argument, NULL, 'f'}, 193210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 194210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 195210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 196210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 197210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 198210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 199210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 200210389Sgabor {"max-count", required_argument, NULL, 'm'}, 201210389Sgabor {"line-number", no_argument, NULL, 'n'}, 202210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 203210389Sgabor {"quiet", no_argument, NULL, 'q'}, 204210389Sgabor {"silent", no_argument, NULL, 'q'}, 205210389Sgabor {"recursive", no_argument, NULL, 'r'}, 206210389Sgabor {"no-messages", no_argument, NULL, 's'}, 207210389Sgabor {"binary", no_argument, NULL, 'U'}, 208210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 209210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 210210389Sgabor {"version", no_argument, NULL, 'V'}, 211210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 212210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 213210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 214210389Sgabor {NULL, no_argument, NULL, 0} 215210389Sgabor}; 216210389Sgabor 217210389Sgabor/* 218210389Sgabor * Adds a searching pattern to the internal array. 219210389Sgabor */ 220210389Sgaborstatic void 221210389Sgaboradd_pattern(char *pat, size_t len) 222210389Sgabor{ 223210389Sgabor 224210389Sgabor /* Check if we can do a shortcut */ 225210389Sgabor if (len == 0 || matchall) { 226210389Sgabor matchall = true; 227210389Sgabor return; 228210389Sgabor } 229210389Sgabor /* Increase size if necessary */ 230210389Sgabor if (patterns == pattern_sz) { 231210389Sgabor pattern_sz *= 2; 232210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 233210389Sgabor sizeof(*pattern)); 234210389Sgabor } 235210389Sgabor if (len > 0 && pat[len - 1] == '\n') 236210389Sgabor --len; 237210389Sgabor /* pat may not be NUL-terminated */ 238210389Sgabor pattern[patterns] = grep_malloc(len + 1); 239210578Sgabor strlcpy(pattern[patterns], pat, len + 1); 240210389Sgabor ++patterns; 241210389Sgabor} 242210389Sgabor 243210389Sgabor/* 244210578Sgabor * Adds a file include/exclude pattern to the internal array. 245210389Sgabor */ 246210389Sgaborstatic void 247210578Sgaboradd_fpattern(const char *pat, int mode) 248210389Sgabor{ 249210389Sgabor 250210389Sgabor /* Increase size if necessary */ 251210578Sgabor if (fpatterns == fpattern_sz) { 252210578Sgabor fpattern_sz *= 2; 253210578Sgabor fpattern = grep_realloc(fpattern, ++fpattern_sz * 254210389Sgabor sizeof(struct epat)); 255210389Sgabor } 256210578Sgabor fpattern[fpatterns].pat = grep_strdup(pat); 257210578Sgabor fpattern[fpatterns].mode = mode; 258210578Sgabor ++fpatterns; 259210389Sgabor} 260210389Sgabor 261210389Sgabor/* 262210578Sgabor * Adds a directory include/exclude pattern to the internal array. 263210578Sgabor */ 264210578Sgaborstatic void 265210578Sgaboradd_dpattern(const char *pat, int mode) 266210578Sgabor{ 267210578Sgabor 268210578Sgabor /* Increase size if necessary */ 269210578Sgabor if (dpatterns == dpattern_sz) { 270210578Sgabor dpattern_sz *= 2; 271210578Sgabor dpattern = grep_realloc(dpattern, ++dpattern_sz * 272210578Sgabor sizeof(struct epat)); 273210578Sgabor } 274210578Sgabor dpattern[dpatterns].pat = grep_strdup(pat); 275210578Sgabor dpattern[dpatterns].mode = mode; 276210578Sgabor ++dpatterns; 277210578Sgabor} 278210578Sgabor 279210578Sgabor/* 280210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 281210389Sgabor */ 282210389Sgaborstatic void 283210389Sgaborread_patterns(const char *fn) 284210389Sgabor{ 285210389Sgabor FILE *f; 286210389Sgabor char *line; 287210389Sgabor size_t len; 288210389Sgabor 289210389Sgabor if ((f = fopen(fn, "r")) == NULL) 290210389Sgabor err(2, "%s", fn); 291210389Sgabor while ((line = fgetln(f, &len)) != NULL) 292210389Sgabor add_pattern(line, *line == '\n' ? 0 : len); 293210389Sgabor if (ferror(f)) 294210389Sgabor err(2, "%s", fn); 295210389Sgabor fclose(f); 296210389Sgabor} 297210389Sgabor 298210461Sgaborstatic inline const char * 299210461Sgaborinit_color(const char *d) 300210461Sgabor{ 301210461Sgabor char *c; 302210461Sgabor 303210461Sgabor c = getenv("GREP_COLOR"); 304210461Sgabor return (c != NULL ? c : d); 305210461Sgabor} 306210461Sgabor 307210389Sgaborint 308210389Sgabormain(int argc, char *argv[]) 309210389Sgabor{ 310210389Sgabor char **aargv, **eargv, *eopts; 311210389Sgabor char *ep; 312210389Sgabor unsigned long long l; 313210389Sgabor unsigned int aargc, eargc, i; 314210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 315210389Sgabor 316210389Sgabor setlocale(LC_ALL, ""); 317210389Sgabor 318210389Sgabor#ifndef WITHOUT_NLS 319210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 320210389Sgabor#endif 321210389Sgabor 322210389Sgabor /* Check what is the program name of the binary. In this 323210389Sgabor way we can have all the funcionalities in one binary 324210389Sgabor without the need of scripting and using ugly hacks. */ 325210389Sgabor switch (__progname[0]) { 326210389Sgabor case 'e': 327210389Sgabor grepbehave = GREP_EXTENDED; 328210389Sgabor break; 329210389Sgabor case 'f': 330210389Sgabor grepbehave = GREP_FIXED; 331210389Sgabor break; 332210389Sgabor case 'g': 333210389Sgabor grepbehave = GREP_BASIC; 334210389Sgabor break; 335210389Sgabor case 'z': 336210389Sgabor filebehave = FILE_GZIP; 337210389Sgabor switch(__progname[1]) { 338210389Sgabor case 'e': 339210389Sgabor grepbehave = GREP_EXTENDED; 340210389Sgabor break; 341210389Sgabor case 'f': 342210389Sgabor grepbehave = GREP_FIXED; 343210389Sgabor break; 344210389Sgabor case 'g': 345210389Sgabor grepbehave = GREP_BASIC; 346210389Sgabor break; 347210389Sgabor } 348210389Sgabor break; 349210389Sgabor } 350210389Sgabor 351210389Sgabor lastc = '\0'; 352210389Sgabor newarg = 1; 353210389Sgabor prevoptind = 1; 354210389Sgabor needpattern = 1; 355210389Sgabor 356210389Sgabor eopts = getenv("GREP_OPTIONS"); 357210389Sgabor 358210389Sgabor eargc = 1; 359210389Sgabor if (eopts != NULL) { 360210389Sgabor char *str; 361210389Sgabor 362210389Sgabor for(i = 0; i < strlen(eopts); i++) 363210389Sgabor if (eopts[i] == ' ') 364210389Sgabor eargc++; 365210389Sgabor 366210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 367210389Sgabor 368210389Sgabor str = strtok(eopts, " "); 369210389Sgabor eargc = 0; 370210389Sgabor 371210389Sgabor while(str != NULL) { 372210389Sgabor eargv[++eargc] = (char *)grep_malloc(sizeof(char) * 373210389Sgabor (strlen(str) + 1)); 374210389Sgabor strlcpy(eargv[eargc], str, strlen(str) + 1); 375210389Sgabor str = strtok(NULL, " "); 376210389Sgabor } 377210389Sgabor eargv[++eargc] = NULL; 378210389Sgabor 379210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 380210430Sdelphij sizeof(char *)); 381210389Sgabor aargv[0] = argv[0]; 382210389Sgabor 383210389Sgabor for(i = 1; i < eargc; i++) 384210389Sgabor aargv[i] = eargv[i]; 385210389Sgabor for(int j = 1; j < argc; j++) 386210389Sgabor aargv[i++] = argv[j]; 387210389Sgabor 388210389Sgabor aargc = eargc + argc - 1; 389210389Sgabor 390210389Sgabor } else { 391210389Sgabor aargv = argv; 392210389Sgabor aargc = argc; 393210389Sgabor } 394210389Sgabor 395210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 396210389Sgabor -1)) { 397210389Sgabor switch (c) { 398210389Sgabor case '0': case '1': case '2': case '3': case '4': 399210389Sgabor case '5': case '6': case '7': case '8': case '9': 400210389Sgabor if (newarg || !isdigit(lastc)) 401210389Sgabor Aflag = 0; 402210389Sgabor else if (Aflag > LLONG_MAX / 10) { 403210389Sgabor errno = ERANGE; 404210389Sgabor err(2, NULL); 405210389Sgabor } 406210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 407210389Sgabor break; 408210389Sgabor case 'C': 409210389Sgabor if (optarg == NULL) { 410210389Sgabor Aflag = Bflag = 2; 411210389Sgabor break; 412210389Sgabor } 413210389Sgabor /* FALLTHROUGH */ 414210389Sgabor case 'A': 415210389Sgabor /* FALLTHROUGH */ 416210389Sgabor case 'B': 417210389Sgabor errno = 0; 418210389Sgabor l = strtoull(optarg, &ep, 10); 419210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 420210389Sgabor ((errno == EINVAL) && (l == 0))) 421210389Sgabor err(2, NULL); 422210389Sgabor else if (ep[0] != '\0') { 423210389Sgabor errno = EINVAL; 424210389Sgabor err(2, NULL); 425210389Sgabor } 426210389Sgabor if (c == 'A') 427210389Sgabor Aflag = l; 428210389Sgabor else if (c == 'B') 429210389Sgabor Bflag = l; 430210389Sgabor else 431210389Sgabor Aflag = Bflag = l; 432210389Sgabor break; 433210389Sgabor case 'a': 434210389Sgabor binbehave = BINFILE_TEXT; 435210389Sgabor break; 436210389Sgabor case 'b': 437210389Sgabor bflag = true; 438210389Sgabor break; 439210389Sgabor case 'c': 440210389Sgabor cflag = true; 441210389Sgabor break; 442210389Sgabor case 'D': 443210461Sgabor if (strcasecmp(optarg, "skip") == 0) 444210389Sgabor devbehave = DEV_SKIP; 445210461Sgabor else if (strcasecmp(optarg, "read") == 0) 446210461Sgabor devbehave = DEV_READ; 447210622Sgabor else 448210622Sgabor errx(2, getstr(3), "--devices"); 449210389Sgabor break; 450210389Sgabor case 'd': 451210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 452210389Sgabor Hflag = true; 453210389Sgabor dirbehave = DIR_RECURSE; 454210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 455210389Sgabor dirbehave = DIR_SKIP; 456210461Sgabor else if (strcasecmp("read", optarg) == 0) 457210461Sgabor dirbehave = DIR_READ; 458210622Sgabor else 459210622Sgabor errx(2, getstr(3), "--directories"); 460210389Sgabor break; 461210389Sgabor case 'E': 462210389Sgabor grepbehave = GREP_EXTENDED; 463210389Sgabor break; 464210389Sgabor case 'e': 465210389Sgabor add_pattern(optarg, strlen(optarg)); 466210389Sgabor needpattern = 0; 467210389Sgabor break; 468210389Sgabor case 'F': 469210389Sgabor grepbehave = GREP_FIXED; 470210389Sgabor break; 471210389Sgabor case 'f': 472210389Sgabor read_patterns(optarg); 473210389Sgabor needpattern = 0; 474210389Sgabor break; 475210389Sgabor case 'G': 476210389Sgabor grepbehave = GREP_BASIC; 477210389Sgabor break; 478210389Sgabor case 'H': 479210389Sgabor Hflag = true; 480210389Sgabor break; 481210389Sgabor case 'h': 482210389Sgabor Hflag = false; 483210389Sgabor hflag = true; 484210389Sgabor break; 485210389Sgabor case 'I': 486210389Sgabor binbehave = BINFILE_SKIP; 487210389Sgabor break; 488210389Sgabor case 'i': 489210389Sgabor case 'y': 490210389Sgabor iflag = true; 491210389Sgabor cflags |= REG_ICASE; 492210389Sgabor break; 493210389Sgabor case 'J': 494210389Sgabor filebehave = FILE_BZIP; 495210389Sgabor break; 496210389Sgabor case 'L': 497210389Sgabor lflag = false; 498210461Sgabor Lflag = true; 499210389Sgabor break; 500210389Sgabor case 'l': 501210389Sgabor Lflag = false; 502210461Sgabor lflag = true; 503210389Sgabor break; 504210389Sgabor case 'm': 505210389Sgabor mflag = true; 506210389Sgabor errno = 0; 507210389Sgabor mcount = strtoull(optarg, &ep, 10); 508210389Sgabor if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 509210389Sgabor ((errno == EINVAL) && (mcount == 0))) 510210389Sgabor err(2, NULL); 511210389Sgabor else if (ep[0] != '\0') { 512210389Sgabor errno = EINVAL; 513210389Sgabor err(2, NULL); 514210389Sgabor } 515210389Sgabor break; 516210389Sgabor case 'n': 517210389Sgabor nflag = true; 518210389Sgabor break; 519210389Sgabor case 'O': 520210389Sgabor linkbehave = LINK_EXPLICIT; 521210389Sgabor break; 522210389Sgabor case 'o': 523210389Sgabor oflag = true; 524210389Sgabor break; 525210389Sgabor case 'p': 526210389Sgabor linkbehave = LINK_SKIP; 527210389Sgabor break; 528210389Sgabor case 'q': 529210389Sgabor qflag = true; 530210389Sgabor break; 531210389Sgabor case 'S': 532210461Sgabor linkbehave = LINK_READ; 533210389Sgabor break; 534210389Sgabor case 'R': 535210389Sgabor case 'r': 536210389Sgabor dirbehave = DIR_RECURSE; 537210389Sgabor Hflag = true; 538210389Sgabor break; 539210389Sgabor case 's': 540210389Sgabor sflag = true; 541210389Sgabor break; 542210389Sgabor case 'U': 543210389Sgabor binbehave = BINFILE_BIN; 544210389Sgabor break; 545210389Sgabor case 'u': 546210389Sgabor case MMAP_OPT: 547210389Sgabor /* noop, compatibility */ 548210389Sgabor break; 549210389Sgabor case 'V': 550210622Sgabor printf(getstr(9), __progname, VERSION); 551210389Sgabor exit(0); 552210389Sgabor case 'v': 553210389Sgabor vflag = true; 554210389Sgabor break; 555210389Sgabor case 'w': 556210389Sgabor wflag = true; 557210389Sgabor break; 558210389Sgabor case 'x': 559210389Sgabor xflag = true; 560210389Sgabor break; 561210389Sgabor case 'Z': 562210389Sgabor filebehave = FILE_GZIP; 563210389Sgabor break; 564210389Sgabor case BIN_OPT: 565210461Sgabor if (strcasecmp("binary", optarg) == 0) 566210389Sgabor binbehave = BINFILE_BIN; 567210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 568210389Sgabor binbehave = BINFILE_SKIP; 569210461Sgabor else if (strcasecmp("text", optarg) == 0) 570210389Sgabor binbehave = BINFILE_TEXT; 571210389Sgabor else 572210622Sgabor errx(2, getstr(3), "--binary-files"); 573210389Sgabor break; 574210389Sgabor case COLOR_OPT: 575210461Sgabor color = NULL; 576210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 577210461Sgabor strcasecmp("tty", optarg) == 0 || 578210461Sgabor strcasecmp("if-tty", optarg) == 0) { 579210461Sgabor char *term; 580210461Sgabor 581210461Sgabor term = getenv("TERM"); 582210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 583210461Sgabor strcasecmp(term, "dumb") != 0) 584210461Sgabor color = init_color("01;31"); 585210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 586210461Sgabor strcasecmp("yes", optarg) == 0 || 587210461Sgabor strcasecmp("force", optarg) == 0) { 588210461Sgabor color = init_color("01;31"); 589210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 590210461Sgabor strcasecmp("none", optarg) != 0 && 591210461Sgabor strcasecmp("no", optarg) != 0) 592210622Sgabor errx(2, getstr(3), "--color"); 593210389Sgabor break; 594210389Sgabor case LABEL_OPT: 595210389Sgabor label = optarg; 596210389Sgabor break; 597210389Sgabor case LINEBUF_OPT: 598210389Sgabor lbflag = true; 599210389Sgabor break; 600210389Sgabor case NULL_OPT: 601210389Sgabor nullflag = true; 602210389Sgabor break; 603210389Sgabor case R_INCLUDE_OPT: 604210578Sgabor finclude = true; 605210578Sgabor add_fpattern(optarg, INCL_PAT); 606210389Sgabor break; 607210389Sgabor case R_EXCLUDE_OPT: 608210578Sgabor fexclude = true; 609210578Sgabor add_fpattern(optarg, EXCL_PAT); 610210389Sgabor break; 611210389Sgabor case R_DINCLUDE_OPT: 612210578Sgabor dexclude = true; 613210578Sgabor add_dpattern(optarg, INCL_PAT); 614210389Sgabor break; 615210389Sgabor case R_DEXCLUDE_OPT: 616210578Sgabor dinclude = true; 617210578Sgabor add_dpattern(optarg, EXCL_PAT); 618210389Sgabor break; 619210389Sgabor case HELP_OPT: 620210389Sgabor default: 621210389Sgabor usage(); 622210389Sgabor } 623210389Sgabor lastc = c; 624210389Sgabor newarg = optind != prevoptind; 625210389Sgabor prevoptind = optind; 626210389Sgabor } 627210389Sgabor aargc -= optind; 628210389Sgabor aargv += optind; 629210389Sgabor 630210389Sgabor /* Fail if we don't have any pattern */ 631210389Sgabor if (aargc == 0 && needpattern) 632210389Sgabor usage(); 633210389Sgabor 634210389Sgabor /* Process patterns from command line */ 635210389Sgabor if (aargc != 0 && needpattern) { 636210389Sgabor add_pattern(*aargv, strlen(*aargv)); 637210389Sgabor --aargc; 638210389Sgabor ++aargv; 639210389Sgabor } 640210389Sgabor 641210389Sgabor switch (grepbehave) { 642210389Sgabor case GREP_FIXED: 643210389Sgabor case GREP_BASIC: 644210389Sgabor break; 645210389Sgabor case GREP_EXTENDED: 646210389Sgabor cflags |= REG_EXTENDED; 647210389Sgabor break; 648210389Sgabor default: 649210389Sgabor /* NOTREACHED */ 650210389Sgabor usage(); 651210389Sgabor } 652210389Sgabor 653210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 654210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 655210389Sgabor/* 656210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 657210389Sgabor * Optimizations should be done there. 658210389Sgabor */ 659210389Sgabor /* Check if cheating is allowed (always is for fgrep). */ 660210389Sgabor if (grepbehave == GREP_FIXED) { 661210389Sgabor for (i = 0; i < patterns; ++i) 662210389Sgabor fgrepcomp(&fg_pattern[i], pattern[i]); 663210389Sgabor } else { 664210389Sgabor for (i = 0; i < patterns; ++i) { 665210389Sgabor if (fastcomp(&fg_pattern[i], pattern[i])) { 666210389Sgabor /* Fall back to full regex library */ 667210389Sgabor c = regcomp(&r_pattern[i], pattern[i], cflags); 668210389Sgabor if (c != 0) { 669210389Sgabor regerror(c, &r_pattern[i], re_error, 670210389Sgabor RE_ERROR_BUF); 671210389Sgabor errx(2, "%s", re_error); 672210389Sgabor } 673210389Sgabor } 674210389Sgabor } 675210389Sgabor } 676210389Sgabor 677210389Sgabor if (lbflag) 678210389Sgabor setlinebuf(stdout); 679210389Sgabor 680210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 681210389Sgabor hflag = true; 682210389Sgabor 683210389Sgabor if (aargc == 0) 684210389Sgabor exit(!procfile("-")); 685210389Sgabor 686210389Sgabor if (dirbehave == DIR_RECURSE) 687210389Sgabor c = grep_tree(aargv); 688210389Sgabor else 689210578Sgabor for (c = 0; aargc--; ++aargv) { 690210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 691210578Sgabor continue; 692210389Sgabor c+= procfile(*aargv); 693210578Sgabor } 694210389Sgabor 695210389Sgabor#ifndef WITHOUT_NLS 696210389Sgabor catclose(catalog); 697210389Sgabor#endif 698210389Sgabor 699210389Sgabor /* Find out the correct return value according to the 700210389Sgabor results and the command line option. */ 701210389Sgabor exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 702210389Sgabor} 703