grep.c revision 211364
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 211364 2010-08-15 22:15:04Z 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 124211364Sgaborbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 125211364Sgaborbool fexclude, finclude; /* --exclude and --include */ 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); 239211364Sgabor memcpy(pattern[patterns], pat, len); 240211364Sgabor pattern[patterns][len] = '\0'; 241210389Sgabor ++patterns; 242210389Sgabor} 243210389Sgabor 244210389Sgabor/* 245210578Sgabor * Adds a file include/exclude pattern to the internal array. 246210389Sgabor */ 247210389Sgaborstatic void 248210578Sgaboradd_fpattern(const char *pat, int mode) 249210389Sgabor{ 250210389Sgabor 251210389Sgabor /* Increase size if necessary */ 252210578Sgabor if (fpatterns == fpattern_sz) { 253210578Sgabor fpattern_sz *= 2; 254210578Sgabor fpattern = grep_realloc(fpattern, ++fpattern_sz * 255210389Sgabor sizeof(struct epat)); 256210389Sgabor } 257210578Sgabor fpattern[fpatterns].pat = grep_strdup(pat); 258210578Sgabor fpattern[fpatterns].mode = mode; 259210578Sgabor ++fpatterns; 260210389Sgabor} 261210389Sgabor 262210389Sgabor/* 263210578Sgabor * Adds a directory include/exclude pattern to the internal array. 264210578Sgabor */ 265210578Sgaborstatic void 266210578Sgaboradd_dpattern(const char *pat, int mode) 267210578Sgabor{ 268210578Sgabor 269210578Sgabor /* Increase size if necessary */ 270210578Sgabor if (dpatterns == dpattern_sz) { 271210578Sgabor dpattern_sz *= 2; 272210578Sgabor dpattern = grep_realloc(dpattern, ++dpattern_sz * 273210578Sgabor sizeof(struct epat)); 274210578Sgabor } 275210578Sgabor dpattern[dpatterns].pat = grep_strdup(pat); 276210578Sgabor dpattern[dpatterns].mode = mode; 277210578Sgabor ++dpatterns; 278210578Sgabor} 279210578Sgabor 280210578Sgabor/* 281210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 282210389Sgabor */ 283210389Sgaborstatic void 284210389Sgaborread_patterns(const char *fn) 285210389Sgabor{ 286210389Sgabor FILE *f; 287210389Sgabor char *line; 288210389Sgabor size_t len; 289210389Sgabor 290210389Sgabor if ((f = fopen(fn, "r")) == NULL) 291210389Sgabor err(2, "%s", fn); 292210389Sgabor while ((line = fgetln(f, &len)) != NULL) 293210389Sgabor add_pattern(line, *line == '\n' ? 0 : len); 294210389Sgabor if (ferror(f)) 295210389Sgabor err(2, "%s", fn); 296210389Sgabor fclose(f); 297210389Sgabor} 298210389Sgabor 299210461Sgaborstatic inline const char * 300210461Sgaborinit_color(const char *d) 301210461Sgabor{ 302210461Sgabor char *c; 303210461Sgabor 304210461Sgabor c = getenv("GREP_COLOR"); 305210461Sgabor return (c != NULL ? c : d); 306210461Sgabor} 307210461Sgabor 308210389Sgaborint 309210389Sgabormain(int argc, char *argv[]) 310210389Sgabor{ 311210389Sgabor char **aargv, **eargv, *eopts; 312210389Sgabor char *ep; 313210389Sgabor unsigned long long l; 314210389Sgabor unsigned int aargc, eargc, i; 315210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 316210389Sgabor 317210389Sgabor setlocale(LC_ALL, ""); 318210389Sgabor 319210389Sgabor#ifndef WITHOUT_NLS 320210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 321210389Sgabor#endif 322210389Sgabor 323210389Sgabor /* Check what is the program name of the binary. In this 324210389Sgabor way we can have all the funcionalities in one binary 325210389Sgabor without the need of scripting and using ugly hacks. */ 326210389Sgabor switch (__progname[0]) { 327210389Sgabor case 'e': 328210389Sgabor grepbehave = GREP_EXTENDED; 329210389Sgabor break; 330210389Sgabor case 'f': 331210389Sgabor grepbehave = GREP_FIXED; 332210389Sgabor break; 333210389Sgabor case 'g': 334210389Sgabor grepbehave = GREP_BASIC; 335210389Sgabor break; 336210389Sgabor case 'z': 337210389Sgabor filebehave = FILE_GZIP; 338210389Sgabor switch(__progname[1]) { 339210389Sgabor case 'e': 340210389Sgabor grepbehave = GREP_EXTENDED; 341210389Sgabor break; 342210389Sgabor case 'f': 343210389Sgabor grepbehave = GREP_FIXED; 344210389Sgabor break; 345210389Sgabor case 'g': 346210389Sgabor grepbehave = GREP_BASIC; 347210389Sgabor break; 348210389Sgabor } 349210389Sgabor break; 350210389Sgabor } 351210389Sgabor 352210389Sgabor lastc = '\0'; 353210389Sgabor newarg = 1; 354210389Sgabor prevoptind = 1; 355210389Sgabor needpattern = 1; 356210389Sgabor 357210389Sgabor eopts = getenv("GREP_OPTIONS"); 358210389Sgabor 359211364Sgabor /* support for extra arguments in GREP_OPTIONS */ 360211364Sgabor eargc = 0; 361210389Sgabor if (eopts != NULL) { 362210389Sgabor char *str; 363210389Sgabor 364211364Sgabor /* make an estimation of how many extra arguments we have */ 365211364Sgabor for (unsigned int j = 0; j < strlen(eopts); j++) 366211364Sgabor if (eopts[j] == ' ') 367210389Sgabor eargc++; 368210389Sgabor 369210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 370210389Sgabor 371210389Sgabor eargc = 0; 372211364Sgabor /* parse extra arguments */ 373211364Sgabor while ((str = strsep(&eopts, " ")) != NULL) 374211364Sgabor eargv[eargc++] = grep_strdup(str); 375210389Sgabor 376210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 377210430Sdelphij sizeof(char *)); 378211364Sgabor 379210389Sgabor aargv[0] = argv[0]; 380211364Sgabor for (i = 0; i < eargc; i++) 381211364Sgabor aargv[i + 1] = eargv[i]; 382211364Sgabor for (int j = 1; j < argc; j++, i++) 383211364Sgabor aargv[i + 1] = argv[j]; 384210389Sgabor 385211364Sgabor aargc = eargc + argc; 386210389Sgabor } else { 387210389Sgabor aargv = argv; 388210389Sgabor aargc = argc; 389210389Sgabor } 390210389Sgabor 391210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 392210389Sgabor -1)) { 393210389Sgabor switch (c) { 394210389Sgabor case '0': case '1': case '2': case '3': case '4': 395210389Sgabor case '5': case '6': case '7': case '8': case '9': 396210389Sgabor if (newarg || !isdigit(lastc)) 397210389Sgabor Aflag = 0; 398210389Sgabor else if (Aflag > LLONG_MAX / 10) { 399210389Sgabor errno = ERANGE; 400210389Sgabor err(2, NULL); 401210389Sgabor } 402210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 403210389Sgabor break; 404210389Sgabor case 'C': 405210389Sgabor if (optarg == NULL) { 406210389Sgabor Aflag = Bflag = 2; 407210389Sgabor break; 408210389Sgabor } 409210389Sgabor /* FALLTHROUGH */ 410210389Sgabor case 'A': 411210389Sgabor /* FALLTHROUGH */ 412210389Sgabor case 'B': 413210389Sgabor errno = 0; 414210389Sgabor l = strtoull(optarg, &ep, 10); 415210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 416210389Sgabor ((errno == EINVAL) && (l == 0))) 417210389Sgabor err(2, NULL); 418210389Sgabor else if (ep[0] != '\0') { 419210389Sgabor errno = EINVAL; 420210389Sgabor err(2, NULL); 421210389Sgabor } 422210389Sgabor if (c == 'A') 423210389Sgabor Aflag = l; 424210389Sgabor else if (c == 'B') 425210389Sgabor Bflag = l; 426210389Sgabor else 427210389Sgabor Aflag = Bflag = l; 428210389Sgabor break; 429210389Sgabor case 'a': 430210389Sgabor binbehave = BINFILE_TEXT; 431210389Sgabor break; 432210389Sgabor case 'b': 433210389Sgabor bflag = true; 434210389Sgabor break; 435210389Sgabor case 'c': 436210389Sgabor cflag = true; 437210389Sgabor break; 438210389Sgabor case 'D': 439210461Sgabor if (strcasecmp(optarg, "skip") == 0) 440210389Sgabor devbehave = DEV_SKIP; 441210461Sgabor else if (strcasecmp(optarg, "read") == 0) 442210461Sgabor devbehave = DEV_READ; 443210622Sgabor else 444210622Sgabor errx(2, getstr(3), "--devices"); 445210389Sgabor break; 446210389Sgabor case 'd': 447210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 448210389Sgabor Hflag = true; 449210389Sgabor dirbehave = DIR_RECURSE; 450210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 451210389Sgabor dirbehave = DIR_SKIP; 452210461Sgabor else if (strcasecmp("read", optarg) == 0) 453210461Sgabor dirbehave = DIR_READ; 454210622Sgabor else 455210622Sgabor errx(2, getstr(3), "--directories"); 456210389Sgabor break; 457210389Sgabor case 'E': 458210389Sgabor grepbehave = GREP_EXTENDED; 459210389Sgabor break; 460210389Sgabor case 'e': 461210389Sgabor add_pattern(optarg, strlen(optarg)); 462210389Sgabor needpattern = 0; 463210389Sgabor break; 464210389Sgabor case 'F': 465210389Sgabor grepbehave = GREP_FIXED; 466210389Sgabor break; 467210389Sgabor case 'f': 468210389Sgabor read_patterns(optarg); 469210389Sgabor needpattern = 0; 470210389Sgabor break; 471210389Sgabor case 'G': 472210389Sgabor grepbehave = GREP_BASIC; 473210389Sgabor break; 474210389Sgabor case 'H': 475210389Sgabor Hflag = true; 476210389Sgabor break; 477210389Sgabor case 'h': 478210389Sgabor Hflag = false; 479210389Sgabor hflag = true; 480210389Sgabor break; 481210389Sgabor case 'I': 482210389Sgabor binbehave = BINFILE_SKIP; 483210389Sgabor break; 484210389Sgabor case 'i': 485210389Sgabor case 'y': 486210389Sgabor iflag = true; 487210389Sgabor cflags |= REG_ICASE; 488210389Sgabor break; 489210389Sgabor case 'J': 490210389Sgabor filebehave = FILE_BZIP; 491210389Sgabor break; 492210389Sgabor case 'L': 493210389Sgabor lflag = false; 494210461Sgabor Lflag = true; 495210389Sgabor break; 496210389Sgabor case 'l': 497210389Sgabor Lflag = false; 498210461Sgabor lflag = true; 499210389Sgabor break; 500210389Sgabor case 'm': 501210389Sgabor mflag = true; 502210389Sgabor errno = 0; 503210389Sgabor mcount = strtoull(optarg, &ep, 10); 504210389Sgabor if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 505210389Sgabor ((errno == EINVAL) && (mcount == 0))) 506210389Sgabor err(2, NULL); 507210389Sgabor else if (ep[0] != '\0') { 508210389Sgabor errno = EINVAL; 509210389Sgabor err(2, NULL); 510210389Sgabor } 511210389Sgabor break; 512210389Sgabor case 'n': 513210389Sgabor nflag = true; 514210389Sgabor break; 515210389Sgabor case 'O': 516210389Sgabor linkbehave = LINK_EXPLICIT; 517210389Sgabor break; 518210389Sgabor case 'o': 519210389Sgabor oflag = true; 520210389Sgabor break; 521210389Sgabor case 'p': 522210389Sgabor linkbehave = LINK_SKIP; 523210389Sgabor break; 524210389Sgabor case 'q': 525210389Sgabor qflag = true; 526210389Sgabor break; 527210389Sgabor case 'S': 528210461Sgabor linkbehave = LINK_READ; 529210389Sgabor break; 530210389Sgabor case 'R': 531210389Sgabor case 'r': 532210389Sgabor dirbehave = DIR_RECURSE; 533210389Sgabor Hflag = true; 534210389Sgabor break; 535210389Sgabor case 's': 536210389Sgabor sflag = true; 537210389Sgabor break; 538210389Sgabor case 'U': 539210389Sgabor binbehave = BINFILE_BIN; 540210389Sgabor break; 541210389Sgabor case 'u': 542210389Sgabor case MMAP_OPT: 543210389Sgabor /* noop, compatibility */ 544210389Sgabor break; 545210389Sgabor case 'V': 546210622Sgabor printf(getstr(9), __progname, VERSION); 547210389Sgabor exit(0); 548210389Sgabor case 'v': 549210389Sgabor vflag = true; 550210389Sgabor break; 551210389Sgabor case 'w': 552210389Sgabor wflag = true; 553210389Sgabor break; 554210389Sgabor case 'x': 555210389Sgabor xflag = true; 556210389Sgabor break; 557210389Sgabor case 'Z': 558210389Sgabor filebehave = FILE_GZIP; 559210389Sgabor break; 560210389Sgabor case BIN_OPT: 561210461Sgabor if (strcasecmp("binary", optarg) == 0) 562210389Sgabor binbehave = BINFILE_BIN; 563210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 564210389Sgabor binbehave = BINFILE_SKIP; 565210461Sgabor else if (strcasecmp("text", optarg) == 0) 566210389Sgabor binbehave = BINFILE_TEXT; 567210389Sgabor else 568210622Sgabor errx(2, getstr(3), "--binary-files"); 569210389Sgabor break; 570210389Sgabor case COLOR_OPT: 571210461Sgabor color = NULL; 572210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 573210461Sgabor strcasecmp("tty", optarg) == 0 || 574210461Sgabor strcasecmp("if-tty", optarg) == 0) { 575210461Sgabor char *term; 576210461Sgabor 577210461Sgabor term = getenv("TERM"); 578210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 579210461Sgabor strcasecmp(term, "dumb") != 0) 580210461Sgabor color = init_color("01;31"); 581210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 582210461Sgabor strcasecmp("yes", optarg) == 0 || 583210461Sgabor strcasecmp("force", optarg) == 0) { 584210461Sgabor color = init_color("01;31"); 585210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 586210461Sgabor strcasecmp("none", optarg) != 0 && 587210461Sgabor strcasecmp("no", optarg) != 0) 588210622Sgabor errx(2, getstr(3), "--color"); 589210389Sgabor break; 590210389Sgabor case LABEL_OPT: 591210389Sgabor label = optarg; 592210389Sgabor break; 593210389Sgabor case LINEBUF_OPT: 594210389Sgabor lbflag = true; 595210389Sgabor break; 596210389Sgabor case NULL_OPT: 597210389Sgabor nullflag = true; 598210389Sgabor break; 599210389Sgabor case R_INCLUDE_OPT: 600210578Sgabor finclude = true; 601210578Sgabor add_fpattern(optarg, INCL_PAT); 602210389Sgabor break; 603210389Sgabor case R_EXCLUDE_OPT: 604210578Sgabor fexclude = true; 605210578Sgabor add_fpattern(optarg, EXCL_PAT); 606210389Sgabor break; 607210389Sgabor case R_DINCLUDE_OPT: 608211364Sgabor dinclude = true; 609210578Sgabor add_dpattern(optarg, INCL_PAT); 610210389Sgabor break; 611210389Sgabor case R_DEXCLUDE_OPT: 612211364Sgabor dexclude = true; 613210578Sgabor add_dpattern(optarg, EXCL_PAT); 614210389Sgabor break; 615210389Sgabor case HELP_OPT: 616210389Sgabor default: 617210389Sgabor usage(); 618210389Sgabor } 619210389Sgabor lastc = c; 620210389Sgabor newarg = optind != prevoptind; 621210389Sgabor prevoptind = optind; 622210389Sgabor } 623210389Sgabor aargc -= optind; 624210389Sgabor aargv += optind; 625210389Sgabor 626210389Sgabor /* Fail if we don't have any pattern */ 627210389Sgabor if (aargc == 0 && needpattern) 628210389Sgabor usage(); 629210389Sgabor 630210389Sgabor /* Process patterns from command line */ 631210389Sgabor if (aargc != 0 && needpattern) { 632210389Sgabor add_pattern(*aargv, strlen(*aargv)); 633210389Sgabor --aargc; 634210389Sgabor ++aargv; 635210389Sgabor } 636210389Sgabor 637210389Sgabor switch (grepbehave) { 638210389Sgabor case GREP_FIXED: 639210389Sgabor case GREP_BASIC: 640210389Sgabor break; 641210389Sgabor case GREP_EXTENDED: 642210389Sgabor cflags |= REG_EXTENDED; 643210389Sgabor break; 644210389Sgabor default: 645210389Sgabor /* NOTREACHED */ 646210389Sgabor usage(); 647210389Sgabor } 648210389Sgabor 649210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 650210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 651210389Sgabor/* 652210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 653210389Sgabor * Optimizations should be done there. 654210389Sgabor */ 655210389Sgabor /* Check if cheating is allowed (always is for fgrep). */ 656210389Sgabor if (grepbehave == GREP_FIXED) { 657210389Sgabor for (i = 0; i < patterns; ++i) 658210389Sgabor fgrepcomp(&fg_pattern[i], pattern[i]); 659210389Sgabor } else { 660210389Sgabor for (i = 0; i < patterns; ++i) { 661210389Sgabor if (fastcomp(&fg_pattern[i], pattern[i])) { 662210389Sgabor /* Fall back to full regex library */ 663210389Sgabor c = regcomp(&r_pattern[i], pattern[i], cflags); 664210389Sgabor if (c != 0) { 665210389Sgabor regerror(c, &r_pattern[i], re_error, 666210389Sgabor RE_ERROR_BUF); 667210389Sgabor errx(2, "%s", re_error); 668210389Sgabor } 669210389Sgabor } 670210389Sgabor } 671210389Sgabor } 672210389Sgabor 673210389Sgabor if (lbflag) 674210389Sgabor setlinebuf(stdout); 675210389Sgabor 676210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 677210389Sgabor hflag = true; 678210389Sgabor 679210389Sgabor if (aargc == 0) 680210389Sgabor exit(!procfile("-")); 681210389Sgabor 682210389Sgabor if (dirbehave == DIR_RECURSE) 683210389Sgabor c = grep_tree(aargv); 684211364Sgabor else { 685211364Sgabor if (aargc == 1) 686211364Sgabor hflag = true; 687210578Sgabor for (c = 0; aargc--; ++aargv) { 688210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 689210578Sgabor continue; 690210389Sgabor c+= procfile(*aargv); 691210578Sgabor } 692211364Sgabor } 693210389Sgabor 694210389Sgabor#ifndef WITHOUT_NLS 695210389Sgabor catclose(catalog); 696210389Sgabor#endif 697210389Sgabor 698210389Sgabor /* Find out the correct return value according to the 699210389Sgabor results and the command line option. */ 700210389Sgabor exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 701210389Sgabor} 702