grep.c revision 210461
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 210461 2010-07-25 08:42:18Z 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 */ 88210389Sgaborunsigned int epatterns, epattern_sz; 89210389Sgaborstruct epat *epattern; 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 */ 115210389Sgaborbool exclflag; /* --exclude */ 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 125210389Sgaborenum { 126210389Sgabor BIN_OPT = CHAR_MAX + 1, 127210389Sgabor COLOR_OPT, 128210389Sgabor HELP_OPT, 129210389Sgabor MMAP_OPT, 130210389Sgabor LINEBUF_OPT, 131210389Sgabor LABEL_OPT, 132210389Sgabor NULL_OPT, 133210389Sgabor R_EXCLUDE_OPT, 134210389Sgabor R_INCLUDE_OPT, 135210389Sgabor R_DEXCLUDE_OPT, 136210389Sgabor R_DINCLUDE_OPT 137210389Sgabor}; 138210389Sgabor 139210461Sgaborstatic inline const char *init_color(const char *); 140210461Sgabor 141210389Sgabor/* Housekeeping */ 142210389Sgaborbool first = true; /* flag whether we are processing the first match */ 143210389Sgaborbool prev; /* flag whether or not the previous line matched */ 144210389Sgaborint tail; /* lines left to print */ 145210389Sgaborbool notfound; /* file not found */ 146210389Sgabor 147210389Sgaborextern char *__progname; 148210389Sgabor 149210389Sgabor/* 150210389Sgabor * Prints usage information and returns 2. 151210389Sgabor */ 152210389Sgaborstatic void 153210389Sgaborusage(void) 154210389Sgabor{ 155210389Sgabor fprintf(stderr, getstr(4), __progname); 156210389Sgabor fprintf(stderr, "%s", getstr(5)); 157210389Sgabor fprintf(stderr, "%s", getstr(5)); 158210389Sgabor fprintf(stderr, "%s", getstr(6)); 159210389Sgabor fprintf(stderr, "%s", getstr(7)); 160210389Sgabor exit(2); 161210389Sgabor} 162210389Sgabor 163210389Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy"; 164210389Sgabor 165210389Sgaborstruct option long_options[] = 166210389Sgabor{ 167210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 168210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 169210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 170210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 171210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 172210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 173210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 174210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 175210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 176210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 177210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 178210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 179210389Sgabor {"after-context", required_argument, NULL, 'A'}, 180210389Sgabor {"text", no_argument, NULL, 'a'}, 181210389Sgabor {"before-context", required_argument, NULL, 'B'}, 182210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 183210389Sgabor {"context", optional_argument, NULL, 'C'}, 184210389Sgabor {"count", no_argument, NULL, 'c'}, 185210389Sgabor {"devices", required_argument, NULL, 'D'}, 186210389Sgabor {"directories", required_argument, NULL, 'd'}, 187210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 188210389Sgabor {"regexp", required_argument, NULL, 'e'}, 189210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 190210389Sgabor {"file", required_argument, NULL, 'f'}, 191210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 192210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 193210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 194210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 195210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 196210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 197210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 198210389Sgabor {"max-count", required_argument, NULL, 'm'}, 199210389Sgabor {"line-number", no_argument, NULL, 'n'}, 200210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 201210389Sgabor {"quiet", no_argument, NULL, 'q'}, 202210389Sgabor {"silent", no_argument, NULL, 'q'}, 203210389Sgabor {"recursive", no_argument, NULL, 'r'}, 204210389Sgabor {"no-messages", no_argument, NULL, 's'}, 205210389Sgabor {"binary", no_argument, NULL, 'U'}, 206210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 207210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 208210389Sgabor {"version", no_argument, NULL, 'V'}, 209210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 210210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 211210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 212210389Sgabor {NULL, no_argument, NULL, 0} 213210389Sgabor}; 214210389Sgabor 215210389Sgabor/* 216210389Sgabor * Adds a searching pattern to the internal array. 217210389Sgabor */ 218210389Sgaborstatic void 219210389Sgaboradd_pattern(char *pat, size_t len) 220210389Sgabor{ 221210389Sgabor 222210389Sgabor /* Check if we can do a shortcut */ 223210389Sgabor if (len == 0 || matchall) { 224210389Sgabor matchall = true; 225210389Sgabor return; 226210389Sgabor } 227210389Sgabor /* Increase size if necessary */ 228210389Sgabor if (patterns == pattern_sz) { 229210389Sgabor pattern_sz *= 2; 230210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 231210389Sgabor sizeof(*pattern)); 232210389Sgabor } 233210389Sgabor if (len > 0 && pat[len - 1] == '\n') 234210389Sgabor --len; 235210389Sgabor /* pat may not be NUL-terminated */ 236210389Sgabor pattern[patterns] = grep_malloc(len + 1); 237210389Sgabor memcpy(pattern[patterns], pat, len); 238210389Sgabor pattern[patterns][len] = '\0'; 239210389Sgabor ++patterns; 240210389Sgabor} 241210389Sgabor 242210389Sgabor/* 243210389Sgabor * Adds an include/exclude pattern to the internal array. 244210389Sgabor */ 245210389Sgaborstatic void 246210389Sgaboradd_epattern(char *pat, size_t len, int type, int mode) 247210389Sgabor{ 248210389Sgabor 249210389Sgabor /* Increase size if necessary */ 250210389Sgabor if (epatterns == epattern_sz) { 251210389Sgabor epattern_sz *= 2; 252210389Sgabor epattern = grep_realloc(epattern, ++epattern_sz * 253210389Sgabor sizeof(struct epat)); 254210389Sgabor } 255210389Sgabor if (len > 0 && pat[len - 1] == '\n') 256210389Sgabor --len; 257210389Sgabor epattern[epatterns].pat = grep_malloc(len + 1); 258210389Sgabor memcpy(epattern[epatterns].pat, pat, len); 259210389Sgabor epattern[epatterns].pat[len] = '\0'; 260210389Sgabor epattern[epatterns].type = type; 261210389Sgabor epattern[epatterns].mode = mode; 262210389Sgabor ++epatterns; 263210389Sgabor} 264210389Sgabor 265210389Sgabor/* 266210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 267210389Sgabor */ 268210389Sgaborstatic void 269210389Sgaborread_patterns(const char *fn) 270210389Sgabor{ 271210389Sgabor FILE *f; 272210389Sgabor char *line; 273210389Sgabor size_t len; 274210389Sgabor 275210389Sgabor if ((f = fopen(fn, "r")) == NULL) 276210389Sgabor err(2, "%s", fn); 277210389Sgabor while ((line = fgetln(f, &len)) != NULL) 278210389Sgabor add_pattern(line, *line == '\n' ? 0 : len); 279210389Sgabor if (ferror(f)) 280210389Sgabor err(2, "%s", fn); 281210389Sgabor fclose(f); 282210389Sgabor} 283210389Sgabor 284210461Sgaborstatic inline const char * 285210461Sgaborinit_color(const char *d) 286210461Sgabor{ 287210461Sgabor char *c; 288210461Sgabor 289210461Sgabor c = getenv("GREP_COLOR"); 290210461Sgabor return (c != NULL ? c : d); 291210461Sgabor} 292210461Sgabor 293210389Sgaborint 294210389Sgabormain(int argc, char *argv[]) 295210389Sgabor{ 296210389Sgabor char **aargv, **eargv, *eopts; 297210389Sgabor char *ep; 298210389Sgabor unsigned long long l; 299210389Sgabor unsigned int aargc, eargc, i; 300210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 301210389Sgabor 302210389Sgabor setlocale(LC_ALL, ""); 303210389Sgabor 304210389Sgabor#ifndef WITHOUT_NLS 305210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 306210389Sgabor#endif 307210389Sgabor 308210389Sgabor /* Check what is the program name of the binary. In this 309210389Sgabor way we can have all the funcionalities in one binary 310210389Sgabor without the need of scripting and using ugly hacks. */ 311210389Sgabor switch (__progname[0]) { 312210389Sgabor case 'e': 313210389Sgabor grepbehave = GREP_EXTENDED; 314210389Sgabor break; 315210389Sgabor case 'f': 316210389Sgabor grepbehave = GREP_FIXED; 317210389Sgabor break; 318210389Sgabor case 'g': 319210389Sgabor grepbehave = GREP_BASIC; 320210389Sgabor break; 321210389Sgabor case 'z': 322210389Sgabor filebehave = FILE_GZIP; 323210389Sgabor switch(__progname[1]) { 324210389Sgabor case 'e': 325210389Sgabor grepbehave = GREP_EXTENDED; 326210389Sgabor break; 327210389Sgabor case 'f': 328210389Sgabor grepbehave = GREP_FIXED; 329210389Sgabor break; 330210389Sgabor case 'g': 331210389Sgabor grepbehave = GREP_BASIC; 332210389Sgabor break; 333210389Sgabor } 334210389Sgabor break; 335210389Sgabor } 336210389Sgabor 337210389Sgabor lastc = '\0'; 338210389Sgabor newarg = 1; 339210389Sgabor prevoptind = 1; 340210389Sgabor needpattern = 1; 341210389Sgabor 342210389Sgabor eopts = getenv("GREP_OPTIONS"); 343210389Sgabor 344210389Sgabor eargc = 1; 345210389Sgabor if (eopts != NULL) { 346210389Sgabor char *str; 347210389Sgabor 348210389Sgabor for(i = 0; i < strlen(eopts); i++) 349210389Sgabor if (eopts[i] == ' ') 350210389Sgabor eargc++; 351210389Sgabor 352210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 353210389Sgabor 354210389Sgabor str = strtok(eopts, " "); 355210389Sgabor eargc = 0; 356210389Sgabor 357210389Sgabor while(str != NULL) { 358210389Sgabor eargv[++eargc] = (char *)grep_malloc(sizeof(char) * 359210389Sgabor (strlen(str) + 1)); 360210389Sgabor strlcpy(eargv[eargc], str, strlen(str) + 1); 361210389Sgabor str = strtok(NULL, " "); 362210389Sgabor } 363210389Sgabor eargv[++eargc] = NULL; 364210389Sgabor 365210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 366210430Sdelphij sizeof(char *)); 367210389Sgabor aargv[0] = argv[0]; 368210389Sgabor 369210389Sgabor for(i = 1; i < eargc; i++) 370210389Sgabor aargv[i] = eargv[i]; 371210389Sgabor for(int j = 1; j < argc; j++) 372210389Sgabor aargv[i++] = argv[j]; 373210389Sgabor 374210389Sgabor aargc = eargc + argc - 1; 375210389Sgabor 376210389Sgabor } else { 377210389Sgabor aargv = argv; 378210389Sgabor aargc = argc; 379210389Sgabor } 380210389Sgabor 381210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 382210389Sgabor -1)) { 383210389Sgabor switch (c) { 384210389Sgabor case '0': case '1': case '2': case '3': case '4': 385210389Sgabor case '5': case '6': case '7': case '8': case '9': 386210389Sgabor if (newarg || !isdigit(lastc)) 387210389Sgabor Aflag = 0; 388210389Sgabor else if (Aflag > LLONG_MAX / 10) { 389210389Sgabor errno = ERANGE; 390210389Sgabor err(2, NULL); 391210389Sgabor } 392210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 393210389Sgabor break; 394210389Sgabor case 'C': 395210389Sgabor if (optarg == NULL) { 396210389Sgabor Aflag = Bflag = 2; 397210389Sgabor break; 398210389Sgabor } 399210389Sgabor /* FALLTHROUGH */ 400210389Sgabor case 'A': 401210389Sgabor /* FALLTHROUGH */ 402210389Sgabor case 'B': 403210389Sgabor errno = 0; 404210389Sgabor l = strtoull(optarg, &ep, 10); 405210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 406210389Sgabor ((errno == EINVAL) && (l == 0))) 407210389Sgabor err(2, NULL); 408210389Sgabor else if (ep[0] != '\0') { 409210389Sgabor errno = EINVAL; 410210389Sgabor err(2, NULL); 411210389Sgabor } 412210389Sgabor if (c == 'A') 413210389Sgabor Aflag = l; 414210389Sgabor else if (c == 'B') 415210389Sgabor Bflag = l; 416210389Sgabor else 417210389Sgabor Aflag = Bflag = l; 418210389Sgabor break; 419210389Sgabor case 'a': 420210389Sgabor binbehave = BINFILE_TEXT; 421210389Sgabor break; 422210389Sgabor case 'b': 423210389Sgabor bflag = true; 424210389Sgabor break; 425210389Sgabor case 'c': 426210389Sgabor cflag = true; 427210389Sgabor break; 428210389Sgabor case 'D': 429210461Sgabor if (strcasecmp(optarg, "skip") == 0) 430210389Sgabor devbehave = DEV_SKIP; 431210461Sgabor else if (strcasecmp(optarg, "read") == 0) 432210461Sgabor devbehave = DEV_READ; 433210461Sgabor else { 434210461Sgabor errno = EINVAL; 435210461Sgabor err(2, NULL); 436210461Sgabor } 437210389Sgabor break; 438210389Sgabor case 'd': 439210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 440210389Sgabor Hflag = true; 441210389Sgabor dirbehave = DIR_RECURSE; 442210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 443210389Sgabor dirbehave = DIR_SKIP; 444210461Sgabor else if (strcasecmp("read", optarg) == 0) 445210461Sgabor dirbehave = DIR_READ; 446210461Sgabor else { 447210389Sgabor errno = EINVAL; 448210389Sgabor err(2, NULL); 449210389Sgabor } 450210389Sgabor break; 451210389Sgabor case 'E': 452210389Sgabor grepbehave = GREP_EXTENDED; 453210389Sgabor break; 454210389Sgabor case 'e': 455210389Sgabor add_pattern(optarg, strlen(optarg)); 456210389Sgabor needpattern = 0; 457210389Sgabor break; 458210389Sgabor case 'F': 459210389Sgabor grepbehave = GREP_FIXED; 460210389Sgabor break; 461210389Sgabor case 'f': 462210389Sgabor read_patterns(optarg); 463210389Sgabor needpattern = 0; 464210389Sgabor break; 465210389Sgabor case 'G': 466210389Sgabor grepbehave = GREP_BASIC; 467210389Sgabor break; 468210389Sgabor case 'H': 469210389Sgabor Hflag = true; 470210389Sgabor break; 471210389Sgabor case 'h': 472210389Sgabor Hflag = false; 473210389Sgabor hflag = true; 474210389Sgabor break; 475210389Sgabor case 'I': 476210389Sgabor binbehave = BINFILE_SKIP; 477210389Sgabor break; 478210389Sgabor case 'i': 479210389Sgabor case 'y': 480210389Sgabor iflag = true; 481210389Sgabor cflags |= REG_ICASE; 482210389Sgabor break; 483210389Sgabor case 'J': 484210389Sgabor filebehave = FILE_BZIP; 485210389Sgabor break; 486210389Sgabor case 'L': 487210389Sgabor lflag = false; 488210461Sgabor Lflag = true; 489210389Sgabor break; 490210389Sgabor case 'l': 491210389Sgabor Lflag = false; 492210461Sgabor lflag = true; 493210389Sgabor break; 494210389Sgabor case 'm': 495210389Sgabor mflag = true; 496210389Sgabor errno = 0; 497210389Sgabor mcount = strtoull(optarg, &ep, 10); 498210389Sgabor if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 499210389Sgabor ((errno == EINVAL) && (mcount == 0))) 500210389Sgabor err(2, NULL); 501210389Sgabor else if (ep[0] != '\0') { 502210389Sgabor errno = EINVAL; 503210389Sgabor err(2, NULL); 504210389Sgabor } 505210389Sgabor break; 506210389Sgabor case 'n': 507210389Sgabor nflag = true; 508210389Sgabor break; 509210389Sgabor case 'O': 510210389Sgabor linkbehave = LINK_EXPLICIT; 511210389Sgabor break; 512210389Sgabor case 'o': 513210389Sgabor oflag = true; 514210389Sgabor break; 515210389Sgabor case 'p': 516210389Sgabor linkbehave = LINK_SKIP; 517210389Sgabor break; 518210389Sgabor case 'q': 519210389Sgabor qflag = true; 520210389Sgabor break; 521210389Sgabor case 'S': 522210461Sgabor linkbehave = LINK_READ; 523210389Sgabor break; 524210389Sgabor case 'R': 525210389Sgabor case 'r': 526210389Sgabor dirbehave = DIR_RECURSE; 527210389Sgabor Hflag = true; 528210389Sgabor break; 529210389Sgabor case 's': 530210389Sgabor sflag = true; 531210389Sgabor break; 532210389Sgabor case 'U': 533210389Sgabor binbehave = BINFILE_BIN; 534210389Sgabor break; 535210389Sgabor case 'u': 536210389Sgabor case MMAP_OPT: 537210389Sgabor /* noop, compatibility */ 538210389Sgabor break; 539210389Sgabor case 'V': 540210389Sgabor printf(getstr(10), __progname, VERSION); 541210389Sgabor exit(0); 542210389Sgabor case 'v': 543210389Sgabor vflag = true; 544210389Sgabor break; 545210389Sgabor case 'w': 546210389Sgabor wflag = true; 547210389Sgabor break; 548210389Sgabor case 'x': 549210389Sgabor xflag = true; 550210389Sgabor break; 551210389Sgabor case 'Z': 552210389Sgabor filebehave = FILE_GZIP; 553210389Sgabor break; 554210389Sgabor case BIN_OPT: 555210461Sgabor if (strcasecmp("binary", optarg) == 0) 556210389Sgabor binbehave = BINFILE_BIN; 557210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 558210389Sgabor binbehave = BINFILE_SKIP; 559210461Sgabor else if (strcasecmp("text", optarg) == 0) 560210389Sgabor binbehave = BINFILE_TEXT; 561210389Sgabor else 562210389Sgabor errx(2, "%s", getstr(8)); 563210389Sgabor break; 564210389Sgabor case COLOR_OPT: 565210461Sgabor color = NULL; 566210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 567210461Sgabor strcasecmp("tty", optarg) == 0 || 568210461Sgabor strcasecmp("if-tty", optarg) == 0) { 569210461Sgabor char *term; 570210461Sgabor 571210461Sgabor term = getenv("TERM"); 572210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 573210461Sgabor strcasecmp(term, "dumb") != 0) 574210461Sgabor color = init_color("01;31"); 575210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 576210461Sgabor strcasecmp("yes", optarg) == 0 || 577210461Sgabor strcasecmp("force", optarg) == 0) { 578210461Sgabor color = init_color("01;31"); 579210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 580210461Sgabor strcasecmp("none", optarg) != 0 && 581210461Sgabor strcasecmp("no", optarg) != 0) 582210389Sgabor errx(2, "%s", getstr(3)); 583210389Sgabor break; 584210389Sgabor case LABEL_OPT: 585210389Sgabor label = optarg; 586210389Sgabor break; 587210389Sgabor case LINEBUF_OPT: 588210389Sgabor lbflag = true; 589210389Sgabor break; 590210389Sgabor case NULL_OPT: 591210389Sgabor nullflag = true; 592210389Sgabor break; 593210389Sgabor case R_INCLUDE_OPT: 594210389Sgabor exclflag = true; 595210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 596210389Sgabor FILE_PAT, INCL_PAT); 597210389Sgabor break; 598210389Sgabor case R_EXCLUDE_OPT: 599210389Sgabor exclflag = true; 600210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 601210389Sgabor FILE_PAT, EXCL_PAT); 602210389Sgabor break; 603210389Sgabor case R_DINCLUDE_OPT: 604210389Sgabor exclflag = true; 605210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 606210389Sgabor DIR_PAT, INCL_PAT); 607210389Sgabor break; 608210389Sgabor case R_DEXCLUDE_OPT: 609210389Sgabor exclflag = true; 610210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 611210389Sgabor DIR_PAT, EXCL_PAT); 612210389Sgabor break; 613210389Sgabor case HELP_OPT: 614210389Sgabor default: 615210389Sgabor usage(); 616210389Sgabor } 617210389Sgabor lastc = c; 618210389Sgabor newarg = optind != prevoptind; 619210389Sgabor prevoptind = optind; 620210389Sgabor } 621210389Sgabor aargc -= optind; 622210389Sgabor aargv += optind; 623210389Sgabor 624210389Sgabor /* Fail if we don't have any pattern */ 625210389Sgabor if (aargc == 0 && needpattern) 626210389Sgabor usage(); 627210389Sgabor 628210389Sgabor /* Process patterns from command line */ 629210389Sgabor if (aargc != 0 && needpattern) { 630210389Sgabor add_pattern(*aargv, strlen(*aargv)); 631210389Sgabor --aargc; 632210389Sgabor ++aargv; 633210389Sgabor } 634210389Sgabor 635210389Sgabor switch (grepbehave) { 636210389Sgabor case GREP_FIXED: 637210389Sgabor case GREP_BASIC: 638210389Sgabor break; 639210389Sgabor case GREP_EXTENDED: 640210389Sgabor cflags |= REG_EXTENDED; 641210389Sgabor break; 642210389Sgabor default: 643210389Sgabor /* NOTREACHED */ 644210389Sgabor usage(); 645210389Sgabor } 646210389Sgabor 647210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 648210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 649210389Sgabor/* 650210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 651210389Sgabor * Optimizations should be done there. 652210389Sgabor */ 653210389Sgabor /* Check if cheating is allowed (always is for fgrep). */ 654210389Sgabor if (grepbehave == GREP_FIXED) { 655210389Sgabor for (i = 0; i < patterns; ++i) 656210389Sgabor fgrepcomp(&fg_pattern[i], pattern[i]); 657210389Sgabor } else { 658210389Sgabor for (i = 0; i < patterns; ++i) { 659210389Sgabor if (fastcomp(&fg_pattern[i], pattern[i])) { 660210389Sgabor /* Fall back to full regex library */ 661210389Sgabor c = regcomp(&r_pattern[i], pattern[i], cflags); 662210389Sgabor if (c != 0) { 663210389Sgabor regerror(c, &r_pattern[i], re_error, 664210389Sgabor RE_ERROR_BUF); 665210389Sgabor errx(2, "%s", re_error); 666210389Sgabor } 667210389Sgabor } 668210389Sgabor } 669210389Sgabor } 670210389Sgabor 671210389Sgabor if (lbflag) 672210389Sgabor setlinebuf(stdout); 673210389Sgabor 674210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 675210389Sgabor hflag = true; 676210389Sgabor 677210389Sgabor if (aargc == 0) 678210389Sgabor exit(!procfile("-")); 679210389Sgabor 680210389Sgabor if (dirbehave == DIR_RECURSE) 681210389Sgabor c = grep_tree(aargv); 682210389Sgabor else 683210389Sgabor for (c = 0; aargc--; ++aargv) 684210389Sgabor c+= procfile(*aargv); 685210389Sgabor 686210389Sgabor#ifndef WITHOUT_NLS 687210389Sgabor catclose(catalog); 688210389Sgabor#endif 689210389Sgabor 690210389Sgabor /* Find out the correct return value according to the 691210389Sgabor results and the command line option. */ 692210389Sgabor exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 693210389Sgabor} 694