grep.c revision 210578
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 210578 2010-07-29 00:11:14Z 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", 64210389Sgabor/* 3*/ "unknown --color 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", 69210389Sgabor/* 8*/ "unknown --binary-files option", 70210389Sgabor/* 9*/ "Binary file %s matches\n", 71210389Sgabor/*10*/ "%s (BSD grep) %s\n", 72210389Sgabor}; 73210389Sgabor 74210389Sgabor/* Flags passed to regcomp() and regexec() */ 75210389Sgaborint cflags = 0; 76210389Sgaborint eflags = REG_STARTEND; 77210389Sgabor 78210389Sgabor/* Shortcut for matching all cases like empty regex */ 79210389Sgaborbool matchall; 80210389Sgabor 81210389Sgabor/* Searching patterns */ 82210389Sgaborunsigned int patterns, pattern_sz; 83210389Sgaborchar **pattern; 84210389Sgaborregex_t *r_pattern; 85210389Sgaborfastgrep_t *fg_pattern; 86210389Sgabor 87210389Sgabor/* Filename exclusion/inclusion patterns */ 88210578Sgaborunsigned int fpatterns, fpattern_sz; 89210578Sgaborunsigned int dpatterns, dpattern_sz; 90210578Sgaborstruct epat *dpattern, *fpattern; 91210389Sgabor 92210389Sgabor/* For regex errors */ 93210389Sgaborchar re_error[RE_ERROR_BUF + 1]; 94210389Sgabor 95210389Sgabor/* Command-line flags */ 96210389Sgaborunsigned long long Aflag; /* -A x: print x lines trailing each match */ 97210389Sgaborunsigned long long Bflag; /* -B x: print x lines leading each match */ 98210389Sgaborbool Hflag; /* -H: always print file name */ 99210389Sgaborbool Lflag; /* -L: only show names of files with no matches */ 100210389Sgaborbool bflag; /* -b: show block numbers for each match */ 101210389Sgaborbool cflag; /* -c: only show a count of matching lines */ 102210389Sgaborbool hflag; /* -h: don't print filename headers */ 103210389Sgaborbool iflag; /* -i: ignore case */ 104210389Sgaborbool lflag; /* -l: only show names of files with matches */ 105210389Sgaborbool mflag; /* -m x: stop reading the files after x matches */ 106210389Sgaborunsigned long long mcount; /* count for -m */ 107210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 108210389Sgaborbool oflag; /* -o: print only matching part */ 109210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 110210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 111210389Sgaborbool vflag; /* -v: only show non-matching lines */ 112210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 113210389Sgaborbool xflag; /* -x: pattern must match entire line */ 114210389Sgaborbool lbflag; /* --line-buffered */ 115210389Sgaborbool nullflag; /* --null */ 116210389Sgaborchar *label; /* --label */ 117210461Sgaborconst char *color; /* --color */ 118210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 119210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 120210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 121210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 122210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 123210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 124210389Sgabor 125210578Sgaborbool dexclude, dinclude; /* --exclude amd --include */ 126210578Sgaborbool fexclude, finclude; /* --exclude-dir and --include-dir */ 127210578Sgabor 128210389Sgaborenum { 129210389Sgabor BIN_OPT = CHAR_MAX + 1, 130210389Sgabor COLOR_OPT, 131210389Sgabor HELP_OPT, 132210389Sgabor MMAP_OPT, 133210389Sgabor LINEBUF_OPT, 134210389Sgabor LABEL_OPT, 135210389Sgabor NULL_OPT, 136210389Sgabor R_EXCLUDE_OPT, 137210389Sgabor R_INCLUDE_OPT, 138210389Sgabor R_DEXCLUDE_OPT, 139210389Sgabor R_DINCLUDE_OPT 140210389Sgabor}; 141210389Sgabor 142210461Sgaborstatic inline const char *init_color(const char *); 143210461Sgabor 144210389Sgabor/* Housekeeping */ 145210389Sgaborbool first = true; /* flag whether we are processing the first match */ 146210389Sgaborbool prev; /* flag whether or not the previous line matched */ 147210389Sgaborint tail; /* lines left to print */ 148210389Sgaborbool notfound; /* file not found */ 149210389Sgabor 150210389Sgaborextern char *__progname; 151210389Sgabor 152210389Sgabor/* 153210389Sgabor * Prints usage information and returns 2. 154210389Sgabor */ 155210389Sgaborstatic void 156210389Sgaborusage(void) 157210389Sgabor{ 158210389Sgabor fprintf(stderr, getstr(4), __progname); 159210389Sgabor fprintf(stderr, "%s", getstr(5)); 160210389Sgabor fprintf(stderr, "%s", getstr(5)); 161210389Sgabor fprintf(stderr, "%s", getstr(6)); 162210389Sgabor fprintf(stderr, "%s", getstr(7)); 163210389Sgabor exit(2); 164210389Sgabor} 165210389Sgabor 166210389Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy"; 167210389Sgabor 168210389Sgaborstruct option long_options[] = 169210389Sgabor{ 170210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 171210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 172210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 173210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 174210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 175210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 176210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 177210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 178210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 179210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 180210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 181210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 182210389Sgabor {"after-context", required_argument, NULL, 'A'}, 183210389Sgabor {"text", no_argument, NULL, 'a'}, 184210389Sgabor {"before-context", required_argument, NULL, 'B'}, 185210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 186210389Sgabor {"context", optional_argument, NULL, 'C'}, 187210389Sgabor {"count", no_argument, NULL, 'c'}, 188210389Sgabor {"devices", required_argument, NULL, 'D'}, 189210389Sgabor {"directories", required_argument, NULL, 'd'}, 190210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 191210389Sgabor {"regexp", required_argument, NULL, 'e'}, 192210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 193210389Sgabor {"file", required_argument, NULL, 'f'}, 194210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 195210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 196210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 197210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 198210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 199210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 200210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 201210389Sgabor {"max-count", required_argument, NULL, 'm'}, 202210389Sgabor {"line-number", no_argument, NULL, 'n'}, 203210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 204210389Sgabor {"quiet", no_argument, NULL, 'q'}, 205210389Sgabor {"silent", no_argument, NULL, 'q'}, 206210389Sgabor {"recursive", no_argument, NULL, 'r'}, 207210389Sgabor {"no-messages", no_argument, NULL, 's'}, 208210389Sgabor {"binary", no_argument, NULL, 'U'}, 209210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 210210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 211210389Sgabor {"version", no_argument, NULL, 'V'}, 212210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 213210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 214210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 215210389Sgabor {NULL, no_argument, NULL, 0} 216210389Sgabor}; 217210389Sgabor 218210389Sgabor/* 219210389Sgabor * Adds a searching pattern to the internal array. 220210389Sgabor */ 221210389Sgaborstatic void 222210389Sgaboradd_pattern(char *pat, size_t len) 223210389Sgabor{ 224210389Sgabor 225210389Sgabor /* Check if we can do a shortcut */ 226210389Sgabor if (len == 0 || matchall) { 227210389Sgabor matchall = true; 228210389Sgabor return; 229210389Sgabor } 230210389Sgabor /* Increase size if necessary */ 231210389Sgabor if (patterns == pattern_sz) { 232210389Sgabor pattern_sz *= 2; 233210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 234210389Sgabor sizeof(*pattern)); 235210389Sgabor } 236210389Sgabor if (len > 0 && pat[len - 1] == '\n') 237210389Sgabor --len; 238210389Sgabor /* pat may not be NUL-terminated */ 239210389Sgabor pattern[patterns] = grep_malloc(len + 1); 240210578Sgabor strlcpy(pattern[patterns], pat, len + 1); 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 359210389Sgabor eargc = 1; 360210389Sgabor if (eopts != NULL) { 361210389Sgabor char *str; 362210389Sgabor 363210389Sgabor for(i = 0; i < strlen(eopts); i++) 364210389Sgabor if (eopts[i] == ' ') 365210389Sgabor eargc++; 366210389Sgabor 367210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 368210389Sgabor 369210389Sgabor str = strtok(eopts, " "); 370210389Sgabor eargc = 0; 371210389Sgabor 372210389Sgabor while(str != NULL) { 373210389Sgabor eargv[++eargc] = (char *)grep_malloc(sizeof(char) * 374210389Sgabor (strlen(str) + 1)); 375210389Sgabor strlcpy(eargv[eargc], str, strlen(str) + 1); 376210389Sgabor str = strtok(NULL, " "); 377210389Sgabor } 378210389Sgabor eargv[++eargc] = NULL; 379210389Sgabor 380210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 381210430Sdelphij sizeof(char *)); 382210389Sgabor aargv[0] = argv[0]; 383210389Sgabor 384210389Sgabor for(i = 1; i < eargc; i++) 385210389Sgabor aargv[i] = eargv[i]; 386210389Sgabor for(int j = 1; j < argc; j++) 387210389Sgabor aargv[i++] = argv[j]; 388210389Sgabor 389210389Sgabor aargc = eargc + argc - 1; 390210389Sgabor 391210389Sgabor } else { 392210389Sgabor aargv = argv; 393210389Sgabor aargc = argc; 394210389Sgabor } 395210389Sgabor 396210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 397210389Sgabor -1)) { 398210389Sgabor switch (c) { 399210389Sgabor case '0': case '1': case '2': case '3': case '4': 400210389Sgabor case '5': case '6': case '7': case '8': case '9': 401210389Sgabor if (newarg || !isdigit(lastc)) 402210389Sgabor Aflag = 0; 403210389Sgabor else if (Aflag > LLONG_MAX / 10) { 404210389Sgabor errno = ERANGE; 405210389Sgabor err(2, NULL); 406210389Sgabor } 407210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 408210389Sgabor break; 409210389Sgabor case 'C': 410210389Sgabor if (optarg == NULL) { 411210389Sgabor Aflag = Bflag = 2; 412210389Sgabor break; 413210389Sgabor } 414210389Sgabor /* FALLTHROUGH */ 415210389Sgabor case 'A': 416210389Sgabor /* FALLTHROUGH */ 417210389Sgabor case 'B': 418210389Sgabor errno = 0; 419210389Sgabor l = strtoull(optarg, &ep, 10); 420210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 421210389Sgabor ((errno == EINVAL) && (l == 0))) 422210389Sgabor err(2, NULL); 423210389Sgabor else if (ep[0] != '\0') { 424210389Sgabor errno = EINVAL; 425210389Sgabor err(2, NULL); 426210389Sgabor } 427210389Sgabor if (c == 'A') 428210389Sgabor Aflag = l; 429210389Sgabor else if (c == 'B') 430210389Sgabor Bflag = l; 431210389Sgabor else 432210389Sgabor Aflag = Bflag = l; 433210389Sgabor break; 434210389Sgabor case 'a': 435210389Sgabor binbehave = BINFILE_TEXT; 436210389Sgabor break; 437210389Sgabor case 'b': 438210389Sgabor bflag = true; 439210389Sgabor break; 440210389Sgabor case 'c': 441210389Sgabor cflag = true; 442210389Sgabor break; 443210389Sgabor case 'D': 444210461Sgabor if (strcasecmp(optarg, "skip") == 0) 445210389Sgabor devbehave = DEV_SKIP; 446210461Sgabor else if (strcasecmp(optarg, "read") == 0) 447210461Sgabor devbehave = DEV_READ; 448210461Sgabor else { 449210461Sgabor errno = EINVAL; 450210461Sgabor err(2, NULL); 451210461Sgabor } 452210389Sgabor break; 453210389Sgabor case 'd': 454210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 455210389Sgabor Hflag = true; 456210389Sgabor dirbehave = DIR_RECURSE; 457210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 458210389Sgabor dirbehave = DIR_SKIP; 459210461Sgabor else if (strcasecmp("read", optarg) == 0) 460210461Sgabor dirbehave = DIR_READ; 461210461Sgabor else { 462210389Sgabor errno = EINVAL; 463210389Sgabor err(2, NULL); 464210389Sgabor } 465210389Sgabor break; 466210389Sgabor case 'E': 467210389Sgabor grepbehave = GREP_EXTENDED; 468210389Sgabor break; 469210389Sgabor case 'e': 470210389Sgabor add_pattern(optarg, strlen(optarg)); 471210389Sgabor needpattern = 0; 472210389Sgabor break; 473210389Sgabor case 'F': 474210389Sgabor grepbehave = GREP_FIXED; 475210389Sgabor break; 476210389Sgabor case 'f': 477210389Sgabor read_patterns(optarg); 478210389Sgabor needpattern = 0; 479210389Sgabor break; 480210389Sgabor case 'G': 481210389Sgabor grepbehave = GREP_BASIC; 482210389Sgabor break; 483210389Sgabor case 'H': 484210389Sgabor Hflag = true; 485210389Sgabor break; 486210389Sgabor case 'h': 487210389Sgabor Hflag = false; 488210389Sgabor hflag = true; 489210389Sgabor break; 490210389Sgabor case 'I': 491210389Sgabor binbehave = BINFILE_SKIP; 492210389Sgabor break; 493210389Sgabor case 'i': 494210389Sgabor case 'y': 495210389Sgabor iflag = true; 496210389Sgabor cflags |= REG_ICASE; 497210389Sgabor break; 498210389Sgabor case 'J': 499210389Sgabor filebehave = FILE_BZIP; 500210389Sgabor break; 501210389Sgabor case 'L': 502210389Sgabor lflag = false; 503210461Sgabor Lflag = true; 504210389Sgabor break; 505210389Sgabor case 'l': 506210389Sgabor Lflag = false; 507210461Sgabor lflag = true; 508210389Sgabor break; 509210389Sgabor case 'm': 510210389Sgabor mflag = true; 511210389Sgabor errno = 0; 512210389Sgabor mcount = strtoull(optarg, &ep, 10); 513210389Sgabor if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 514210389Sgabor ((errno == EINVAL) && (mcount == 0))) 515210389Sgabor err(2, NULL); 516210389Sgabor else if (ep[0] != '\0') { 517210389Sgabor errno = EINVAL; 518210389Sgabor err(2, NULL); 519210389Sgabor } 520210389Sgabor break; 521210389Sgabor case 'n': 522210389Sgabor nflag = true; 523210389Sgabor break; 524210389Sgabor case 'O': 525210389Sgabor linkbehave = LINK_EXPLICIT; 526210389Sgabor break; 527210389Sgabor case 'o': 528210389Sgabor oflag = true; 529210389Sgabor break; 530210389Sgabor case 'p': 531210389Sgabor linkbehave = LINK_SKIP; 532210389Sgabor break; 533210389Sgabor case 'q': 534210389Sgabor qflag = true; 535210389Sgabor break; 536210389Sgabor case 'S': 537210461Sgabor linkbehave = LINK_READ; 538210389Sgabor break; 539210389Sgabor case 'R': 540210389Sgabor case 'r': 541210389Sgabor dirbehave = DIR_RECURSE; 542210389Sgabor Hflag = true; 543210389Sgabor break; 544210389Sgabor case 's': 545210389Sgabor sflag = true; 546210389Sgabor break; 547210389Sgabor case 'U': 548210389Sgabor binbehave = BINFILE_BIN; 549210389Sgabor break; 550210389Sgabor case 'u': 551210389Sgabor case MMAP_OPT: 552210389Sgabor /* noop, compatibility */ 553210389Sgabor break; 554210389Sgabor case 'V': 555210389Sgabor printf(getstr(10), __progname, VERSION); 556210389Sgabor exit(0); 557210389Sgabor case 'v': 558210389Sgabor vflag = true; 559210389Sgabor break; 560210389Sgabor case 'w': 561210389Sgabor wflag = true; 562210389Sgabor break; 563210389Sgabor case 'x': 564210389Sgabor xflag = true; 565210389Sgabor break; 566210389Sgabor case 'Z': 567210389Sgabor filebehave = FILE_GZIP; 568210389Sgabor break; 569210389Sgabor case BIN_OPT: 570210461Sgabor if (strcasecmp("binary", optarg) == 0) 571210389Sgabor binbehave = BINFILE_BIN; 572210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 573210389Sgabor binbehave = BINFILE_SKIP; 574210461Sgabor else if (strcasecmp("text", optarg) == 0) 575210389Sgabor binbehave = BINFILE_TEXT; 576210389Sgabor else 577210389Sgabor errx(2, "%s", getstr(8)); 578210389Sgabor break; 579210389Sgabor case COLOR_OPT: 580210461Sgabor color = NULL; 581210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 582210461Sgabor strcasecmp("tty", optarg) == 0 || 583210461Sgabor strcasecmp("if-tty", optarg) == 0) { 584210461Sgabor char *term; 585210461Sgabor 586210461Sgabor term = getenv("TERM"); 587210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 588210461Sgabor strcasecmp(term, "dumb") != 0) 589210461Sgabor color = init_color("01;31"); 590210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 591210461Sgabor strcasecmp("yes", optarg) == 0 || 592210461Sgabor strcasecmp("force", optarg) == 0) { 593210461Sgabor color = init_color("01;31"); 594210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 595210461Sgabor strcasecmp("none", optarg) != 0 && 596210461Sgabor strcasecmp("no", optarg) != 0) 597210389Sgabor errx(2, "%s", getstr(3)); 598210389Sgabor break; 599210389Sgabor case LABEL_OPT: 600210389Sgabor label = optarg; 601210389Sgabor break; 602210389Sgabor case LINEBUF_OPT: 603210389Sgabor lbflag = true; 604210389Sgabor break; 605210389Sgabor case NULL_OPT: 606210389Sgabor nullflag = true; 607210389Sgabor break; 608210389Sgabor case R_INCLUDE_OPT: 609210578Sgabor finclude = true; 610210578Sgabor add_fpattern(optarg, INCL_PAT); 611210389Sgabor break; 612210389Sgabor case R_EXCLUDE_OPT: 613210578Sgabor fexclude = true; 614210578Sgabor add_fpattern(optarg, EXCL_PAT); 615210389Sgabor break; 616210389Sgabor case R_DINCLUDE_OPT: 617210578Sgabor dexclude = true; 618210578Sgabor add_dpattern(optarg, INCL_PAT); 619210389Sgabor break; 620210389Sgabor case R_DEXCLUDE_OPT: 621210578Sgabor dinclude = true; 622210578Sgabor add_dpattern(optarg, EXCL_PAT); 623210389Sgabor break; 624210389Sgabor case HELP_OPT: 625210389Sgabor default: 626210389Sgabor usage(); 627210389Sgabor } 628210389Sgabor lastc = c; 629210389Sgabor newarg = optind != prevoptind; 630210389Sgabor prevoptind = optind; 631210389Sgabor } 632210389Sgabor aargc -= optind; 633210389Sgabor aargv += optind; 634210389Sgabor 635210389Sgabor /* Fail if we don't have any pattern */ 636210389Sgabor if (aargc == 0 && needpattern) 637210389Sgabor usage(); 638210389Sgabor 639210389Sgabor /* Process patterns from command line */ 640210389Sgabor if (aargc != 0 && needpattern) { 641210389Sgabor add_pattern(*aargv, strlen(*aargv)); 642210389Sgabor --aargc; 643210389Sgabor ++aargv; 644210389Sgabor } 645210389Sgabor 646210389Sgabor switch (grepbehave) { 647210389Sgabor case GREP_FIXED: 648210389Sgabor case GREP_BASIC: 649210389Sgabor break; 650210389Sgabor case GREP_EXTENDED: 651210389Sgabor cflags |= REG_EXTENDED; 652210389Sgabor break; 653210389Sgabor default: 654210389Sgabor /* NOTREACHED */ 655210389Sgabor usage(); 656210389Sgabor } 657210389Sgabor 658210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 659210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 660210389Sgabor/* 661210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 662210389Sgabor * Optimizations should be done there. 663210389Sgabor */ 664210389Sgabor /* Check if cheating is allowed (always is for fgrep). */ 665210389Sgabor if (grepbehave == GREP_FIXED) { 666210389Sgabor for (i = 0; i < patterns; ++i) 667210389Sgabor fgrepcomp(&fg_pattern[i], pattern[i]); 668210389Sgabor } else { 669210389Sgabor for (i = 0; i < patterns; ++i) { 670210389Sgabor if (fastcomp(&fg_pattern[i], pattern[i])) { 671210389Sgabor /* Fall back to full regex library */ 672210389Sgabor c = regcomp(&r_pattern[i], pattern[i], cflags); 673210389Sgabor if (c != 0) { 674210389Sgabor regerror(c, &r_pattern[i], re_error, 675210389Sgabor RE_ERROR_BUF); 676210389Sgabor errx(2, "%s", re_error); 677210389Sgabor } 678210389Sgabor } 679210389Sgabor } 680210389Sgabor } 681210389Sgabor 682210389Sgabor if (lbflag) 683210389Sgabor setlinebuf(stdout); 684210389Sgabor 685210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 686210389Sgabor hflag = true; 687210389Sgabor 688210389Sgabor if (aargc == 0) 689210389Sgabor exit(!procfile("-")); 690210389Sgabor 691210389Sgabor if (dirbehave == DIR_RECURSE) 692210389Sgabor c = grep_tree(aargv); 693210389Sgabor else 694210578Sgabor for (c = 0; aargc--; ++aargv) { 695210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 696210578Sgabor continue; 697210389Sgabor c+= procfile(*aargv); 698210578Sgabor } 699210389Sgabor 700210389Sgabor#ifndef WITHOUT_NLS 701210389Sgabor catclose(catalog); 702210389Sgabor#endif 703210389Sgabor 704210389Sgabor /* Find out the correct return value according to the 705210389Sgabor results and the command line option. */ 706210389Sgabor exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 707210389Sgabor} 708