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