grep.c revision 210389
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 210389 2010-07-22 19:11:57Z gabor $"); 32210389Sgabor 33210389Sgabor#include <sys/stat.h> 34210389Sgabor#include <sys/types.h> 35210389Sgabor 36210389Sgabor#include <ctype.h> 37210389Sgabor#include <err.h> 38210389Sgabor#include <errno.h> 39210389Sgabor#include <getopt.h> 40210389Sgabor#include <limits.h> 41210389Sgabor#include <libgen.h> 42210389Sgabor#include <locale.h> 43210389Sgabor#include <stdbool.h> 44210389Sgabor#include <stdio.h> 45210389Sgabor#include <stdlib.h> 46210389Sgabor#include <string.h> 47210389Sgabor#include <unistd.h> 48210389Sgabor 49210389Sgabor#include "grep.h" 50210389Sgabor 51210389Sgabor#ifndef WITHOUT_NLS 52210389Sgabor#include <nl_types.h> 53210389Sgabornl_catd catalog; 54210389Sgabor#endif 55210389Sgabor 56210389Sgabor/* 57210389Sgabor * Default messags to use when NLS is disabled or no catalogue 58210389Sgabor * is found. 59210389Sgabor */ 60210389Sgaborconst char *errstr[] = { 61210389Sgabor "", 62210389Sgabor/* 1*/ "(standard input)", 63210389Sgabor/* 2*/ "cannot read bzip2 compressed file", 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 */ 117210389Sgaborchar *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 */ 121210389Sgaborint devbehave = DEV_GREP; /* -D: handling of devices */ 122210389Sgaborint dirbehave = DIR_GREP; /* -dRr: handling of directories */ 123210389Sgaborint linkbehave = LINK_GREP; /* -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 139210389Sgabor/* Housekeeping */ 140210389Sgaborbool first = true; /* flag whether we are processing the first match */ 141210389Sgaborbool prev; /* flag whether or not the previous line matched */ 142210389Sgaborint tail; /* lines left to print */ 143210389Sgaborbool notfound; /* file not found */ 144210389Sgabor 145210389Sgaborextern char *__progname; 146210389Sgabor 147210389Sgabor/* 148210389Sgabor * Prints usage information and returns 2. 149210389Sgabor */ 150210389Sgaborstatic void 151210389Sgaborusage(void) 152210389Sgabor{ 153210389Sgabor fprintf(stderr, getstr(4), __progname); 154210389Sgabor fprintf(stderr, "%s", getstr(5)); 155210389Sgabor fprintf(stderr, "%s", getstr(5)); 156210389Sgabor fprintf(stderr, "%s", getstr(6)); 157210389Sgabor fprintf(stderr, "%s", getstr(7)); 158210389Sgabor exit(2); 159210389Sgabor} 160210389Sgabor 161210389Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy"; 162210389Sgabor 163210389Sgaborstruct option long_options[] = 164210389Sgabor{ 165210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 166210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 167210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 168210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 169210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 170210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 171210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 172210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 173210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 174210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 175210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 176210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 177210389Sgabor {"after-context", required_argument, NULL, 'A'}, 178210389Sgabor {"text", no_argument, NULL, 'a'}, 179210389Sgabor {"before-context", required_argument, NULL, 'B'}, 180210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 181210389Sgabor {"context", optional_argument, NULL, 'C'}, 182210389Sgabor {"count", no_argument, NULL, 'c'}, 183210389Sgabor {"devices", required_argument, NULL, 'D'}, 184210389Sgabor {"directories", required_argument, NULL, 'd'}, 185210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 186210389Sgabor {"regexp", required_argument, NULL, 'e'}, 187210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 188210389Sgabor {"file", required_argument, NULL, 'f'}, 189210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 190210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 191210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 192210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 193210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 194210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 195210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 196210389Sgabor {"max-count", required_argument, NULL, 'm'}, 197210389Sgabor {"line-number", no_argument, NULL, 'n'}, 198210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 199210389Sgabor {"quiet", no_argument, NULL, 'q'}, 200210389Sgabor {"silent", no_argument, NULL, 'q'}, 201210389Sgabor {"recursive", no_argument, NULL, 'r'}, 202210389Sgabor {"no-messages", no_argument, NULL, 's'}, 203210389Sgabor {"binary", no_argument, NULL, 'U'}, 204210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 205210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 206210389Sgabor {"version", no_argument, NULL, 'V'}, 207210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 208210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 209210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 210210389Sgabor {NULL, no_argument, NULL, 0} 211210389Sgabor}; 212210389Sgabor 213210389Sgabor/* 214210389Sgabor * Adds a searching pattern to the internal array. 215210389Sgabor */ 216210389Sgaborstatic void 217210389Sgaboradd_pattern(char *pat, size_t len) 218210389Sgabor{ 219210389Sgabor 220210389Sgabor /* Check if we can do a shortcut */ 221210389Sgabor if (len == 0 || matchall) { 222210389Sgabor matchall = true; 223210389Sgabor return; 224210389Sgabor } 225210389Sgabor /* Increase size if necessary */ 226210389Sgabor if (patterns == pattern_sz) { 227210389Sgabor pattern_sz *= 2; 228210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 229210389Sgabor sizeof(*pattern)); 230210389Sgabor } 231210389Sgabor if (len > 0 && pat[len - 1] == '\n') 232210389Sgabor --len; 233210389Sgabor /* pat may not be NUL-terminated */ 234210389Sgabor pattern[patterns] = grep_malloc(len + 1); 235210389Sgabor memcpy(pattern[patterns], pat, len); 236210389Sgabor pattern[patterns][len] = '\0'; 237210389Sgabor ++patterns; 238210389Sgabor} 239210389Sgabor 240210389Sgabor/* 241210389Sgabor * Adds an include/exclude pattern to the internal array. 242210389Sgabor */ 243210389Sgaborstatic void 244210389Sgaboradd_epattern(char *pat, size_t len, int type, int mode) 245210389Sgabor{ 246210389Sgabor 247210389Sgabor /* Increase size if necessary */ 248210389Sgabor if (epatterns == epattern_sz) { 249210389Sgabor epattern_sz *= 2; 250210389Sgabor epattern = grep_realloc(epattern, ++epattern_sz * 251210389Sgabor sizeof(struct epat)); 252210389Sgabor } 253210389Sgabor if (len > 0 && pat[len - 1] == '\n') 254210389Sgabor --len; 255210389Sgabor epattern[epatterns].pat = grep_malloc(len + 1); 256210389Sgabor memcpy(epattern[epatterns].pat, pat, len); 257210389Sgabor epattern[epatterns].pat[len] = '\0'; 258210389Sgabor epattern[epatterns].type = type; 259210389Sgabor epattern[epatterns].mode = mode; 260210389Sgabor ++epatterns; 261210389Sgabor} 262210389Sgabor 263210389Sgabor/* 264210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 265210389Sgabor */ 266210389Sgaborstatic void 267210389Sgaborread_patterns(const char *fn) 268210389Sgabor{ 269210389Sgabor FILE *f; 270210389Sgabor char *line; 271210389Sgabor size_t len; 272210389Sgabor 273210389Sgabor if ((f = fopen(fn, "r")) == NULL) 274210389Sgabor err(2, "%s", fn); 275210389Sgabor while ((line = fgetln(f, &len)) != NULL) 276210389Sgabor add_pattern(line, *line == '\n' ? 0 : len); 277210389Sgabor if (ferror(f)) 278210389Sgabor err(2, "%s", fn); 279210389Sgabor fclose(f); 280210389Sgabor} 281210389Sgabor 282210389Sgaborint 283210389Sgabormain(int argc, char *argv[]) 284210389Sgabor{ 285210389Sgabor char **aargv, **eargv, *eopts; 286210389Sgabor char *ep; 287210389Sgabor unsigned long long l; 288210389Sgabor unsigned int aargc, eargc, i; 289210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 290210389Sgabor 291210389Sgabor setlocale(LC_ALL, ""); 292210389Sgabor 293210389Sgabor#ifndef WITHOUT_NLS 294210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 295210389Sgabor#endif 296210389Sgabor 297210389Sgabor /* Check what is the program name of the binary. In this 298210389Sgabor way we can have all the funcionalities in one binary 299210389Sgabor without the need of scripting and using ugly hacks. */ 300210389Sgabor switch (__progname[0]) { 301210389Sgabor case 'e': 302210389Sgabor grepbehave = GREP_EXTENDED; 303210389Sgabor break; 304210389Sgabor case 'f': 305210389Sgabor grepbehave = GREP_FIXED; 306210389Sgabor break; 307210389Sgabor case 'g': 308210389Sgabor grepbehave = GREP_BASIC; 309210389Sgabor break; 310210389Sgabor case 'z': 311210389Sgabor filebehave = FILE_GZIP; 312210389Sgabor switch(__progname[1]) { 313210389Sgabor case 'e': 314210389Sgabor grepbehave = GREP_EXTENDED; 315210389Sgabor break; 316210389Sgabor case 'f': 317210389Sgabor grepbehave = GREP_FIXED; 318210389Sgabor break; 319210389Sgabor case 'g': 320210389Sgabor grepbehave = GREP_BASIC; 321210389Sgabor break; 322210389Sgabor } 323210389Sgabor break; 324210389Sgabor } 325210389Sgabor 326210389Sgabor lastc = '\0'; 327210389Sgabor newarg = 1; 328210389Sgabor prevoptind = 1; 329210389Sgabor needpattern = 1; 330210389Sgabor 331210389Sgabor eopts = getenv("GREP_OPTIONS"); 332210389Sgabor 333210389Sgabor eargc = 1; 334210389Sgabor if (eopts != NULL) { 335210389Sgabor char *str; 336210389Sgabor 337210389Sgabor for(i = 0; i < strlen(eopts); i++) 338210389Sgabor if (eopts[i] == ' ') 339210389Sgabor eargc++; 340210389Sgabor 341210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 342210389Sgabor 343210389Sgabor str = strtok(eopts, " "); 344210389Sgabor eargc = 0; 345210389Sgabor 346210389Sgabor while(str != NULL) { 347210389Sgabor eargv[++eargc] = (char *)grep_malloc(sizeof(char) * 348210389Sgabor (strlen(str) + 1)); 349210389Sgabor strlcpy(eargv[eargc], str, strlen(str) + 1); 350210389Sgabor str = strtok(NULL, " "); 351210389Sgabor } 352210389Sgabor eargv[++eargc] = NULL; 353210389Sgabor 354210389Sgabor aargv = (char **)grep_malloc(sizeof(char *) * 355210389Sgabor (eargc + argc + 1)); 356210389Sgabor aargv[0] = argv[0]; 357210389Sgabor 358210389Sgabor for(i = 1; i < eargc; i++) 359210389Sgabor aargv[i] = eargv[i]; 360210389Sgabor for(int j = 1; j < argc; j++) 361210389Sgabor aargv[i++] = argv[j]; 362210389Sgabor 363210389Sgabor aargc = eargc + argc - 1; 364210389Sgabor 365210389Sgabor } else { 366210389Sgabor aargv = argv; 367210389Sgabor aargc = argc; 368210389Sgabor } 369210389Sgabor 370210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 371210389Sgabor -1)) { 372210389Sgabor switch (c) { 373210389Sgabor case '0': case '1': case '2': case '3': case '4': 374210389Sgabor case '5': case '6': case '7': case '8': case '9': 375210389Sgabor if (newarg || !isdigit(lastc)) 376210389Sgabor Aflag = 0; 377210389Sgabor else if (Aflag > LLONG_MAX / 10) { 378210389Sgabor errno = ERANGE; 379210389Sgabor err(2, NULL); 380210389Sgabor } 381210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 382210389Sgabor break; 383210389Sgabor case 'C': 384210389Sgabor if (optarg == NULL) { 385210389Sgabor Aflag = Bflag = 2; 386210389Sgabor break; 387210389Sgabor } 388210389Sgabor /* FALLTHROUGH */ 389210389Sgabor case 'A': 390210389Sgabor /* FALLTHROUGH */ 391210389Sgabor case 'B': 392210389Sgabor errno = 0; 393210389Sgabor l = strtoull(optarg, &ep, 10); 394210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 395210389Sgabor ((errno == EINVAL) && (l == 0))) 396210389Sgabor err(2, NULL); 397210389Sgabor else if (ep[0] != '\0') { 398210389Sgabor errno = EINVAL; 399210389Sgabor err(2, NULL); 400210389Sgabor } 401210389Sgabor if (c == 'A') 402210389Sgabor Aflag = l; 403210389Sgabor else if (c == 'B') 404210389Sgabor Bflag = l; 405210389Sgabor else 406210389Sgabor Aflag = Bflag = l; 407210389Sgabor break; 408210389Sgabor case 'a': 409210389Sgabor binbehave = BINFILE_TEXT; 410210389Sgabor break; 411210389Sgabor case 'b': 412210389Sgabor bflag = true; 413210389Sgabor break; 414210389Sgabor case 'c': 415210389Sgabor cflag = true; 416210389Sgabor break; 417210389Sgabor case 'D': 418210389Sgabor if (strcmp(optarg, "skip") == 0) 419210389Sgabor devbehave = DEV_SKIP; 420210389Sgabor break; 421210389Sgabor case 'd': 422210389Sgabor if (strcmp("recurse", optarg) == 0) { 423210389Sgabor Hflag = true; 424210389Sgabor dirbehave = DIR_RECURSE; 425210389Sgabor } else if (strcmp("skip", optarg) == 0) 426210389Sgabor dirbehave = DIR_SKIP; 427210389Sgabor else if (strcmp("read", optarg) != 0) { 428210389Sgabor errno = EINVAL; 429210389Sgabor err(2, NULL); 430210389Sgabor } 431210389Sgabor break; 432210389Sgabor case 'E': 433210389Sgabor grepbehave = GREP_EXTENDED; 434210389Sgabor break; 435210389Sgabor case 'e': 436210389Sgabor add_pattern(optarg, strlen(optarg)); 437210389Sgabor needpattern = 0; 438210389Sgabor break; 439210389Sgabor case 'F': 440210389Sgabor grepbehave = GREP_FIXED; 441210389Sgabor break; 442210389Sgabor case 'f': 443210389Sgabor read_patterns(optarg); 444210389Sgabor needpattern = 0; 445210389Sgabor break; 446210389Sgabor case 'G': 447210389Sgabor grepbehave = GREP_BASIC; 448210389Sgabor break; 449210389Sgabor case 'H': 450210389Sgabor Hflag = true; 451210389Sgabor break; 452210389Sgabor case 'h': 453210389Sgabor Hflag = false; 454210389Sgabor hflag = true; 455210389Sgabor break; 456210389Sgabor case 'I': 457210389Sgabor binbehave = BINFILE_SKIP; 458210389Sgabor break; 459210389Sgabor case 'i': 460210389Sgabor case 'y': 461210389Sgabor iflag = true; 462210389Sgabor cflags |= REG_ICASE; 463210389Sgabor break; 464210389Sgabor case 'J': 465210389Sgabor filebehave = FILE_BZIP; 466210389Sgabor break; 467210389Sgabor case 'L': 468210389Sgabor lflag = false; 469210389Sgabor Lflag = qflag = true; 470210389Sgabor break; 471210389Sgabor case 'l': 472210389Sgabor Lflag = false; 473210389Sgabor lflag = qflag = true; 474210389Sgabor break; 475210389Sgabor case 'm': 476210389Sgabor mflag = true; 477210389Sgabor errno = 0; 478210389Sgabor mcount = strtoull(optarg, &ep, 10); 479210389Sgabor if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 480210389Sgabor ((errno == EINVAL) && (mcount == 0))) 481210389Sgabor err(2, NULL); 482210389Sgabor else if (ep[0] != '\0') { 483210389Sgabor errno = EINVAL; 484210389Sgabor err(2, NULL); 485210389Sgabor } 486210389Sgabor break; 487210389Sgabor case 'n': 488210389Sgabor nflag = true; 489210389Sgabor break; 490210389Sgabor case 'O': 491210389Sgabor linkbehave = LINK_EXPLICIT; 492210389Sgabor break; 493210389Sgabor case 'o': 494210389Sgabor oflag = true; 495210389Sgabor break; 496210389Sgabor case 'p': 497210389Sgabor linkbehave = LINK_SKIP; 498210389Sgabor break; 499210389Sgabor case 'q': 500210389Sgabor qflag = true; 501210389Sgabor break; 502210389Sgabor case 'S': 503210389Sgabor linkbehave = LINK_GREP; 504210389Sgabor break; 505210389Sgabor case 'R': 506210389Sgabor case 'r': 507210389Sgabor dirbehave = DIR_RECURSE; 508210389Sgabor Hflag = true; 509210389Sgabor break; 510210389Sgabor case 's': 511210389Sgabor sflag = true; 512210389Sgabor break; 513210389Sgabor case 'U': 514210389Sgabor binbehave = BINFILE_BIN; 515210389Sgabor break; 516210389Sgabor case 'u': 517210389Sgabor case MMAP_OPT: 518210389Sgabor /* noop, compatibility */ 519210389Sgabor break; 520210389Sgabor case 'V': 521210389Sgabor printf(getstr(10), __progname, VERSION); 522210389Sgabor exit(0); 523210389Sgabor case 'v': 524210389Sgabor vflag = true; 525210389Sgabor break; 526210389Sgabor case 'w': 527210389Sgabor wflag = true; 528210389Sgabor break; 529210389Sgabor case 'x': 530210389Sgabor xflag = true; 531210389Sgabor break; 532210389Sgabor case 'Z': 533210389Sgabor filebehave = FILE_GZIP; 534210389Sgabor break; 535210389Sgabor case BIN_OPT: 536210389Sgabor if (strcmp("binary", optarg) == 0) 537210389Sgabor binbehave = BINFILE_BIN; 538210389Sgabor else if (strcmp("without-match", optarg) == 0) 539210389Sgabor binbehave = BINFILE_SKIP; 540210389Sgabor else if (strcmp("text", optarg) == 0) 541210389Sgabor binbehave = BINFILE_TEXT; 542210389Sgabor else 543210389Sgabor errx(2, "%s", getstr(8)); 544210389Sgabor break; 545210389Sgabor case COLOR_OPT: 546210389Sgabor if (optarg == NULL || strcmp("auto", optarg) == 0 || 547210389Sgabor strcmp("always", optarg) == 0 ) { 548210389Sgabor color = getenv("GREP_COLOR"); 549210389Sgabor if (color == NULL) { 550210389Sgabor color = grep_malloc(sizeof(char) * 6); 551210389Sgabor strcpy(color, "01;31"); 552210389Sgabor } 553210389Sgabor } else if (strcmp("never", optarg) == 0) 554210389Sgabor color = NULL; 555210389Sgabor else 556210389Sgabor errx(2, "%s", getstr(3)); 557210389Sgabor break; 558210389Sgabor case LABEL_OPT: 559210389Sgabor label = optarg; 560210389Sgabor break; 561210389Sgabor case LINEBUF_OPT: 562210389Sgabor lbflag = true; 563210389Sgabor break; 564210389Sgabor case NULL_OPT: 565210389Sgabor nullflag = true; 566210389Sgabor break; 567210389Sgabor case R_INCLUDE_OPT: 568210389Sgabor exclflag = true; 569210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 570210389Sgabor FILE_PAT, INCL_PAT); 571210389Sgabor break; 572210389Sgabor case R_EXCLUDE_OPT: 573210389Sgabor exclflag = true; 574210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 575210389Sgabor FILE_PAT, EXCL_PAT); 576210389Sgabor break; 577210389Sgabor case R_DINCLUDE_OPT: 578210389Sgabor exclflag = true; 579210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 580210389Sgabor DIR_PAT, INCL_PAT); 581210389Sgabor break; 582210389Sgabor case R_DEXCLUDE_OPT: 583210389Sgabor exclflag = true; 584210389Sgabor add_epattern(basename(optarg), strlen(basename(optarg)), 585210389Sgabor DIR_PAT, EXCL_PAT); 586210389Sgabor break; 587210389Sgabor case HELP_OPT: 588210389Sgabor default: 589210389Sgabor usage(); 590210389Sgabor } 591210389Sgabor lastc = c; 592210389Sgabor newarg = optind != prevoptind; 593210389Sgabor prevoptind = optind; 594210389Sgabor } 595210389Sgabor aargc -= optind; 596210389Sgabor aargv += optind; 597210389Sgabor 598210389Sgabor /* Fail if we don't have any pattern */ 599210389Sgabor if (aargc == 0 && needpattern) 600210389Sgabor usage(); 601210389Sgabor 602210389Sgabor /* Process patterns from command line */ 603210389Sgabor if (aargc != 0 && needpattern) { 604210389Sgabor add_pattern(*aargv, strlen(*aargv)); 605210389Sgabor --aargc; 606210389Sgabor ++aargv; 607210389Sgabor } 608210389Sgabor 609210389Sgabor switch (grepbehave) { 610210389Sgabor case GREP_FIXED: 611210389Sgabor case GREP_BASIC: 612210389Sgabor break; 613210389Sgabor case GREP_EXTENDED: 614210389Sgabor cflags |= REG_EXTENDED; 615210389Sgabor break; 616210389Sgabor default: 617210389Sgabor /* NOTREACHED */ 618210389Sgabor usage(); 619210389Sgabor } 620210389Sgabor 621210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 622210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 623210389Sgabor/* 624210389Sgabor * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 625210389Sgabor * Optimizations should be done there. 626210389Sgabor */ 627210389Sgabor /* Check if cheating is allowed (always is for fgrep). */ 628210389Sgabor if (grepbehave == GREP_FIXED) { 629210389Sgabor for (i = 0; i < patterns; ++i) 630210389Sgabor fgrepcomp(&fg_pattern[i], pattern[i]); 631210389Sgabor } else { 632210389Sgabor for (i = 0; i < patterns; ++i) { 633210389Sgabor if (fastcomp(&fg_pattern[i], pattern[i])) { 634210389Sgabor /* Fall back to full regex library */ 635210389Sgabor c = regcomp(&r_pattern[i], pattern[i], cflags); 636210389Sgabor if (c != 0) { 637210389Sgabor regerror(c, &r_pattern[i], re_error, 638210389Sgabor RE_ERROR_BUF); 639210389Sgabor errx(2, "%s", re_error); 640210389Sgabor } 641210389Sgabor } 642210389Sgabor } 643210389Sgabor } 644210389Sgabor 645210389Sgabor if (lbflag) 646210389Sgabor setlinebuf(stdout); 647210389Sgabor 648210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 649210389Sgabor hflag = true; 650210389Sgabor 651210389Sgabor if (aargc == 0) 652210389Sgabor exit(!procfile("-")); 653210389Sgabor 654210389Sgabor if (dirbehave == DIR_RECURSE) 655210389Sgabor c = grep_tree(aargv); 656210389Sgabor else 657210389Sgabor for (c = 0; aargc--; ++aargv) 658210389Sgabor c+= procfile(*aargv); 659210389Sgabor 660210389Sgabor#ifndef WITHOUT_NLS 661210389Sgabor catclose(catalog); 662210389Sgabor#endif 663210389Sgabor 664210389Sgabor /* Find out the correct return value according to the 665210389Sgabor results and the command line option. */ 666210389Sgabor exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 667210389Sgabor} 668