grep.c revision 246279
1220422Sgabor/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ 2220422Sgabor/* $FreeBSD: stable/9/usr.bin/grep/grep.c 246279 2013-02-03 03:38:44Z 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: stable/9/usr.bin/grep/grep.c 246279 2013-02-03 03:38:44Z 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> 41226261Sgabor#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 52226261Sgabor#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 */ 85210389Sgaborunsigned int patterns, pattern_sz; 86226261Sgaborstruct pat *pattern; 87210389Sgaborregex_t *r_pattern; 88226261Sgaborfastmatch_t *fg_pattern; 89210389Sgabor 90210389Sgabor/* Filename exclusion/inclusion patterns */ 91210578Sgaborunsigned int fpatterns, fpattern_sz; 92210578Sgaborunsigned int dpatterns, dpattern_sz; 93210578Sgaborstruct epat *dpattern, *fpattern; 94210389Sgabor 95210389Sgabor/* For regex errors */ 96210389Sgaborchar re_error[RE_ERROR_BUF + 1]; 97210389Sgabor 98210389Sgabor/* Command-line flags */ 99210389Sgaborunsigned long long Aflag; /* -A x: print x lines trailing each match */ 100210389Sgaborunsigned long long Bflag; /* -B x: print x lines leading each match */ 101210389Sgaborbool Hflag; /* -H: always print file name */ 102210389Sgaborbool Lflag; /* -L: only show names of files with no matches */ 103210389Sgaborbool bflag; /* -b: show block numbers for each match */ 104210389Sgaborbool cflag; /* -c: only show a count of matching lines */ 105210389Sgaborbool hflag; /* -h: don't print filename headers */ 106210389Sgaborbool iflag; /* -i: ignore case */ 107210389Sgaborbool lflag; /* -l: only show names of files with matches */ 108210389Sgaborbool mflag; /* -m x: stop reading the files after x matches */ 109226261Sgaborlong long mcount; /* count for -m */ 110246279Seadlerlong long mlimit; /* requested value for -m */ 111210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 112210389Sgaborbool oflag; /* -o: print only matching part */ 113210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 114210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 115210389Sgaborbool vflag; /* -v: only show non-matching lines */ 116210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 117210389Sgaborbool xflag; /* -x: pattern must match entire line */ 118210389Sgaborbool lbflag; /* --line-buffered */ 119210389Sgaborbool nullflag; /* --null */ 120210389Sgaborchar *label; /* --label */ 121210461Sgaborconst char *color; /* --color */ 122210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 123210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 124210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 125210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 126210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 127210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 128210389Sgabor 129211364Sgaborbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 130211364Sgaborbool fexclude, finclude; /* --exclude and --include */ 131210578Sgabor 132210389Sgaborenum { 133210389Sgabor BIN_OPT = CHAR_MAX + 1, 134210389Sgabor COLOR_OPT, 135210389Sgabor HELP_OPT, 136210389Sgabor MMAP_OPT, 137210389Sgabor LINEBUF_OPT, 138210389Sgabor LABEL_OPT, 139210389Sgabor NULL_OPT, 140210389Sgabor R_EXCLUDE_OPT, 141210389Sgabor R_INCLUDE_OPT, 142210389Sgabor R_DEXCLUDE_OPT, 143210389Sgabor R_DINCLUDE_OPT 144210389Sgabor}; 145210389Sgabor 146210461Sgaborstatic inline const char *init_color(const char *); 147210461Sgabor 148210389Sgabor/* Housekeeping */ 149210389Sgaborbool first = true; /* flag whether we are processing the first match */ 150210389Sgaborbool prev; /* flag whether or not the previous line matched */ 151210389Sgaborint tail; /* lines left to print */ 152229081Sgaborbool file_err; /* file reading error */ 153210389Sgabor 154210389Sgabor/* 155210389Sgabor * Prints usage information and returns 2. 156210389Sgabor */ 157210389Sgaborstatic void 158210389Sgaborusage(void) 159210389Sgabor{ 160226573Sgabor fprintf(stderr, getstr(4), getprogname()); 161210389Sgabor fprintf(stderr, "%s", getstr(5)); 162210389Sgabor fprintf(stderr, "%s", getstr(6)); 163210389Sgabor fprintf(stderr, "%s", getstr(7)); 164210389Sgabor exit(2); 165210389Sgabor} 166210389Sgabor 167226261Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; 168210389Sgabor 169210389Sgaborstruct option long_options[] = 170210389Sgabor{ 171210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 172210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 173210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 174210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 175210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 176210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 177210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 178210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 179210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 180210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 181210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 182210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 183210389Sgabor {"after-context", required_argument, NULL, 'A'}, 184210389Sgabor {"text", no_argument, NULL, 'a'}, 185210389Sgabor {"before-context", required_argument, NULL, 'B'}, 186210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 187210389Sgabor {"context", optional_argument, NULL, 'C'}, 188210389Sgabor {"count", no_argument, NULL, 'c'}, 189210389Sgabor {"devices", required_argument, NULL, 'D'}, 190210389Sgabor {"directories", required_argument, NULL, 'd'}, 191210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 192210389Sgabor {"regexp", required_argument, NULL, 'e'}, 193210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 194210389Sgabor {"file", required_argument, NULL, 'f'}, 195210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 196210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 197210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 198210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 199210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 200210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 201210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 202210389Sgabor {"max-count", required_argument, NULL, 'm'}, 203226261Sgabor {"lzma", no_argument, NULL, 'M'}, 204210389Sgabor {"line-number", no_argument, NULL, 'n'}, 205210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 206210389Sgabor {"quiet", no_argument, NULL, 'q'}, 207210389Sgabor {"silent", no_argument, NULL, 'q'}, 208210389Sgabor {"recursive", no_argument, NULL, 'r'}, 209210389Sgabor {"no-messages", no_argument, NULL, 's'}, 210210389Sgabor {"binary", no_argument, NULL, 'U'}, 211210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 212210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 213210389Sgabor {"version", no_argument, NULL, 'V'}, 214210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 215210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 216226261Sgabor {"xz", no_argument, NULL, 'X'}, 217210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 218210389Sgabor {NULL, no_argument, NULL, 0} 219210389Sgabor}; 220210389Sgabor 221210389Sgabor/* 222210389Sgabor * Adds a searching pattern to the internal array. 223210389Sgabor */ 224210389Sgaborstatic void 225210389Sgaboradd_pattern(char *pat, size_t len) 226210389Sgabor{ 227210389Sgabor 228226261Sgabor /* Do not add further pattern is we already match everything */ 229226261Sgabor if (matchall) 230226261Sgabor return; 231226261Sgabor 232210389Sgabor /* Check if we can do a shortcut */ 233226261Sgabor if (len == 0) { 234210389Sgabor matchall = true; 235226261Sgabor for (unsigned int i = 0; i < patterns; i++) { 236226261Sgabor free(pattern[i].pat); 237226261Sgabor } 238226261Sgabor pattern = grep_realloc(pattern, sizeof(struct pat)); 239226261Sgabor pattern[0].pat = NULL; 240226261Sgabor pattern[0].len = 0; 241226261Sgabor patterns = 1; 242210389Sgabor return; 243210389Sgabor } 244210389Sgabor /* Increase size if necessary */ 245210389Sgabor if (patterns == pattern_sz) { 246210389Sgabor pattern_sz *= 2; 247210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 248226261Sgabor sizeof(struct pat)); 249210389Sgabor } 250210389Sgabor if (len > 0 && pat[len - 1] == '\n') 251210389Sgabor --len; 252210389Sgabor /* pat may not be NUL-terminated */ 253226261Sgabor pattern[patterns].pat = grep_malloc(len + 1); 254226261Sgabor memcpy(pattern[patterns].pat, pat, len); 255226261Sgabor pattern[patterns].len = len; 256226261Sgabor pattern[patterns].pat[len] = '\0'; 257210389Sgabor ++patterns; 258210389Sgabor} 259210389Sgabor 260210389Sgabor/* 261210578Sgabor * Adds a file include/exclude pattern to the internal array. 262210389Sgabor */ 263210389Sgaborstatic void 264210578Sgaboradd_fpattern(const char *pat, int mode) 265210389Sgabor{ 266210389Sgabor 267210389Sgabor /* Increase size if necessary */ 268210578Sgabor if (fpatterns == fpattern_sz) { 269210578Sgabor fpattern_sz *= 2; 270210578Sgabor fpattern = grep_realloc(fpattern, ++fpattern_sz * 271210389Sgabor sizeof(struct epat)); 272210389Sgabor } 273210578Sgabor fpattern[fpatterns].pat = grep_strdup(pat); 274210578Sgabor fpattern[fpatterns].mode = mode; 275210578Sgabor ++fpatterns; 276210389Sgabor} 277210389Sgabor 278210389Sgabor/* 279210578Sgabor * Adds a directory include/exclude pattern to the internal array. 280210578Sgabor */ 281210578Sgaborstatic void 282210578Sgaboradd_dpattern(const char *pat, int mode) 283210578Sgabor{ 284210578Sgabor 285210578Sgabor /* Increase size if necessary */ 286210578Sgabor if (dpatterns == dpattern_sz) { 287210578Sgabor dpattern_sz *= 2; 288210578Sgabor dpattern = grep_realloc(dpattern, ++dpattern_sz * 289210578Sgabor sizeof(struct epat)); 290210578Sgabor } 291210578Sgabor dpattern[dpatterns].pat = grep_strdup(pat); 292210578Sgabor dpattern[dpatterns].mode = mode; 293210578Sgabor ++dpatterns; 294210578Sgabor} 295210578Sgabor 296210578Sgabor/* 297210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 298210389Sgabor */ 299210389Sgaborstatic void 300210389Sgaborread_patterns(const char *fn) 301210389Sgabor{ 302226261Sgabor struct stat st; 303210389Sgabor FILE *f; 304210389Sgabor char *line; 305210389Sgabor size_t len; 306210389Sgabor 307210389Sgabor if ((f = fopen(fn, "r")) == NULL) 308210389Sgabor err(2, "%s", fn); 309226261Sgabor if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { 310226261Sgabor fclose(f); 311226261Sgabor return; 312226261Sgabor } 313226261Sgabor while ((line = fgetln(f, &len)) != NULL) 314226261Sgabor add_pattern(line, line[0] == '\n' ? 0 : len); 315210389Sgabor if (ferror(f)) 316210389Sgabor err(2, "%s", fn); 317210389Sgabor fclose(f); 318210389Sgabor} 319210389Sgabor 320210461Sgaborstatic inline const char * 321210461Sgaborinit_color(const char *d) 322210461Sgabor{ 323210461Sgabor char *c; 324210461Sgabor 325210461Sgabor c = getenv("GREP_COLOR"); 326224937Sgabor return (c != NULL && c[0] != '\0' ? c : d); 327210461Sgabor} 328210461Sgabor 329210389Sgaborint 330210389Sgabormain(int argc, char *argv[]) 331210389Sgabor{ 332210389Sgabor char **aargv, **eargv, *eopts; 333226573Sgabor char *ep; 334226573Sgabor const char *pn; 335210389Sgabor unsigned long long l; 336210389Sgabor unsigned int aargc, eargc, i; 337210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 338210389Sgabor 339210389Sgabor setlocale(LC_ALL, ""); 340210389Sgabor 341210389Sgabor#ifndef WITHOUT_NLS 342210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 343210389Sgabor#endif 344210389Sgabor 345210389Sgabor /* Check what is the program name of the binary. In this 346210389Sgabor way we can have all the funcionalities in one binary 347210389Sgabor without the need of scripting and using ugly hacks. */ 348226573Sgabor pn = getprogname(); 349226261Sgabor if (pn[0] == 'b' && pn[1] == 'z') { 350226261Sgabor filebehave = FILE_BZIP; 351226261Sgabor pn += 2; 352226261Sgabor } else if (pn[0] == 'x' && pn[1] == 'z') { 353226261Sgabor filebehave = FILE_XZ; 354226261Sgabor pn += 2; 355226261Sgabor } else if (pn[0] == 'l' && pn[1] == 'z') { 356226261Sgabor filebehave = FILE_LZMA; 357226261Sgabor pn += 2; 358226261Sgabor } else if (pn[0] == 'z') { 359226261Sgabor filebehave = FILE_GZIP; 360226261Sgabor pn += 1; 361226261Sgabor } 362226261Sgabor switch (pn[0]) { 363210389Sgabor case 'e': 364210389Sgabor grepbehave = GREP_EXTENDED; 365210389Sgabor break; 366210389Sgabor case 'f': 367210389Sgabor grepbehave = GREP_FIXED; 368210389Sgabor break; 369210389Sgabor } 370210389Sgabor 371210389Sgabor lastc = '\0'; 372210389Sgabor newarg = 1; 373210389Sgabor prevoptind = 1; 374210389Sgabor needpattern = 1; 375210389Sgabor 376210389Sgabor eopts = getenv("GREP_OPTIONS"); 377210389Sgabor 378211364Sgabor /* support for extra arguments in GREP_OPTIONS */ 379211364Sgabor eargc = 0; 380224937Sgabor if (eopts != NULL && eopts[0] != '\0') { 381210389Sgabor char *str; 382210389Sgabor 383211364Sgabor /* make an estimation of how many extra arguments we have */ 384211364Sgabor for (unsigned int j = 0; j < strlen(eopts); j++) 385211364Sgabor if (eopts[j] == ' ') 386210389Sgabor eargc++; 387210389Sgabor 388210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 389210389Sgabor 390210389Sgabor eargc = 0; 391211364Sgabor /* parse extra arguments */ 392211364Sgabor while ((str = strsep(&eopts, " ")) != NULL) 393224937Sgabor if (str[0] != '\0') 394224937Sgabor eargv[eargc++] = grep_strdup(str); 395210389Sgabor 396210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 397210430Sdelphij sizeof(char *)); 398211364Sgabor 399210389Sgabor aargv[0] = argv[0]; 400211364Sgabor for (i = 0; i < eargc; i++) 401211364Sgabor aargv[i + 1] = eargv[i]; 402211364Sgabor for (int j = 1; j < argc; j++, i++) 403211364Sgabor aargv[i + 1] = argv[j]; 404210389Sgabor 405211364Sgabor aargc = eargc + argc; 406210389Sgabor } else { 407210389Sgabor aargv = argv; 408210389Sgabor aargc = argc; 409210389Sgabor } 410210389Sgabor 411210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 412210389Sgabor -1)) { 413210389Sgabor switch (c) { 414210389Sgabor case '0': case '1': case '2': case '3': case '4': 415210389Sgabor case '5': case '6': case '7': case '8': case '9': 416210389Sgabor if (newarg || !isdigit(lastc)) 417210389Sgabor Aflag = 0; 418210389Sgabor else if (Aflag > LLONG_MAX / 10) { 419210389Sgabor errno = ERANGE; 420210389Sgabor err(2, NULL); 421210389Sgabor } 422210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 423210389Sgabor break; 424210389Sgabor case 'C': 425210389Sgabor if (optarg == NULL) { 426210389Sgabor Aflag = Bflag = 2; 427210389Sgabor break; 428210389Sgabor } 429210389Sgabor /* FALLTHROUGH */ 430210389Sgabor case 'A': 431210389Sgabor /* FALLTHROUGH */ 432210389Sgabor case 'B': 433210389Sgabor errno = 0; 434210389Sgabor l = strtoull(optarg, &ep, 10); 435210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 436210389Sgabor ((errno == EINVAL) && (l == 0))) 437210389Sgabor err(2, NULL); 438210389Sgabor else if (ep[0] != '\0') { 439210389Sgabor errno = EINVAL; 440210389Sgabor err(2, NULL); 441210389Sgabor } 442210389Sgabor if (c == 'A') 443210389Sgabor Aflag = l; 444210389Sgabor else if (c == 'B') 445210389Sgabor Bflag = l; 446210389Sgabor else 447210389Sgabor Aflag = Bflag = l; 448210389Sgabor break; 449210389Sgabor case 'a': 450210389Sgabor binbehave = BINFILE_TEXT; 451210389Sgabor break; 452210389Sgabor case 'b': 453210389Sgabor bflag = true; 454210389Sgabor break; 455210389Sgabor case 'c': 456210389Sgabor cflag = true; 457210389Sgabor break; 458210389Sgabor case 'D': 459210461Sgabor if (strcasecmp(optarg, "skip") == 0) 460210389Sgabor devbehave = DEV_SKIP; 461210461Sgabor else if (strcasecmp(optarg, "read") == 0) 462210461Sgabor devbehave = DEV_READ; 463210622Sgabor else 464210622Sgabor errx(2, getstr(3), "--devices"); 465210389Sgabor break; 466210389Sgabor case 'd': 467210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 468210389Sgabor Hflag = true; 469210389Sgabor dirbehave = DIR_RECURSE; 470210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 471210389Sgabor dirbehave = DIR_SKIP; 472210461Sgabor else if (strcasecmp("read", optarg) == 0) 473210461Sgabor dirbehave = DIR_READ; 474210622Sgabor else 475210622Sgabor errx(2, getstr(3), "--directories"); 476210389Sgabor break; 477210389Sgabor case 'E': 478210389Sgabor grepbehave = GREP_EXTENDED; 479210389Sgabor break; 480210389Sgabor case 'e': 481245996Sgabor { 482245996Sgabor char *token; 483245996Sgabor char *string = optarg; 484245996Sgabor 485245996Sgabor while ((token = strsep(&string, "\n")) != NULL) 486245996Sgabor add_pattern(token, strlen(token)); 487245996Sgabor } 488210389Sgabor needpattern = 0; 489210389Sgabor break; 490210389Sgabor case 'F': 491210389Sgabor grepbehave = GREP_FIXED; 492210389Sgabor break; 493210389Sgabor case 'f': 494210389Sgabor read_patterns(optarg); 495210389Sgabor needpattern = 0; 496210389Sgabor break; 497210389Sgabor case 'G': 498210389Sgabor grepbehave = GREP_BASIC; 499210389Sgabor break; 500210389Sgabor case 'H': 501210389Sgabor Hflag = true; 502210389Sgabor break; 503210389Sgabor case 'h': 504210389Sgabor Hflag = false; 505210389Sgabor hflag = true; 506210389Sgabor break; 507210389Sgabor case 'I': 508210389Sgabor binbehave = BINFILE_SKIP; 509210389Sgabor break; 510210389Sgabor case 'i': 511210389Sgabor case 'y': 512210389Sgabor iflag = true; 513210389Sgabor cflags |= REG_ICASE; 514210389Sgabor break; 515210389Sgabor case 'J': 516226573Sgabor#ifdef WITHOUT_BZIP2 517226573Sgabor errno = EOPNOTSUPP; 518226573Sgabor err(2, "bzip2 support was disabled at compile-time"); 519226573Sgabor#endif 520210389Sgabor filebehave = FILE_BZIP; 521210389Sgabor break; 522210389Sgabor case 'L': 523210389Sgabor lflag = false; 524210461Sgabor Lflag = true; 525210389Sgabor break; 526210389Sgabor case 'l': 527210389Sgabor Lflag = false; 528210461Sgabor lflag = true; 529210389Sgabor break; 530210389Sgabor case 'm': 531210389Sgabor mflag = true; 532210389Sgabor errno = 0; 533246279Seadler mlimit = mcount = strtoll(optarg, &ep, 10); 534226261Sgabor if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 535210389Sgabor ((errno == EINVAL) && (mcount == 0))) 536210389Sgabor err(2, NULL); 537210389Sgabor else if (ep[0] != '\0') { 538210389Sgabor errno = EINVAL; 539210389Sgabor err(2, NULL); 540210389Sgabor } 541210389Sgabor break; 542226261Sgabor case 'M': 543226261Sgabor filebehave = FILE_LZMA; 544226261Sgabor break; 545210389Sgabor case 'n': 546210389Sgabor nflag = true; 547210389Sgabor break; 548210389Sgabor case 'O': 549210389Sgabor linkbehave = LINK_EXPLICIT; 550210389Sgabor break; 551210389Sgabor case 'o': 552210389Sgabor oflag = true; 553223009Sgabor cflags &= ~REG_NOSUB; 554210389Sgabor break; 555210389Sgabor case 'p': 556210389Sgabor linkbehave = LINK_SKIP; 557210389Sgabor break; 558210389Sgabor case 'q': 559210389Sgabor qflag = true; 560210389Sgabor break; 561210389Sgabor case 'S': 562210461Sgabor linkbehave = LINK_READ; 563210389Sgabor break; 564210389Sgabor case 'R': 565210389Sgabor case 'r': 566210389Sgabor dirbehave = DIR_RECURSE; 567210389Sgabor Hflag = true; 568210389Sgabor break; 569210389Sgabor case 's': 570210389Sgabor sflag = true; 571210389Sgabor break; 572210389Sgabor case 'U': 573210389Sgabor binbehave = BINFILE_BIN; 574210389Sgabor break; 575210389Sgabor case 'u': 576210389Sgabor case MMAP_OPT: 577226261Sgabor filebehave = FILE_MMAP; 578210389Sgabor break; 579210389Sgabor case 'V': 580226573Sgabor printf(getstr(9), getprogname(), VERSION); 581210389Sgabor exit(0); 582210389Sgabor case 'v': 583210389Sgabor vflag = true; 584210389Sgabor break; 585210389Sgabor case 'w': 586210389Sgabor wflag = true; 587223009Sgabor cflags &= ~REG_NOSUB; 588210389Sgabor break; 589210389Sgabor case 'x': 590210389Sgabor xflag = true; 591223009Sgabor cflags &= ~REG_NOSUB; 592210389Sgabor break; 593226261Sgabor case 'X': 594226261Sgabor filebehave = FILE_XZ; 595226261Sgabor break; 596210389Sgabor case 'Z': 597210389Sgabor filebehave = FILE_GZIP; 598210389Sgabor break; 599210389Sgabor case BIN_OPT: 600210461Sgabor if (strcasecmp("binary", optarg) == 0) 601210389Sgabor binbehave = BINFILE_BIN; 602210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 603210389Sgabor binbehave = BINFILE_SKIP; 604210461Sgabor else if (strcasecmp("text", optarg) == 0) 605210389Sgabor binbehave = BINFILE_TEXT; 606210389Sgabor else 607210622Sgabor errx(2, getstr(3), "--binary-files"); 608210389Sgabor break; 609210389Sgabor case COLOR_OPT: 610210461Sgabor color = NULL; 611210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 612210461Sgabor strcasecmp("tty", optarg) == 0 || 613210461Sgabor strcasecmp("if-tty", optarg) == 0) { 614210461Sgabor char *term; 615210461Sgabor 616210461Sgabor term = getenv("TERM"); 617210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 618210461Sgabor strcasecmp(term, "dumb") != 0) 619210461Sgabor color = init_color("01;31"); 620210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 621210461Sgabor strcasecmp("yes", optarg) == 0 || 622210461Sgabor strcasecmp("force", optarg) == 0) { 623210461Sgabor color = init_color("01;31"); 624210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 625210461Sgabor strcasecmp("none", optarg) != 0 && 626210461Sgabor strcasecmp("no", optarg) != 0) 627210622Sgabor errx(2, getstr(3), "--color"); 628223009Sgabor cflags &= ~REG_NOSUB; 629210389Sgabor break; 630210389Sgabor case LABEL_OPT: 631210389Sgabor label = optarg; 632210389Sgabor break; 633210389Sgabor case LINEBUF_OPT: 634210389Sgabor lbflag = true; 635210389Sgabor break; 636210389Sgabor case NULL_OPT: 637210389Sgabor nullflag = true; 638210389Sgabor break; 639210389Sgabor case R_INCLUDE_OPT: 640210578Sgabor finclude = true; 641210578Sgabor add_fpattern(optarg, INCL_PAT); 642210389Sgabor break; 643210389Sgabor case R_EXCLUDE_OPT: 644210578Sgabor fexclude = true; 645210578Sgabor add_fpattern(optarg, EXCL_PAT); 646210389Sgabor break; 647210389Sgabor case R_DINCLUDE_OPT: 648211364Sgabor dinclude = true; 649210578Sgabor add_dpattern(optarg, INCL_PAT); 650210389Sgabor break; 651210389Sgabor case R_DEXCLUDE_OPT: 652211364Sgabor dexclude = true; 653210578Sgabor add_dpattern(optarg, EXCL_PAT); 654210389Sgabor break; 655210389Sgabor case HELP_OPT: 656210389Sgabor default: 657210389Sgabor usage(); 658210389Sgabor } 659210389Sgabor lastc = c; 660210389Sgabor newarg = optind != prevoptind; 661210389Sgabor prevoptind = optind; 662210389Sgabor } 663210389Sgabor aargc -= optind; 664210389Sgabor aargv += optind; 665210389Sgabor 666226261Sgabor /* Empty pattern file matches nothing */ 667226261Sgabor if (!needpattern && (patterns == 0)) 668226261Sgabor exit(1); 669226261Sgabor 670210389Sgabor /* Fail if we don't have any pattern */ 671210389Sgabor if (aargc == 0 && needpattern) 672210389Sgabor usage(); 673210389Sgabor 674210389Sgabor /* Process patterns from command line */ 675210389Sgabor if (aargc != 0 && needpattern) { 676245996Sgabor char *token; 677245996Sgabor char *string = *aargv; 678245996Sgabor 679245996Sgabor while ((token = strsep(&string, "\n")) != NULL) 680245996Sgabor add_pattern(token, strlen(token)); 681210389Sgabor --aargc; 682210389Sgabor ++aargv; 683210389Sgabor } 684210389Sgabor 685210389Sgabor switch (grepbehave) { 686210389Sgabor case GREP_BASIC: 687210389Sgabor break; 688226261Sgabor case GREP_FIXED: 689226261Sgabor /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 690226261Sgabor cflags |= 0020; 691226261Sgabor break; 692210389Sgabor case GREP_EXTENDED: 693210389Sgabor cflags |= REG_EXTENDED; 694210389Sgabor break; 695210389Sgabor default: 696210389Sgabor /* NOTREACHED */ 697210389Sgabor usage(); 698210389Sgabor } 699210389Sgabor 700210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 701210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 702226261Sgabor 703226261Sgabor /* Check if cheating is allowed (always is for fgrep). */ 704226261Sgabor for (i = 0; i < patterns; ++i) { 705226261Sgabor if (fastncomp(&fg_pattern[i], pattern[i].pat, 706226261Sgabor pattern[i].len, cflags) != 0) { 707226261Sgabor /* Fall back to full regex library */ 708226261Sgabor c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 709226261Sgabor if (c != 0) { 710226261Sgabor regerror(c, &r_pattern[i], re_error, 711226261Sgabor RE_ERROR_BUF); 712226261Sgabor errx(2, "%s", re_error); 713210389Sgabor } 714210389Sgabor } 715210389Sgabor } 716210389Sgabor 717210389Sgabor if (lbflag) 718210389Sgabor setlinebuf(stdout); 719210389Sgabor 720210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 721210389Sgabor hflag = true; 722210389Sgabor 723210389Sgabor if (aargc == 0) 724210389Sgabor exit(!procfile("-")); 725210389Sgabor 726210389Sgabor if (dirbehave == DIR_RECURSE) 727210389Sgabor c = grep_tree(aargv); 728211519Sdelphij else 729210578Sgabor for (c = 0; aargc--; ++aargv) { 730210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 731210578Sgabor continue; 732210389Sgabor c+= procfile(*aargv); 733210578Sgabor } 734210389Sgabor 735210389Sgabor#ifndef WITHOUT_NLS 736210389Sgabor catclose(catalog); 737210389Sgabor#endif 738210389Sgabor 739210389Sgabor /* Find out the correct return value according to the 740210389Sgabor results and the command line option. */ 741229081Sgabor exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 742210389Sgabor} 743