grep.c revision 244493
1220422Sgabor/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ 2220422Sgabor/* $FreeBSD: head/usr.bin/grep/grep.c 244493 2012-12-20 17:38:14Z eadler $ */ 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 244493 2012-12-20 17:38:14Z eadler $"); 34210389Sgabor 35210389Sgabor#include <sys/stat.h> 36210389Sgabor#include <sys/types.h> 37210389Sgabor 38210389Sgabor#include <ctype.h> 39210389Sgabor#include <err.h> 40210389Sgabor#include <errno.h> 41226035Sgabor#include <fcntl.h> 42210389Sgabor#include <getopt.h> 43210389Sgabor#include <limits.h> 44210389Sgabor#include <libgen.h> 45210389Sgabor#include <locale.h> 46210389Sgabor#include <stdbool.h> 47210389Sgabor#include <stdio.h> 48210389Sgabor#include <stdlib.h> 49210389Sgabor#include <string.h> 50210389Sgabor#include <unistd.h> 51210389Sgabor 52226035Sgabor#include "fastmatch.h" 53210389Sgabor#include "grep.h" 54210389Sgabor 55210389Sgabor#ifndef WITHOUT_NLS 56210389Sgabor#include <nl_types.h> 57210389Sgabornl_catd catalog; 58210389Sgabor#endif 59210389Sgabor 60210389Sgabor/* 61210389Sgabor * Default messags to use when NLS is disabled or no catalogue 62210389Sgabor * is found. 63210389Sgabor */ 64210389Sgaborconst char *errstr[] = { 65210389Sgabor "", 66210389Sgabor/* 1*/ "(standard input)", 67210389Sgabor/* 2*/ "cannot read bzip2 compressed file", 68210622Sgabor/* 3*/ "unknown %s option", 69210389Sgabor/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", 70210389Sgabor/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", 71210389Sgabor/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", 72210389Sgabor/* 7*/ "\t[--null] [pattern] [file ...]\n", 73210622Sgabor/* 8*/ "Binary file %s matches\n", 74210622Sgabor/* 9*/ "%s (BSD grep) %s\n", 75210389Sgabor}; 76210389Sgabor 77210389Sgabor/* Flags passed to regcomp() and regexec() */ 78223009Sgaborint cflags = REG_NOSUB; 79210389Sgaborint eflags = REG_STARTEND; 80210389Sgabor 81210389Sgabor/* Shortcut for matching all cases like empty regex */ 82210389Sgaborbool matchall; 83210389Sgabor 84210389Sgabor/* Searching patterns */ 85241737Sedunsigned int patterns; 86241737Sedstatic unsigned int pattern_sz; 87226035Sgaborstruct pat *pattern; 88210389Sgaborregex_t *r_pattern; 89226035Sgaborfastmatch_t *fg_pattern; 90210389Sgabor 91210389Sgabor/* Filename exclusion/inclusion patterns */ 92241737Sedunsigned int fpatterns, dpatterns; 93241737Sedstatic unsigned int fpattern_sz, dpattern_sz; 94210578Sgaborstruct epat *dpattern, *fpattern; 95210389Sgabor 96210389Sgabor/* For regex errors */ 97210389Sgaborchar re_error[RE_ERROR_BUF + 1]; 98210389Sgabor 99210389Sgabor/* Command-line flags */ 100210389Sgaborunsigned long long Aflag; /* -A x: print x lines trailing each match */ 101210389Sgaborunsigned long long Bflag; /* -B x: print x lines leading each match */ 102210389Sgaborbool Hflag; /* -H: always print file name */ 103210389Sgaborbool Lflag; /* -L: only show names of files with no matches */ 104210389Sgaborbool bflag; /* -b: show block numbers for each match */ 105210389Sgaborbool cflag; /* -c: only show a count of matching lines */ 106210389Sgaborbool hflag; /* -h: don't print filename headers */ 107210389Sgaborbool iflag; /* -i: ignore case */ 108210389Sgaborbool lflag; /* -l: only show names of files with matches */ 109210389Sgaborbool mflag; /* -m x: stop reading the files after x matches */ 110226035Sgaborlong long mcount; /* count for -m */ 111244493Seadlerlong long mlimit; /* requested value for -m */ 112210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 113210389Sgaborbool oflag; /* -o: print only matching part */ 114210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 115210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 116210389Sgaborbool vflag; /* -v: only show non-matching lines */ 117210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 118210389Sgaborbool xflag; /* -x: pattern must match entire line */ 119210389Sgaborbool lbflag; /* --line-buffered */ 120210389Sgaborbool nullflag; /* --null */ 121210389Sgaborchar *label; /* --label */ 122210461Sgaborconst char *color; /* --color */ 123210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 124210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 125210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 126210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 127210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 128210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 129210389Sgabor 130211364Sgaborbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 131211364Sgaborbool fexclude, finclude; /* --exclude and --include */ 132210578Sgabor 133210389Sgaborenum { 134210389Sgabor BIN_OPT = CHAR_MAX + 1, 135210389Sgabor COLOR_OPT, 136210389Sgabor HELP_OPT, 137210389Sgabor MMAP_OPT, 138210389Sgabor LINEBUF_OPT, 139210389Sgabor LABEL_OPT, 140210389Sgabor NULL_OPT, 141210389Sgabor R_EXCLUDE_OPT, 142210389Sgabor R_INCLUDE_OPT, 143210389Sgabor R_DEXCLUDE_OPT, 144210389Sgabor R_DINCLUDE_OPT 145210389Sgabor}; 146210389Sgabor 147210461Sgaborstatic inline const char *init_color(const char *); 148210461Sgabor 149210389Sgabor/* Housekeeping */ 150210389Sgaborbool first = true; /* flag whether we are processing the first match */ 151210389Sgaborbool prev; /* flag whether or not the previous line matched */ 152210389Sgaborint tail; /* lines left to print */ 153228319Sgaborbool file_err; /* file reading error */ 154210389Sgabor 155210389Sgabor/* 156210389Sgabor * Prints usage information and returns 2. 157210389Sgabor */ 158210389Sgaborstatic void 159210389Sgaborusage(void) 160210389Sgabor{ 161226271Sgabor fprintf(stderr, getstr(4), getprogname()); 162210389Sgabor fprintf(stderr, "%s", getstr(5)); 163210389Sgabor fprintf(stderr, "%s", getstr(6)); 164210389Sgabor fprintf(stderr, "%s", getstr(7)); 165210389Sgabor exit(2); 166210389Sgabor} 167210389Sgabor 168226035Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; 169210389Sgabor 170228395Sedstatic const struct option long_options[] = 171210389Sgabor{ 172210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 173210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 174210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 175210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 176210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 177210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 178210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 179210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 180210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 181210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 182210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 183210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 184210389Sgabor {"after-context", required_argument, NULL, 'A'}, 185210389Sgabor {"text", no_argument, NULL, 'a'}, 186210389Sgabor {"before-context", required_argument, NULL, 'B'}, 187210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 188210389Sgabor {"context", optional_argument, NULL, 'C'}, 189210389Sgabor {"count", no_argument, NULL, 'c'}, 190210389Sgabor {"devices", required_argument, NULL, 'D'}, 191210389Sgabor {"directories", required_argument, NULL, 'd'}, 192210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 193210389Sgabor {"regexp", required_argument, NULL, 'e'}, 194210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 195210389Sgabor {"file", required_argument, NULL, 'f'}, 196210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 197210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 198210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 199210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 200210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 201210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 202210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 203210389Sgabor {"max-count", required_argument, NULL, 'm'}, 204226035Sgabor {"lzma", no_argument, NULL, 'M'}, 205210389Sgabor {"line-number", no_argument, NULL, 'n'}, 206210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 207210389Sgabor {"quiet", no_argument, NULL, 'q'}, 208210389Sgabor {"silent", no_argument, NULL, 'q'}, 209210389Sgabor {"recursive", no_argument, NULL, 'r'}, 210210389Sgabor {"no-messages", no_argument, NULL, 's'}, 211210389Sgabor {"binary", no_argument, NULL, 'U'}, 212210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 213210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 214210389Sgabor {"version", no_argument, NULL, 'V'}, 215210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 216210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 217226035Sgabor {"xz", no_argument, NULL, 'X'}, 218210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 219210389Sgabor {NULL, no_argument, NULL, 0} 220210389Sgabor}; 221210389Sgabor 222210389Sgabor/* 223210389Sgabor * Adds a searching pattern to the internal array. 224210389Sgabor */ 225210389Sgaborstatic void 226210389Sgaboradd_pattern(char *pat, size_t len) 227210389Sgabor{ 228210389Sgabor 229226035Sgabor /* Do not add further pattern is we already match everything */ 230226035Sgabor if (matchall) 231226035Sgabor return; 232226035Sgabor 233210389Sgabor /* Check if we can do a shortcut */ 234226035Sgabor if (len == 0) { 235210389Sgabor matchall = true; 236226035Sgabor for (unsigned int i = 0; i < patterns; i++) { 237226035Sgabor free(pattern[i].pat); 238226035Sgabor } 239226035Sgabor pattern = grep_realloc(pattern, sizeof(struct pat)); 240226035Sgabor pattern[0].pat = NULL; 241226035Sgabor pattern[0].len = 0; 242226035Sgabor patterns = 1; 243210389Sgabor return; 244210389Sgabor } 245210389Sgabor /* Increase size if necessary */ 246210389Sgabor if (patterns == pattern_sz) { 247210389Sgabor pattern_sz *= 2; 248210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 249226035Sgabor sizeof(struct pat)); 250210389Sgabor } 251210389Sgabor if (len > 0 && pat[len - 1] == '\n') 252210389Sgabor --len; 253210389Sgabor /* pat may not be NUL-terminated */ 254226035Sgabor pattern[patterns].pat = grep_malloc(len + 1); 255226035Sgabor memcpy(pattern[patterns].pat, pat, len); 256226035Sgabor pattern[patterns].len = len; 257226035Sgabor pattern[patterns].pat[len] = '\0'; 258210389Sgabor ++patterns; 259210389Sgabor} 260210389Sgabor 261210389Sgabor/* 262210578Sgabor * Adds a file include/exclude pattern to the internal array. 263210389Sgabor */ 264210389Sgaborstatic void 265210578Sgaboradd_fpattern(const char *pat, int mode) 266210389Sgabor{ 267210389Sgabor 268210389Sgabor /* Increase size if necessary */ 269210578Sgabor if (fpatterns == fpattern_sz) { 270210578Sgabor fpattern_sz *= 2; 271210578Sgabor fpattern = grep_realloc(fpattern, ++fpattern_sz * 272210389Sgabor sizeof(struct epat)); 273210389Sgabor } 274210578Sgabor fpattern[fpatterns].pat = grep_strdup(pat); 275210578Sgabor fpattern[fpatterns].mode = mode; 276210578Sgabor ++fpatterns; 277210389Sgabor} 278210389Sgabor 279210389Sgabor/* 280210578Sgabor * Adds a directory include/exclude pattern to the internal array. 281210578Sgabor */ 282210578Sgaborstatic void 283210578Sgaboradd_dpattern(const char *pat, int mode) 284210578Sgabor{ 285210578Sgabor 286210578Sgabor /* Increase size if necessary */ 287210578Sgabor if (dpatterns == dpattern_sz) { 288210578Sgabor dpattern_sz *= 2; 289210578Sgabor dpattern = grep_realloc(dpattern, ++dpattern_sz * 290210578Sgabor sizeof(struct epat)); 291210578Sgabor } 292210578Sgabor dpattern[dpatterns].pat = grep_strdup(pat); 293210578Sgabor dpattern[dpatterns].mode = mode; 294210578Sgabor ++dpatterns; 295210578Sgabor} 296210578Sgabor 297210578Sgabor/* 298210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 299210389Sgabor */ 300210389Sgaborstatic void 301210389Sgaborread_patterns(const char *fn) 302210389Sgabor{ 303226035Sgabor struct stat st; 304210389Sgabor FILE *f; 305210389Sgabor char *line; 306210389Sgabor size_t len; 307210389Sgabor 308210389Sgabor if ((f = fopen(fn, "r")) == NULL) 309210389Sgabor err(2, "%s", fn); 310226035Sgabor if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { 311226035Sgabor fclose(f); 312226035Sgabor return; 313226035Sgabor } 314226035Sgabor while ((line = fgetln(f, &len)) != NULL) 315226035Sgabor add_pattern(line, line[0] == '\n' ? 0 : len); 316210389Sgabor if (ferror(f)) 317210389Sgabor err(2, "%s", fn); 318210389Sgabor fclose(f); 319210389Sgabor} 320210389Sgabor 321210461Sgaborstatic inline const char * 322210461Sgaborinit_color(const char *d) 323210461Sgabor{ 324210461Sgabor char *c; 325210461Sgabor 326210461Sgabor c = getenv("GREP_COLOR"); 327224937Sgabor return (c != NULL && c[0] != '\0' ? c : d); 328210461Sgabor} 329210461Sgabor 330210389Sgaborint 331210389Sgabormain(int argc, char *argv[]) 332210389Sgabor{ 333210389Sgabor char **aargv, **eargv, *eopts; 334226271Sgabor char *ep; 335226271Sgabor const char *pn; 336210389Sgabor unsigned long long l; 337210389Sgabor unsigned int aargc, eargc, i; 338210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 339210389Sgabor 340210389Sgabor setlocale(LC_ALL, ""); 341210389Sgabor 342210389Sgabor#ifndef WITHOUT_NLS 343210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 344210389Sgabor#endif 345210389Sgabor 346210389Sgabor /* Check what is the program name of the binary. In this 347210389Sgabor way we can have all the funcionalities in one binary 348210389Sgabor without the need of scripting and using ugly hacks. */ 349226271Sgabor pn = getprogname(); 350226035Sgabor if (pn[0] == 'b' && pn[1] == 'z') { 351226035Sgabor filebehave = FILE_BZIP; 352226035Sgabor pn += 2; 353226035Sgabor } else if (pn[0] == 'x' && pn[1] == 'z') { 354226035Sgabor filebehave = FILE_XZ; 355226035Sgabor pn += 2; 356226035Sgabor } else if (pn[0] == 'l' && pn[1] == 'z') { 357226035Sgabor filebehave = FILE_LZMA; 358226035Sgabor pn += 2; 359226035Sgabor } else if (pn[0] == 'z') { 360226035Sgabor filebehave = FILE_GZIP; 361226035Sgabor pn += 1; 362226035Sgabor } 363226035Sgabor switch (pn[0]) { 364210389Sgabor case 'e': 365210389Sgabor grepbehave = GREP_EXTENDED; 366210389Sgabor break; 367210389Sgabor case 'f': 368210389Sgabor grepbehave = GREP_FIXED; 369210389Sgabor break; 370210389Sgabor } 371210389Sgabor 372210389Sgabor lastc = '\0'; 373210389Sgabor newarg = 1; 374210389Sgabor prevoptind = 1; 375210389Sgabor needpattern = 1; 376210389Sgabor 377210389Sgabor eopts = getenv("GREP_OPTIONS"); 378210389Sgabor 379211364Sgabor /* support for extra arguments in GREP_OPTIONS */ 380211364Sgabor eargc = 0; 381224937Sgabor if (eopts != NULL && eopts[0] != '\0') { 382210389Sgabor char *str; 383210389Sgabor 384211364Sgabor /* make an estimation of how many extra arguments we have */ 385211364Sgabor for (unsigned int j = 0; j < strlen(eopts); j++) 386211364Sgabor if (eopts[j] == ' ') 387210389Sgabor eargc++; 388210389Sgabor 389210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 390210389Sgabor 391210389Sgabor eargc = 0; 392211364Sgabor /* parse extra arguments */ 393211364Sgabor while ((str = strsep(&eopts, " ")) != NULL) 394224937Sgabor if (str[0] != '\0') 395224937Sgabor eargv[eargc++] = grep_strdup(str); 396210389Sgabor 397210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 398210430Sdelphij sizeof(char *)); 399211364Sgabor 400210389Sgabor aargv[0] = argv[0]; 401211364Sgabor for (i = 0; i < eargc; i++) 402211364Sgabor aargv[i + 1] = eargv[i]; 403211364Sgabor for (int j = 1; j < argc; j++, i++) 404211364Sgabor aargv[i + 1] = argv[j]; 405210389Sgabor 406211364Sgabor aargc = eargc + argc; 407210389Sgabor } else { 408210389Sgabor aargv = argv; 409210389Sgabor aargc = argc; 410210389Sgabor } 411210389Sgabor 412210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 413210389Sgabor -1)) { 414210389Sgabor switch (c) { 415210389Sgabor case '0': case '1': case '2': case '3': case '4': 416210389Sgabor case '5': case '6': case '7': case '8': case '9': 417210389Sgabor if (newarg || !isdigit(lastc)) 418210389Sgabor Aflag = 0; 419210389Sgabor else if (Aflag > LLONG_MAX / 10) { 420210389Sgabor errno = ERANGE; 421210389Sgabor err(2, NULL); 422210389Sgabor } 423210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 424210389Sgabor break; 425210389Sgabor case 'C': 426210389Sgabor if (optarg == NULL) { 427210389Sgabor Aflag = Bflag = 2; 428210389Sgabor break; 429210389Sgabor } 430210389Sgabor /* FALLTHROUGH */ 431210389Sgabor case 'A': 432210389Sgabor /* FALLTHROUGH */ 433210389Sgabor case 'B': 434210389Sgabor errno = 0; 435210389Sgabor l = strtoull(optarg, &ep, 10); 436210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 437210389Sgabor ((errno == EINVAL) && (l == 0))) 438210389Sgabor err(2, NULL); 439210389Sgabor else if (ep[0] != '\0') { 440210389Sgabor errno = EINVAL; 441210389Sgabor err(2, NULL); 442210389Sgabor } 443210389Sgabor if (c == 'A') 444210389Sgabor Aflag = l; 445210389Sgabor else if (c == 'B') 446210389Sgabor Bflag = l; 447210389Sgabor else 448210389Sgabor Aflag = Bflag = l; 449210389Sgabor break; 450210389Sgabor case 'a': 451210389Sgabor binbehave = BINFILE_TEXT; 452210389Sgabor break; 453210389Sgabor case 'b': 454210389Sgabor bflag = true; 455210389Sgabor break; 456210389Sgabor case 'c': 457210389Sgabor cflag = true; 458210389Sgabor break; 459210389Sgabor case 'D': 460210461Sgabor if (strcasecmp(optarg, "skip") == 0) 461210389Sgabor devbehave = DEV_SKIP; 462210461Sgabor else if (strcasecmp(optarg, "read") == 0) 463210461Sgabor devbehave = DEV_READ; 464210622Sgabor else 465210622Sgabor errx(2, getstr(3), "--devices"); 466210389Sgabor break; 467210389Sgabor case 'd': 468210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 469210389Sgabor Hflag = true; 470210389Sgabor dirbehave = DIR_RECURSE; 471210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 472210389Sgabor dirbehave = DIR_SKIP; 473210461Sgabor else if (strcasecmp("read", optarg) == 0) 474210461Sgabor dirbehave = DIR_READ; 475210622Sgabor else 476210622Sgabor errx(2, getstr(3), "--directories"); 477210389Sgabor break; 478210389Sgabor case 'E': 479210389Sgabor grepbehave = GREP_EXTENDED; 480210389Sgabor break; 481210389Sgabor case 'e': 482210389Sgabor add_pattern(optarg, strlen(optarg)); 483210389Sgabor needpattern = 0; 484210389Sgabor break; 485210389Sgabor case 'F': 486210389Sgabor grepbehave = GREP_FIXED; 487210389Sgabor break; 488210389Sgabor case 'f': 489210389Sgabor read_patterns(optarg); 490210389Sgabor needpattern = 0; 491210389Sgabor break; 492210389Sgabor case 'G': 493210389Sgabor grepbehave = GREP_BASIC; 494210389Sgabor break; 495210389Sgabor case 'H': 496210389Sgabor Hflag = true; 497210389Sgabor break; 498210389Sgabor case 'h': 499210389Sgabor Hflag = false; 500210389Sgabor hflag = true; 501210389Sgabor break; 502210389Sgabor case 'I': 503210389Sgabor binbehave = BINFILE_SKIP; 504210389Sgabor break; 505210389Sgabor case 'i': 506210389Sgabor case 'y': 507210389Sgabor iflag = true; 508210389Sgabor cflags |= REG_ICASE; 509210389Sgabor break; 510210389Sgabor case 'J': 511226271Sgabor#ifdef WITHOUT_BZIP2 512226271Sgabor errno = EOPNOTSUPP; 513226271Sgabor err(2, "bzip2 support was disabled at compile-time"); 514226271Sgabor#endif 515210389Sgabor filebehave = FILE_BZIP; 516210389Sgabor break; 517210389Sgabor case 'L': 518210389Sgabor lflag = false; 519210461Sgabor Lflag = true; 520210389Sgabor break; 521210389Sgabor case 'l': 522210389Sgabor Lflag = false; 523210461Sgabor lflag = true; 524210389Sgabor break; 525210389Sgabor case 'm': 526210389Sgabor mflag = true; 527210389Sgabor errno = 0; 528244493Seadler mlimit = mcount = strtoll(optarg, &ep, 10); 529226035Sgabor if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 530210389Sgabor ((errno == EINVAL) && (mcount == 0))) 531210389Sgabor err(2, NULL); 532210389Sgabor else if (ep[0] != '\0') { 533210389Sgabor errno = EINVAL; 534210389Sgabor err(2, NULL); 535210389Sgabor } 536210389Sgabor break; 537226035Sgabor case 'M': 538226035Sgabor filebehave = FILE_LZMA; 539226035Sgabor break; 540210389Sgabor case 'n': 541210389Sgabor nflag = true; 542210389Sgabor break; 543210389Sgabor case 'O': 544210389Sgabor linkbehave = LINK_EXPLICIT; 545210389Sgabor break; 546210389Sgabor case 'o': 547210389Sgabor oflag = true; 548223009Sgabor cflags &= ~REG_NOSUB; 549210389Sgabor break; 550210389Sgabor case 'p': 551210389Sgabor linkbehave = LINK_SKIP; 552210389Sgabor break; 553210389Sgabor case 'q': 554210389Sgabor qflag = true; 555210389Sgabor break; 556210389Sgabor case 'S': 557210461Sgabor linkbehave = LINK_READ; 558210389Sgabor break; 559210389Sgabor case 'R': 560210389Sgabor case 'r': 561210389Sgabor dirbehave = DIR_RECURSE; 562210389Sgabor Hflag = true; 563210389Sgabor break; 564210389Sgabor case 's': 565210389Sgabor sflag = true; 566210389Sgabor break; 567210389Sgabor case 'U': 568210389Sgabor binbehave = BINFILE_BIN; 569210389Sgabor break; 570210389Sgabor case 'u': 571210389Sgabor case MMAP_OPT: 572226035Sgabor filebehave = FILE_MMAP; 573210389Sgabor break; 574210389Sgabor case 'V': 575226271Sgabor printf(getstr(9), getprogname(), VERSION); 576210389Sgabor exit(0); 577210389Sgabor case 'v': 578210389Sgabor vflag = true; 579210389Sgabor break; 580210389Sgabor case 'w': 581210389Sgabor wflag = true; 582223009Sgabor cflags &= ~REG_NOSUB; 583210389Sgabor break; 584210389Sgabor case 'x': 585210389Sgabor xflag = true; 586223009Sgabor cflags &= ~REG_NOSUB; 587210389Sgabor break; 588226035Sgabor case 'X': 589226035Sgabor filebehave = FILE_XZ; 590226035Sgabor break; 591210389Sgabor case 'Z': 592210389Sgabor filebehave = FILE_GZIP; 593210389Sgabor break; 594210389Sgabor case BIN_OPT: 595210461Sgabor if (strcasecmp("binary", optarg) == 0) 596210389Sgabor binbehave = BINFILE_BIN; 597210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 598210389Sgabor binbehave = BINFILE_SKIP; 599210461Sgabor else if (strcasecmp("text", optarg) == 0) 600210389Sgabor binbehave = BINFILE_TEXT; 601210389Sgabor else 602210622Sgabor errx(2, getstr(3), "--binary-files"); 603210389Sgabor break; 604210389Sgabor case COLOR_OPT: 605210461Sgabor color = NULL; 606210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 607210461Sgabor strcasecmp("tty", optarg) == 0 || 608210461Sgabor strcasecmp("if-tty", optarg) == 0) { 609210461Sgabor char *term; 610210461Sgabor 611210461Sgabor term = getenv("TERM"); 612210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 613210461Sgabor strcasecmp(term, "dumb") != 0) 614210461Sgabor color = init_color("01;31"); 615210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 616210461Sgabor strcasecmp("yes", optarg) == 0 || 617210461Sgabor strcasecmp("force", optarg) == 0) { 618210461Sgabor color = init_color("01;31"); 619210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 620210461Sgabor strcasecmp("none", optarg) != 0 && 621210461Sgabor strcasecmp("no", optarg) != 0) 622210622Sgabor errx(2, getstr(3), "--color"); 623223009Sgabor cflags &= ~REG_NOSUB; 624210389Sgabor break; 625210389Sgabor case LABEL_OPT: 626210389Sgabor label = optarg; 627210389Sgabor break; 628210389Sgabor case LINEBUF_OPT: 629210389Sgabor lbflag = true; 630210389Sgabor break; 631210389Sgabor case NULL_OPT: 632210389Sgabor nullflag = true; 633210389Sgabor break; 634210389Sgabor case R_INCLUDE_OPT: 635210578Sgabor finclude = true; 636210578Sgabor add_fpattern(optarg, INCL_PAT); 637210389Sgabor break; 638210389Sgabor case R_EXCLUDE_OPT: 639210578Sgabor fexclude = true; 640210578Sgabor add_fpattern(optarg, EXCL_PAT); 641210389Sgabor break; 642210389Sgabor case R_DINCLUDE_OPT: 643211364Sgabor dinclude = true; 644210578Sgabor add_dpattern(optarg, INCL_PAT); 645210389Sgabor break; 646210389Sgabor case R_DEXCLUDE_OPT: 647211364Sgabor dexclude = true; 648210578Sgabor add_dpattern(optarg, EXCL_PAT); 649210389Sgabor break; 650210389Sgabor case HELP_OPT: 651210389Sgabor default: 652210389Sgabor usage(); 653210389Sgabor } 654210389Sgabor lastc = c; 655210389Sgabor newarg = optind != prevoptind; 656210389Sgabor prevoptind = optind; 657210389Sgabor } 658210389Sgabor aargc -= optind; 659210389Sgabor aargv += optind; 660210389Sgabor 661226035Sgabor /* Empty pattern file matches nothing */ 662226035Sgabor if (!needpattern && (patterns == 0)) 663226035Sgabor exit(1); 664226035Sgabor 665210389Sgabor /* Fail if we don't have any pattern */ 666210389Sgabor if (aargc == 0 && needpattern) 667210389Sgabor usage(); 668210389Sgabor 669210389Sgabor /* Process patterns from command line */ 670210389Sgabor if (aargc != 0 && needpattern) { 671210389Sgabor add_pattern(*aargv, strlen(*aargv)); 672210389Sgabor --aargc; 673210389Sgabor ++aargv; 674210389Sgabor } 675210389Sgabor 676210389Sgabor switch (grepbehave) { 677210389Sgabor case GREP_BASIC: 678210389Sgabor break; 679226035Sgabor case GREP_FIXED: 680226035Sgabor /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 681226035Sgabor cflags |= 0020; 682226035Sgabor break; 683210389Sgabor case GREP_EXTENDED: 684210389Sgabor cflags |= REG_EXTENDED; 685210389Sgabor break; 686210389Sgabor default: 687210389Sgabor /* NOTREACHED */ 688210389Sgabor usage(); 689210389Sgabor } 690210389Sgabor 691210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 692210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 693226035Sgabor 694226035Sgabor /* Check if cheating is allowed (always is for fgrep). */ 695226035Sgabor for (i = 0; i < patterns; ++i) { 696226035Sgabor if (fastncomp(&fg_pattern[i], pattern[i].pat, 697226035Sgabor pattern[i].len, cflags) != 0) { 698226035Sgabor /* Fall back to full regex library */ 699226035Sgabor c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 700226035Sgabor if (c != 0) { 701226035Sgabor regerror(c, &r_pattern[i], re_error, 702226035Sgabor RE_ERROR_BUF); 703226035Sgabor errx(2, "%s", re_error); 704210389Sgabor } 705210389Sgabor } 706210389Sgabor } 707210389Sgabor 708210389Sgabor if (lbflag) 709210389Sgabor setlinebuf(stdout); 710210389Sgabor 711210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 712210389Sgabor hflag = true; 713210389Sgabor 714210389Sgabor if (aargc == 0) 715210389Sgabor exit(!procfile("-")); 716210389Sgabor 717210389Sgabor if (dirbehave == DIR_RECURSE) 718210389Sgabor c = grep_tree(aargv); 719211519Sdelphij else 720210578Sgabor for (c = 0; aargc--; ++aargv) { 721210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 722210578Sgabor continue; 723210389Sgabor c+= procfile(*aargv); 724210578Sgabor } 725210389Sgabor 726210389Sgabor#ifndef WITHOUT_NLS 727210389Sgabor catclose(catalog); 728210389Sgabor#endif 729210389Sgabor 730210389Sgabor /* Find out the correct return value according to the 731210389Sgabor results and the command line option. */ 732228319Sgabor exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 733210389Sgabor} 734