1268966Spfg/* $NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $ */ 2220422Sgabor/* $FreeBSD$ */ 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$"); 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> 47268966Spfg#define _WITH_GETLINE 48210389Sgabor#include <stdio.h> 49210389Sgabor#include <stdlib.h> 50210389Sgabor#include <string.h> 51210389Sgabor#include <unistd.h> 52210389Sgabor 53226035Sgabor#include "fastmatch.h" 54210389Sgabor#include "grep.h" 55210389Sgabor 56210389Sgabor#ifndef WITHOUT_NLS 57210389Sgabor#include <nl_types.h> 58210389Sgabornl_catd catalog; 59210389Sgabor#endif 60210389Sgabor 61210389Sgabor/* 62210389Sgabor * Default messags to use when NLS is disabled or no catalogue 63210389Sgabor * is found. 64210389Sgabor */ 65210389Sgaborconst char *errstr[] = { 66210389Sgabor "", 67210389Sgabor/* 1*/ "(standard input)", 68210389Sgabor/* 2*/ "cannot read bzip2 compressed file", 69210622Sgabor/* 3*/ "unknown %s option", 70210389Sgabor/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", 71210389Sgabor/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", 72210389Sgabor/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", 73210389Sgabor/* 7*/ "\t[--null] [pattern] [file ...]\n", 74210622Sgabor/* 8*/ "Binary file %s matches\n", 75210622Sgabor/* 9*/ "%s (BSD grep) %s\n", 76210389Sgabor}; 77210389Sgabor 78210389Sgabor/* Flags passed to regcomp() and regexec() */ 79223009Sgaborint cflags = REG_NOSUB; 80210389Sgaborint eflags = REG_STARTEND; 81210389Sgabor 82210389Sgabor/* Shortcut for matching all cases like empty regex */ 83210389Sgaborbool matchall; 84210389Sgabor 85210389Sgabor/* Searching patterns */ 86241737Sedunsigned int patterns; 87241737Sedstatic unsigned int pattern_sz; 88226035Sgaborstruct pat *pattern; 89210389Sgaborregex_t *r_pattern; 90226035Sgaborfastmatch_t *fg_pattern; 91210389Sgabor 92210389Sgabor/* Filename exclusion/inclusion patterns */ 93241737Sedunsigned int fpatterns, dpatterns; 94241737Sedstatic unsigned int fpattern_sz, dpattern_sz; 95210578Sgaborstruct epat *dpattern, *fpattern; 96210389Sgabor 97210389Sgabor/* For regex errors */ 98210389Sgaborchar re_error[RE_ERROR_BUF + 1]; 99210389Sgabor 100210389Sgabor/* Command-line flags */ 101210389Sgaborunsigned long long Aflag; /* -A x: print x lines trailing each match */ 102210389Sgaborunsigned long long Bflag; /* -B x: print x lines leading each match */ 103210389Sgaborbool Hflag; /* -H: always print file name */ 104210389Sgaborbool Lflag; /* -L: only show names of files with no matches */ 105210389Sgaborbool bflag; /* -b: show block numbers for each match */ 106210389Sgaborbool cflag; /* -c: only show a count of matching lines */ 107210389Sgaborbool hflag; /* -h: don't print filename headers */ 108210389Sgaborbool iflag; /* -i: ignore case */ 109210389Sgaborbool lflag; /* -l: only show names of files with matches */ 110210389Sgaborbool mflag; /* -m x: stop reading the files after x matches */ 111226035Sgaborlong long mcount; /* count for -m */ 112244493Seadlerlong long mlimit; /* requested value for -m */ 113210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 114210389Sgaborbool oflag; /* -o: print only matching part */ 115210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 116210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 117210389Sgaborbool vflag; /* -v: only show non-matching lines */ 118210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 119210389Sgaborbool xflag; /* -x: pattern must match entire line */ 120210389Sgaborbool lbflag; /* --line-buffered */ 121210389Sgaborbool nullflag; /* --null */ 122210389Sgaborchar *label; /* --label */ 123210461Sgaborconst char *color; /* --color */ 124210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 125210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 126210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 127210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 128210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 129210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 130210389Sgabor 131211364Sgaborbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 132211364Sgaborbool fexclude, finclude; /* --exclude and --include */ 133210578Sgabor 134210389Sgaborenum { 135210389Sgabor BIN_OPT = CHAR_MAX + 1, 136210389Sgabor COLOR_OPT, 137210389Sgabor HELP_OPT, 138210389Sgabor MMAP_OPT, 139210389Sgabor LINEBUF_OPT, 140210389Sgabor LABEL_OPT, 141210389Sgabor NULL_OPT, 142210389Sgabor R_EXCLUDE_OPT, 143210389Sgabor R_INCLUDE_OPT, 144210389Sgabor R_DEXCLUDE_OPT, 145210389Sgabor R_DINCLUDE_OPT 146210389Sgabor}; 147210389Sgabor 148210461Sgaborstatic inline const char *init_color(const char *); 149210461Sgabor 150210389Sgabor/* Housekeeping */ 151210389Sgaborbool first = true; /* flag whether we are processing the first match */ 152210389Sgaborbool prev; /* flag whether or not the previous line matched */ 153210389Sgaborint tail; /* lines left to print */ 154228319Sgaborbool file_err; /* file reading error */ 155210389Sgabor 156210389Sgabor/* 157210389Sgabor * Prints usage information and returns 2. 158210389Sgabor */ 159210389Sgaborstatic void 160210389Sgaborusage(void) 161210389Sgabor{ 162226271Sgabor fprintf(stderr, getstr(4), getprogname()); 163210389Sgabor fprintf(stderr, "%s", getstr(5)); 164210389Sgabor fprintf(stderr, "%s", getstr(6)); 165210389Sgabor fprintf(stderr, "%s", getstr(7)); 166210389Sgabor exit(2); 167210389Sgabor} 168210389Sgabor 169226035Sgaborstatic const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; 170210389Sgabor 171228395Sedstatic const struct option long_options[] = 172210389Sgabor{ 173210389Sgabor {"binary-files", required_argument, NULL, BIN_OPT}, 174210389Sgabor {"help", no_argument, NULL, HELP_OPT}, 175210389Sgabor {"mmap", no_argument, NULL, MMAP_OPT}, 176210389Sgabor {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 177210389Sgabor {"label", required_argument, NULL, LABEL_OPT}, 178210389Sgabor {"null", no_argument, NULL, NULL_OPT}, 179210389Sgabor {"color", optional_argument, NULL, COLOR_OPT}, 180210389Sgabor {"colour", optional_argument, NULL, COLOR_OPT}, 181210389Sgabor {"exclude", required_argument, NULL, R_EXCLUDE_OPT}, 182210389Sgabor {"include", required_argument, NULL, R_INCLUDE_OPT}, 183210389Sgabor {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT}, 184210389Sgabor {"include-dir", required_argument, NULL, R_DINCLUDE_OPT}, 185210389Sgabor {"after-context", required_argument, NULL, 'A'}, 186210389Sgabor {"text", no_argument, NULL, 'a'}, 187210389Sgabor {"before-context", required_argument, NULL, 'B'}, 188210389Sgabor {"byte-offset", no_argument, NULL, 'b'}, 189210389Sgabor {"context", optional_argument, NULL, 'C'}, 190210389Sgabor {"count", no_argument, NULL, 'c'}, 191210389Sgabor {"devices", required_argument, NULL, 'D'}, 192210389Sgabor {"directories", required_argument, NULL, 'd'}, 193210389Sgabor {"extended-regexp", no_argument, NULL, 'E'}, 194210389Sgabor {"regexp", required_argument, NULL, 'e'}, 195210389Sgabor {"fixed-strings", no_argument, NULL, 'F'}, 196210389Sgabor {"file", required_argument, NULL, 'f'}, 197210389Sgabor {"basic-regexp", no_argument, NULL, 'G'}, 198210389Sgabor {"no-filename", no_argument, NULL, 'h'}, 199210389Sgabor {"with-filename", no_argument, NULL, 'H'}, 200210389Sgabor {"ignore-case", no_argument, NULL, 'i'}, 201210389Sgabor {"bz2decompress", no_argument, NULL, 'J'}, 202210389Sgabor {"files-with-matches", no_argument, NULL, 'l'}, 203210389Sgabor {"files-without-match", no_argument, NULL, 'L'}, 204210389Sgabor {"max-count", required_argument, NULL, 'm'}, 205226035Sgabor {"lzma", no_argument, NULL, 'M'}, 206210389Sgabor {"line-number", no_argument, NULL, 'n'}, 207210389Sgabor {"only-matching", no_argument, NULL, 'o'}, 208210389Sgabor {"quiet", no_argument, NULL, 'q'}, 209210389Sgabor {"silent", no_argument, NULL, 'q'}, 210210389Sgabor {"recursive", no_argument, NULL, 'r'}, 211210389Sgabor {"no-messages", no_argument, NULL, 's'}, 212210389Sgabor {"binary", no_argument, NULL, 'U'}, 213210389Sgabor {"unix-byte-offsets", no_argument, NULL, 'u'}, 214210389Sgabor {"invert-match", no_argument, NULL, 'v'}, 215210389Sgabor {"version", no_argument, NULL, 'V'}, 216210389Sgabor {"word-regexp", no_argument, NULL, 'w'}, 217210389Sgabor {"line-regexp", no_argument, NULL, 'x'}, 218226035Sgabor {"xz", no_argument, NULL, 'X'}, 219210389Sgabor {"decompress", no_argument, NULL, 'Z'}, 220210389Sgabor {NULL, no_argument, NULL, 0} 221210389Sgabor}; 222210389Sgabor 223210389Sgabor/* 224210389Sgabor * Adds a searching pattern to the internal array. 225210389Sgabor */ 226210389Sgaborstatic void 227210389Sgaboradd_pattern(char *pat, size_t len) 228210389Sgabor{ 229210389Sgabor 230226035Sgabor /* Do not add further pattern is we already match everything */ 231226035Sgabor if (matchall) 232226035Sgabor return; 233226035Sgabor 234210389Sgabor /* Check if we can do a shortcut */ 235226035Sgabor if (len == 0) { 236210389Sgabor matchall = true; 237226035Sgabor for (unsigned int i = 0; i < patterns; i++) { 238226035Sgabor free(pattern[i].pat); 239226035Sgabor } 240226035Sgabor pattern = grep_realloc(pattern, sizeof(struct pat)); 241226035Sgabor pattern[0].pat = NULL; 242226035Sgabor pattern[0].len = 0; 243226035Sgabor patterns = 1; 244210389Sgabor return; 245210389Sgabor } 246210389Sgabor /* Increase size if necessary */ 247210389Sgabor if (patterns == pattern_sz) { 248210389Sgabor pattern_sz *= 2; 249210389Sgabor pattern = grep_realloc(pattern, ++pattern_sz * 250226035Sgabor sizeof(struct pat)); 251210389Sgabor } 252210389Sgabor if (len > 0 && pat[len - 1] == '\n') 253210389Sgabor --len; 254210389Sgabor /* pat may not be NUL-terminated */ 255226035Sgabor pattern[patterns].pat = grep_malloc(len + 1); 256226035Sgabor memcpy(pattern[patterns].pat, pat, len); 257226035Sgabor pattern[patterns].len = len; 258226035Sgabor pattern[patterns].pat[len] = '\0'; 259210389Sgabor ++patterns; 260210389Sgabor} 261210389Sgabor 262210389Sgabor/* 263210578Sgabor * Adds a file include/exclude pattern to the internal array. 264210389Sgabor */ 265210389Sgaborstatic void 266210578Sgaboradd_fpattern(const char *pat, int mode) 267210389Sgabor{ 268210389Sgabor 269210389Sgabor /* Increase size if necessary */ 270210578Sgabor if (fpatterns == fpattern_sz) { 271210578Sgabor fpattern_sz *= 2; 272210578Sgabor fpattern = grep_realloc(fpattern, ++fpattern_sz * 273210389Sgabor sizeof(struct epat)); 274210389Sgabor } 275210578Sgabor fpattern[fpatterns].pat = grep_strdup(pat); 276210578Sgabor fpattern[fpatterns].mode = mode; 277210578Sgabor ++fpatterns; 278210389Sgabor} 279210389Sgabor 280210389Sgabor/* 281210578Sgabor * Adds a directory include/exclude pattern to the internal array. 282210578Sgabor */ 283210578Sgaborstatic void 284210578Sgaboradd_dpattern(const char *pat, int mode) 285210578Sgabor{ 286210578Sgabor 287210578Sgabor /* Increase size if necessary */ 288210578Sgabor if (dpatterns == dpattern_sz) { 289210578Sgabor dpattern_sz *= 2; 290210578Sgabor dpattern = grep_realloc(dpattern, ++dpattern_sz * 291210578Sgabor sizeof(struct epat)); 292210578Sgabor } 293210578Sgabor dpattern[dpatterns].pat = grep_strdup(pat); 294210578Sgabor dpattern[dpatterns].mode = mode; 295210578Sgabor ++dpatterns; 296210578Sgabor} 297210578Sgabor 298210578Sgabor/* 299210389Sgabor * Reads searching patterns from a file and adds them with add_pattern(). 300210389Sgabor */ 301210389Sgaborstatic void 302210389Sgaborread_patterns(const char *fn) 303210389Sgabor{ 304226035Sgabor struct stat st; 305210389Sgabor FILE *f; 306210389Sgabor char *line; 307210389Sgabor size_t len; 308268966Spfg ssize_t rlen; 309210389Sgabor 310210389Sgabor if ((f = fopen(fn, "r")) == NULL) 311210389Sgabor err(2, "%s", fn); 312226035Sgabor if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) { 313226035Sgabor fclose(f); 314226035Sgabor return; 315226035Sgabor } 316268966Spfg len = 0; 317268966Spfg line = NULL; 318268966Spfg while ((rlen = getline(&line, &len, f)) != -1) 319280408Spfg add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen); 320268966Spfg free(line); 321210389Sgabor if (ferror(f)) 322210389Sgabor err(2, "%s", fn); 323210389Sgabor fclose(f); 324210389Sgabor} 325210389Sgabor 326210461Sgaborstatic inline const char * 327210461Sgaborinit_color(const char *d) 328210461Sgabor{ 329210461Sgabor char *c; 330210461Sgabor 331210461Sgabor c = getenv("GREP_COLOR"); 332224937Sgabor return (c != NULL && c[0] != '\0' ? c : d); 333210461Sgabor} 334210461Sgabor 335210389Sgaborint 336210389Sgabormain(int argc, char *argv[]) 337210389Sgabor{ 338210389Sgabor char **aargv, **eargv, *eopts; 339226271Sgabor char *ep; 340226271Sgabor const char *pn; 341210389Sgabor unsigned long long l; 342210389Sgabor unsigned int aargc, eargc, i; 343210389Sgabor int c, lastc, needpattern, newarg, prevoptind; 344210389Sgabor 345210389Sgabor setlocale(LC_ALL, ""); 346210389Sgabor 347210389Sgabor#ifndef WITHOUT_NLS 348210389Sgabor catalog = catopen("grep", NL_CAT_LOCALE); 349210389Sgabor#endif 350210389Sgabor 351210389Sgabor /* Check what is the program name of the binary. In this 352210389Sgabor way we can have all the funcionalities in one binary 353210389Sgabor without the need of scripting and using ugly hacks. */ 354226271Sgabor pn = getprogname(); 355226035Sgabor if (pn[0] == 'b' && pn[1] == 'z') { 356226035Sgabor filebehave = FILE_BZIP; 357226035Sgabor pn += 2; 358226035Sgabor } else if (pn[0] == 'x' && pn[1] == 'z') { 359226035Sgabor filebehave = FILE_XZ; 360226035Sgabor pn += 2; 361226035Sgabor } else if (pn[0] == 'l' && pn[1] == 'z') { 362226035Sgabor filebehave = FILE_LZMA; 363226035Sgabor pn += 2; 364226035Sgabor } else if (pn[0] == 'z') { 365226035Sgabor filebehave = FILE_GZIP; 366226035Sgabor pn += 1; 367226035Sgabor } 368226035Sgabor switch (pn[0]) { 369210389Sgabor case 'e': 370210389Sgabor grepbehave = GREP_EXTENDED; 371210389Sgabor break; 372210389Sgabor case 'f': 373210389Sgabor grepbehave = GREP_FIXED; 374210389Sgabor break; 375210389Sgabor } 376210389Sgabor 377210389Sgabor lastc = '\0'; 378210389Sgabor newarg = 1; 379210389Sgabor prevoptind = 1; 380210389Sgabor needpattern = 1; 381210389Sgabor 382210389Sgabor eopts = getenv("GREP_OPTIONS"); 383210389Sgabor 384211364Sgabor /* support for extra arguments in GREP_OPTIONS */ 385211364Sgabor eargc = 0; 386224937Sgabor if (eopts != NULL && eopts[0] != '\0') { 387210389Sgabor char *str; 388210389Sgabor 389211364Sgabor /* make an estimation of how many extra arguments we have */ 390211364Sgabor for (unsigned int j = 0; j < strlen(eopts); j++) 391211364Sgabor if (eopts[j] == ' ') 392210389Sgabor eargc++; 393210389Sgabor 394210389Sgabor eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 395210389Sgabor 396210389Sgabor eargc = 0; 397211364Sgabor /* parse extra arguments */ 398211364Sgabor while ((str = strsep(&eopts, " ")) != NULL) 399224937Sgabor if (str[0] != '\0') 400224937Sgabor eargv[eargc++] = grep_strdup(str); 401210389Sgabor 402210430Sdelphij aargv = (char **)grep_calloc(eargc + argc + 1, 403210430Sdelphij sizeof(char *)); 404211364Sgabor 405210389Sgabor aargv[0] = argv[0]; 406211364Sgabor for (i = 0; i < eargc; i++) 407211364Sgabor aargv[i + 1] = eargv[i]; 408211364Sgabor for (int j = 1; j < argc; j++, i++) 409211364Sgabor aargv[i + 1] = argv[j]; 410210389Sgabor 411211364Sgabor aargc = eargc + argc; 412210389Sgabor } else { 413210389Sgabor aargv = argv; 414210389Sgabor aargc = argc; 415210389Sgabor } 416210389Sgabor 417210389Sgabor while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 418210389Sgabor -1)) { 419210389Sgabor switch (c) { 420210389Sgabor case '0': case '1': case '2': case '3': case '4': 421210389Sgabor case '5': case '6': case '7': case '8': case '9': 422210389Sgabor if (newarg || !isdigit(lastc)) 423210389Sgabor Aflag = 0; 424210389Sgabor else if (Aflag > LLONG_MAX / 10) { 425210389Sgabor errno = ERANGE; 426210389Sgabor err(2, NULL); 427210389Sgabor } 428210389Sgabor Aflag = Bflag = (Aflag * 10) + (c - '0'); 429210389Sgabor break; 430210389Sgabor case 'C': 431210389Sgabor if (optarg == NULL) { 432210389Sgabor Aflag = Bflag = 2; 433210389Sgabor break; 434210389Sgabor } 435210389Sgabor /* FALLTHROUGH */ 436210389Sgabor case 'A': 437210389Sgabor /* FALLTHROUGH */ 438210389Sgabor case 'B': 439210389Sgabor errno = 0; 440210389Sgabor l = strtoull(optarg, &ep, 10); 441210389Sgabor if (((errno == ERANGE) && (l == ULLONG_MAX)) || 442210389Sgabor ((errno == EINVAL) && (l == 0))) 443210389Sgabor err(2, NULL); 444210389Sgabor else if (ep[0] != '\0') { 445210389Sgabor errno = EINVAL; 446210389Sgabor err(2, NULL); 447210389Sgabor } 448210389Sgabor if (c == 'A') 449210389Sgabor Aflag = l; 450210389Sgabor else if (c == 'B') 451210389Sgabor Bflag = l; 452210389Sgabor else 453210389Sgabor Aflag = Bflag = l; 454210389Sgabor break; 455210389Sgabor case 'a': 456210389Sgabor binbehave = BINFILE_TEXT; 457210389Sgabor break; 458210389Sgabor case 'b': 459210389Sgabor bflag = true; 460210389Sgabor break; 461210389Sgabor case 'c': 462210389Sgabor cflag = true; 463210389Sgabor break; 464210389Sgabor case 'D': 465210461Sgabor if (strcasecmp(optarg, "skip") == 0) 466210389Sgabor devbehave = DEV_SKIP; 467210461Sgabor else if (strcasecmp(optarg, "read") == 0) 468210461Sgabor devbehave = DEV_READ; 469210622Sgabor else 470210622Sgabor errx(2, getstr(3), "--devices"); 471210389Sgabor break; 472210389Sgabor case 'd': 473210461Sgabor if (strcasecmp("recurse", optarg) == 0) { 474210389Sgabor Hflag = true; 475210389Sgabor dirbehave = DIR_RECURSE; 476210461Sgabor } else if (strcasecmp("skip", optarg) == 0) 477210389Sgabor dirbehave = DIR_SKIP; 478210461Sgabor else if (strcasecmp("read", optarg) == 0) 479210461Sgabor dirbehave = DIR_READ; 480210622Sgabor else 481210622Sgabor errx(2, getstr(3), "--directories"); 482210389Sgabor break; 483210389Sgabor case 'E': 484210389Sgabor grepbehave = GREP_EXTENDED; 485210389Sgabor break; 486210389Sgabor case 'e': 487245057Sgabor { 488245057Sgabor char *token; 489245688Sgabor char *string = optarg; 490245057Sgabor 491245057Sgabor while ((token = strsep(&string, "\n")) != NULL) 492245057Sgabor add_pattern(token, strlen(token)); 493245057Sgabor } 494210389Sgabor needpattern = 0; 495210389Sgabor break; 496210389Sgabor case 'F': 497210389Sgabor grepbehave = GREP_FIXED; 498210389Sgabor break; 499210389Sgabor case 'f': 500210389Sgabor read_patterns(optarg); 501210389Sgabor needpattern = 0; 502210389Sgabor break; 503210389Sgabor case 'G': 504210389Sgabor grepbehave = GREP_BASIC; 505210389Sgabor break; 506210389Sgabor case 'H': 507210389Sgabor Hflag = true; 508210389Sgabor break; 509210389Sgabor case 'h': 510210389Sgabor Hflag = false; 511210389Sgabor hflag = true; 512210389Sgabor break; 513210389Sgabor case 'I': 514210389Sgabor binbehave = BINFILE_SKIP; 515210389Sgabor break; 516210389Sgabor case 'i': 517210389Sgabor case 'y': 518210389Sgabor iflag = true; 519210389Sgabor cflags |= REG_ICASE; 520210389Sgabor break; 521210389Sgabor case 'J': 522226271Sgabor#ifdef WITHOUT_BZIP2 523226271Sgabor errno = EOPNOTSUPP; 524226271Sgabor err(2, "bzip2 support was disabled at compile-time"); 525226271Sgabor#endif 526210389Sgabor filebehave = FILE_BZIP; 527210389Sgabor break; 528210389Sgabor case 'L': 529210389Sgabor lflag = false; 530210461Sgabor Lflag = true; 531210389Sgabor break; 532210389Sgabor case 'l': 533210389Sgabor Lflag = false; 534210461Sgabor lflag = true; 535210389Sgabor break; 536210389Sgabor case 'm': 537210389Sgabor mflag = true; 538210389Sgabor errno = 0; 539244493Seadler mlimit = mcount = strtoll(optarg, &ep, 10); 540226035Sgabor if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 541210389Sgabor ((errno == EINVAL) && (mcount == 0))) 542210389Sgabor err(2, NULL); 543210389Sgabor else if (ep[0] != '\0') { 544210389Sgabor errno = EINVAL; 545210389Sgabor err(2, NULL); 546210389Sgabor } 547210389Sgabor break; 548226035Sgabor case 'M': 549226035Sgabor filebehave = FILE_LZMA; 550226035Sgabor break; 551210389Sgabor case 'n': 552210389Sgabor nflag = true; 553210389Sgabor break; 554210389Sgabor case 'O': 555210389Sgabor linkbehave = LINK_EXPLICIT; 556210389Sgabor break; 557210389Sgabor case 'o': 558210389Sgabor oflag = true; 559223009Sgabor cflags &= ~REG_NOSUB; 560210389Sgabor break; 561210389Sgabor case 'p': 562210389Sgabor linkbehave = LINK_SKIP; 563210389Sgabor break; 564210389Sgabor case 'q': 565210389Sgabor qflag = true; 566210389Sgabor break; 567210389Sgabor case 'S': 568210461Sgabor linkbehave = LINK_READ; 569210389Sgabor break; 570210389Sgabor case 'R': 571210389Sgabor case 'r': 572210389Sgabor dirbehave = DIR_RECURSE; 573210389Sgabor Hflag = true; 574210389Sgabor break; 575210389Sgabor case 's': 576210389Sgabor sflag = true; 577210389Sgabor break; 578210389Sgabor case 'U': 579210389Sgabor binbehave = BINFILE_BIN; 580210389Sgabor break; 581210389Sgabor case 'u': 582210389Sgabor case MMAP_OPT: 583226035Sgabor filebehave = FILE_MMAP; 584210389Sgabor break; 585210389Sgabor case 'V': 586226271Sgabor printf(getstr(9), getprogname(), VERSION); 587210389Sgabor exit(0); 588210389Sgabor case 'v': 589210389Sgabor vflag = true; 590210389Sgabor break; 591210389Sgabor case 'w': 592210389Sgabor wflag = true; 593223009Sgabor cflags &= ~REG_NOSUB; 594210389Sgabor break; 595210389Sgabor case 'x': 596210389Sgabor xflag = true; 597223009Sgabor cflags &= ~REG_NOSUB; 598210389Sgabor break; 599226035Sgabor case 'X': 600226035Sgabor filebehave = FILE_XZ; 601226035Sgabor break; 602210389Sgabor case 'Z': 603210389Sgabor filebehave = FILE_GZIP; 604210389Sgabor break; 605210389Sgabor case BIN_OPT: 606210461Sgabor if (strcasecmp("binary", optarg) == 0) 607210389Sgabor binbehave = BINFILE_BIN; 608210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 609210389Sgabor binbehave = BINFILE_SKIP; 610210461Sgabor else if (strcasecmp("text", optarg) == 0) 611210389Sgabor binbehave = BINFILE_TEXT; 612210389Sgabor else 613210622Sgabor errx(2, getstr(3), "--binary-files"); 614210389Sgabor break; 615210389Sgabor case COLOR_OPT: 616210461Sgabor color = NULL; 617210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 618210461Sgabor strcasecmp("tty", optarg) == 0 || 619210461Sgabor strcasecmp("if-tty", optarg) == 0) { 620210461Sgabor char *term; 621210461Sgabor 622210461Sgabor term = getenv("TERM"); 623210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 624210461Sgabor strcasecmp(term, "dumb") != 0) 625210461Sgabor color = init_color("01;31"); 626210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 627210461Sgabor strcasecmp("yes", optarg) == 0 || 628210461Sgabor strcasecmp("force", optarg) == 0) { 629210461Sgabor color = init_color("01;31"); 630210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 631210461Sgabor strcasecmp("none", optarg) != 0 && 632210461Sgabor strcasecmp("no", optarg) != 0) 633210622Sgabor errx(2, getstr(3), "--color"); 634223009Sgabor cflags &= ~REG_NOSUB; 635210389Sgabor break; 636210389Sgabor case LABEL_OPT: 637210389Sgabor label = optarg; 638210389Sgabor break; 639210389Sgabor case LINEBUF_OPT: 640210389Sgabor lbflag = true; 641210389Sgabor break; 642210389Sgabor case NULL_OPT: 643210389Sgabor nullflag = true; 644210389Sgabor break; 645210389Sgabor case R_INCLUDE_OPT: 646210578Sgabor finclude = true; 647210578Sgabor add_fpattern(optarg, INCL_PAT); 648210389Sgabor break; 649210389Sgabor case R_EXCLUDE_OPT: 650210578Sgabor fexclude = true; 651210578Sgabor add_fpattern(optarg, EXCL_PAT); 652210389Sgabor break; 653210389Sgabor case R_DINCLUDE_OPT: 654211364Sgabor dinclude = true; 655210578Sgabor add_dpattern(optarg, INCL_PAT); 656210389Sgabor break; 657210389Sgabor case R_DEXCLUDE_OPT: 658211364Sgabor dexclude = true; 659210578Sgabor add_dpattern(optarg, EXCL_PAT); 660210389Sgabor break; 661210389Sgabor case HELP_OPT: 662210389Sgabor default: 663210389Sgabor usage(); 664210389Sgabor } 665210389Sgabor lastc = c; 666210389Sgabor newarg = optind != prevoptind; 667210389Sgabor prevoptind = optind; 668210389Sgabor } 669210389Sgabor aargc -= optind; 670210389Sgabor aargv += optind; 671210389Sgabor 672226035Sgabor /* Empty pattern file matches nothing */ 673226035Sgabor if (!needpattern && (patterns == 0)) 674226035Sgabor exit(1); 675226035Sgabor 676210389Sgabor /* Fail if we don't have any pattern */ 677210389Sgabor if (aargc == 0 && needpattern) 678210389Sgabor usage(); 679210389Sgabor 680210389Sgabor /* Process patterns from command line */ 681210389Sgabor if (aargc != 0 && needpattern) { 682245057Sgabor char *token; 683245688Sgabor char *string = *aargv; 684245057Sgabor 685245057Sgabor while ((token = strsep(&string, "\n")) != NULL) 686245057Sgabor add_pattern(token, strlen(token)); 687210389Sgabor --aargc; 688210389Sgabor ++aargv; 689210389Sgabor } 690210389Sgabor 691210389Sgabor switch (grepbehave) { 692210389Sgabor case GREP_BASIC: 693210389Sgabor break; 694226035Sgabor case GREP_FIXED: 695226035Sgabor /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 696226035Sgabor cflags |= 0020; 697226035Sgabor break; 698210389Sgabor case GREP_EXTENDED: 699210389Sgabor cflags |= REG_EXTENDED; 700210389Sgabor break; 701210389Sgabor default: 702210389Sgabor /* NOTREACHED */ 703210389Sgabor usage(); 704210389Sgabor } 705210389Sgabor 706210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 707210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 708226035Sgabor 709226035Sgabor /* Check if cheating is allowed (always is for fgrep). */ 710226035Sgabor for (i = 0; i < patterns; ++i) { 711226035Sgabor if (fastncomp(&fg_pattern[i], pattern[i].pat, 712226035Sgabor pattern[i].len, cflags) != 0) { 713226035Sgabor /* Fall back to full regex library */ 714226035Sgabor c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 715226035Sgabor if (c != 0) { 716226035Sgabor regerror(c, &r_pattern[i], re_error, 717226035Sgabor RE_ERROR_BUF); 718226035Sgabor errx(2, "%s", re_error); 719210389Sgabor } 720210389Sgabor } 721210389Sgabor } 722210389Sgabor 723210389Sgabor if (lbflag) 724210389Sgabor setlinebuf(stdout); 725210389Sgabor 726210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 727210389Sgabor hflag = true; 728210389Sgabor 729210389Sgabor if (aargc == 0) 730210389Sgabor exit(!procfile("-")); 731210389Sgabor 732210389Sgabor if (dirbehave == DIR_RECURSE) 733210389Sgabor c = grep_tree(aargv); 734211519Sdelphij else 735210578Sgabor for (c = 0; aargc--; ++aargv) { 736210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 737210578Sgabor continue; 738210389Sgabor c+= procfile(*aargv); 739210578Sgabor } 740210389Sgabor 741210389Sgabor#ifndef WITHOUT_NLS 742210389Sgabor catclose(catalog); 743210389Sgabor#endif 744210389Sgabor 745210389Sgabor /* Find out the correct return value according to the 746210389Sgabor results and the command line option. */ 747228319Sgabor exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 748210389Sgabor} 749