grep.c revision 229081
1220422Sgabor/* $NetBSD: grep.c,v 1.4 2011/02/16 01:31:33 joerg Exp $ */ 2220422Sgabor/* $FreeBSD: stable/9/usr.bin/grep/grep.c 229081 2011-12-31 13:12:10Z gabor $ */ 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 229081 2011-12-31 13:12:10Z gabor $"); 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 */ 110210389Sgaborbool nflag; /* -n: show line numbers in front of matching lines */ 111210389Sgaborbool oflag; /* -o: print only matching part */ 112210389Sgaborbool qflag; /* -q: quiet mode (don't output anything) */ 113210389Sgaborbool sflag; /* -s: silent mode (ignore errors) */ 114210389Sgaborbool vflag; /* -v: only show non-matching lines */ 115210389Sgaborbool wflag; /* -w: pattern must start and end on word boundaries */ 116210389Sgaborbool xflag; /* -x: pattern must match entire line */ 117210389Sgaborbool lbflag; /* --line-buffered */ 118210389Sgaborbool nullflag; /* --null */ 119210389Sgaborchar *label; /* --label */ 120210461Sgaborconst char *color; /* --color */ 121210389Sgaborint grepbehave = GREP_BASIC; /* -EFGP: type of the regex */ 122210389Sgaborint binbehave = BINFILE_BIN; /* -aIU: handling of binary files */ 123210389Sgaborint filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */ 124210461Sgaborint devbehave = DEV_READ; /* -D: handling of devices */ 125210461Sgaborint dirbehave = DIR_READ; /* -dRr: handling of directories */ 126210461Sgaborint linkbehave = LINK_READ; /* -OpS: handling of symlinks */ 127210389Sgabor 128211364Sgaborbool dexclude, dinclude; /* --exclude-dir and --include-dir */ 129211364Sgaborbool fexclude, finclude; /* --exclude and --include */ 130210578Sgabor 131210389Sgaborenum { 132210389Sgabor BIN_OPT = CHAR_MAX + 1, 133210389Sgabor COLOR_OPT, 134210389Sgabor HELP_OPT, 135210389Sgabor MMAP_OPT, 136210389Sgabor LINEBUF_OPT, 137210389Sgabor LABEL_OPT, 138210389Sgabor NULL_OPT, 139210389Sgabor R_EXCLUDE_OPT, 140210389Sgabor R_INCLUDE_OPT, 141210389Sgabor R_DEXCLUDE_OPT, 142210389Sgabor R_DINCLUDE_OPT 143210389Sgabor}; 144210389Sgabor 145210461Sgaborstatic inline const char *init_color(const char *); 146210461Sgabor 147210389Sgabor/* Housekeeping */ 148210389Sgaborbool first = true; /* flag whether we are processing the first match */ 149210389Sgaborbool prev; /* flag whether or not the previous line matched */ 150210389Sgaborint tail; /* lines left to print */ 151229081Sgaborbool file_err; /* file reading error */ 152210389Sgabor 153210389Sgabor/* 154210389Sgabor * Prints usage information and returns 2. 155210389Sgabor */ 156210389Sgaborstatic void 157210389Sgaborusage(void) 158210389Sgabor{ 159226573Sgabor fprintf(stderr, getstr(4), getprogname()); 160210389Sgabor fprintf(stderr, "%s", getstr(5)); 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': 481210389Sgabor add_pattern(optarg, strlen(optarg)); 482210389Sgabor needpattern = 0; 483210389Sgabor break; 484210389Sgabor case 'F': 485210389Sgabor grepbehave = GREP_FIXED; 486210389Sgabor break; 487210389Sgabor case 'f': 488210389Sgabor read_patterns(optarg); 489210389Sgabor needpattern = 0; 490210389Sgabor break; 491210389Sgabor case 'G': 492210389Sgabor grepbehave = GREP_BASIC; 493210389Sgabor break; 494210389Sgabor case 'H': 495210389Sgabor Hflag = true; 496210389Sgabor break; 497210389Sgabor case 'h': 498210389Sgabor Hflag = false; 499210389Sgabor hflag = true; 500210389Sgabor break; 501210389Sgabor case 'I': 502210389Sgabor binbehave = BINFILE_SKIP; 503210389Sgabor break; 504210389Sgabor case 'i': 505210389Sgabor case 'y': 506210389Sgabor iflag = true; 507210389Sgabor cflags |= REG_ICASE; 508210389Sgabor break; 509210389Sgabor case 'J': 510226573Sgabor#ifdef WITHOUT_BZIP2 511226573Sgabor errno = EOPNOTSUPP; 512226573Sgabor err(2, "bzip2 support was disabled at compile-time"); 513226573Sgabor#endif 514210389Sgabor filebehave = FILE_BZIP; 515210389Sgabor break; 516210389Sgabor case 'L': 517210389Sgabor lflag = false; 518210461Sgabor Lflag = true; 519210389Sgabor break; 520210389Sgabor case 'l': 521210389Sgabor Lflag = false; 522210461Sgabor lflag = true; 523210389Sgabor break; 524210389Sgabor case 'm': 525210389Sgabor mflag = true; 526210389Sgabor errno = 0; 527226261Sgabor mcount = strtoll(optarg, &ep, 10); 528226261Sgabor if (((errno == ERANGE) && (mcount == LLONG_MAX)) || 529210389Sgabor ((errno == EINVAL) && (mcount == 0))) 530210389Sgabor err(2, NULL); 531210389Sgabor else if (ep[0] != '\0') { 532210389Sgabor errno = EINVAL; 533210389Sgabor err(2, NULL); 534210389Sgabor } 535210389Sgabor break; 536226261Sgabor case 'M': 537226261Sgabor filebehave = FILE_LZMA; 538226261Sgabor break; 539210389Sgabor case 'n': 540210389Sgabor nflag = true; 541210389Sgabor break; 542210389Sgabor case 'O': 543210389Sgabor linkbehave = LINK_EXPLICIT; 544210389Sgabor break; 545210389Sgabor case 'o': 546210389Sgabor oflag = true; 547223009Sgabor cflags &= ~REG_NOSUB; 548210389Sgabor break; 549210389Sgabor case 'p': 550210389Sgabor linkbehave = LINK_SKIP; 551210389Sgabor break; 552210389Sgabor case 'q': 553210389Sgabor qflag = true; 554210389Sgabor break; 555210389Sgabor case 'S': 556210461Sgabor linkbehave = LINK_READ; 557210389Sgabor break; 558210389Sgabor case 'R': 559210389Sgabor case 'r': 560210389Sgabor dirbehave = DIR_RECURSE; 561210389Sgabor Hflag = true; 562210389Sgabor break; 563210389Sgabor case 's': 564210389Sgabor sflag = true; 565210389Sgabor break; 566210389Sgabor case 'U': 567210389Sgabor binbehave = BINFILE_BIN; 568210389Sgabor break; 569210389Sgabor case 'u': 570210389Sgabor case MMAP_OPT: 571226261Sgabor filebehave = FILE_MMAP; 572210389Sgabor break; 573210389Sgabor case 'V': 574226573Sgabor printf(getstr(9), getprogname(), VERSION); 575210389Sgabor exit(0); 576210389Sgabor case 'v': 577210389Sgabor vflag = true; 578210389Sgabor break; 579210389Sgabor case 'w': 580210389Sgabor wflag = true; 581223009Sgabor cflags &= ~REG_NOSUB; 582210389Sgabor break; 583210389Sgabor case 'x': 584210389Sgabor xflag = true; 585223009Sgabor cflags &= ~REG_NOSUB; 586210389Sgabor break; 587226261Sgabor case 'X': 588226261Sgabor filebehave = FILE_XZ; 589226261Sgabor break; 590210389Sgabor case 'Z': 591210389Sgabor filebehave = FILE_GZIP; 592210389Sgabor break; 593210389Sgabor case BIN_OPT: 594210461Sgabor if (strcasecmp("binary", optarg) == 0) 595210389Sgabor binbehave = BINFILE_BIN; 596210461Sgabor else if (strcasecmp("without-match", optarg) == 0) 597210389Sgabor binbehave = BINFILE_SKIP; 598210461Sgabor else if (strcasecmp("text", optarg) == 0) 599210389Sgabor binbehave = BINFILE_TEXT; 600210389Sgabor else 601210622Sgabor errx(2, getstr(3), "--binary-files"); 602210389Sgabor break; 603210389Sgabor case COLOR_OPT: 604210461Sgabor color = NULL; 605210461Sgabor if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 606210461Sgabor strcasecmp("tty", optarg) == 0 || 607210461Sgabor strcasecmp("if-tty", optarg) == 0) { 608210461Sgabor char *term; 609210461Sgabor 610210461Sgabor term = getenv("TERM"); 611210461Sgabor if (isatty(STDOUT_FILENO) && term != NULL && 612210461Sgabor strcasecmp(term, "dumb") != 0) 613210461Sgabor color = init_color("01;31"); 614210461Sgabor } else if (strcasecmp("always", optarg) == 0 || 615210461Sgabor strcasecmp("yes", optarg) == 0 || 616210461Sgabor strcasecmp("force", optarg) == 0) { 617210461Sgabor color = init_color("01;31"); 618210461Sgabor } else if (strcasecmp("never", optarg) != 0 && 619210461Sgabor strcasecmp("none", optarg) != 0 && 620210461Sgabor strcasecmp("no", optarg) != 0) 621210622Sgabor errx(2, getstr(3), "--color"); 622223009Sgabor cflags &= ~REG_NOSUB; 623210389Sgabor break; 624210389Sgabor case LABEL_OPT: 625210389Sgabor label = optarg; 626210389Sgabor break; 627210389Sgabor case LINEBUF_OPT: 628210389Sgabor lbflag = true; 629210389Sgabor break; 630210389Sgabor case NULL_OPT: 631210389Sgabor nullflag = true; 632210389Sgabor break; 633210389Sgabor case R_INCLUDE_OPT: 634210578Sgabor finclude = true; 635210578Sgabor add_fpattern(optarg, INCL_PAT); 636210389Sgabor break; 637210389Sgabor case R_EXCLUDE_OPT: 638210578Sgabor fexclude = true; 639210578Sgabor add_fpattern(optarg, EXCL_PAT); 640210389Sgabor break; 641210389Sgabor case R_DINCLUDE_OPT: 642211364Sgabor dinclude = true; 643210578Sgabor add_dpattern(optarg, INCL_PAT); 644210389Sgabor break; 645210389Sgabor case R_DEXCLUDE_OPT: 646211364Sgabor dexclude = true; 647210578Sgabor add_dpattern(optarg, EXCL_PAT); 648210389Sgabor break; 649210389Sgabor case HELP_OPT: 650210389Sgabor default: 651210389Sgabor usage(); 652210389Sgabor } 653210389Sgabor lastc = c; 654210389Sgabor newarg = optind != prevoptind; 655210389Sgabor prevoptind = optind; 656210389Sgabor } 657210389Sgabor aargc -= optind; 658210389Sgabor aargv += optind; 659210389Sgabor 660226261Sgabor /* Empty pattern file matches nothing */ 661226261Sgabor if (!needpattern && (patterns == 0)) 662226261Sgabor exit(1); 663226261Sgabor 664210389Sgabor /* Fail if we don't have any pattern */ 665210389Sgabor if (aargc == 0 && needpattern) 666210389Sgabor usage(); 667210389Sgabor 668210389Sgabor /* Process patterns from command line */ 669210389Sgabor if (aargc != 0 && needpattern) { 670210389Sgabor add_pattern(*aargv, strlen(*aargv)); 671210389Sgabor --aargc; 672210389Sgabor ++aargv; 673210389Sgabor } 674210389Sgabor 675210389Sgabor switch (grepbehave) { 676210389Sgabor case GREP_BASIC: 677210389Sgabor break; 678226261Sgabor case GREP_FIXED: 679226261Sgabor /* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */ 680226261Sgabor cflags |= 0020; 681226261Sgabor break; 682210389Sgabor case GREP_EXTENDED: 683210389Sgabor cflags |= REG_EXTENDED; 684210389Sgabor break; 685210389Sgabor default: 686210389Sgabor /* NOTREACHED */ 687210389Sgabor usage(); 688210389Sgabor } 689210389Sgabor 690210389Sgabor fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 691210389Sgabor r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 692226261Sgabor 693226261Sgabor /* Check if cheating is allowed (always is for fgrep). */ 694226261Sgabor for (i = 0; i < patterns; ++i) { 695226261Sgabor if (fastncomp(&fg_pattern[i], pattern[i].pat, 696226261Sgabor pattern[i].len, cflags) != 0) { 697226261Sgabor /* Fall back to full regex library */ 698226261Sgabor c = regcomp(&r_pattern[i], pattern[i].pat, cflags); 699226261Sgabor if (c != 0) { 700226261Sgabor regerror(c, &r_pattern[i], re_error, 701226261Sgabor RE_ERROR_BUF); 702226261Sgabor errx(2, "%s", re_error); 703210389Sgabor } 704210389Sgabor } 705210389Sgabor } 706210389Sgabor 707210389Sgabor if (lbflag) 708210389Sgabor setlinebuf(stdout); 709210389Sgabor 710210389Sgabor if ((aargc == 0 || aargc == 1) && !Hflag) 711210389Sgabor hflag = true; 712210389Sgabor 713210389Sgabor if (aargc == 0) 714210389Sgabor exit(!procfile("-")); 715210389Sgabor 716210389Sgabor if (dirbehave == DIR_RECURSE) 717210389Sgabor c = grep_tree(aargv); 718211519Sdelphij else 719210578Sgabor for (c = 0; aargc--; ++aargv) { 720210578Sgabor if ((finclude || fexclude) && !file_matching(*aargv)) 721210578Sgabor continue; 722210389Sgabor c+= procfile(*aargv); 723210578Sgabor } 724210389Sgabor 725210389Sgabor#ifndef WITHOUT_NLS 726210389Sgabor catclose(catalog); 727210389Sgabor#endif 728210389Sgabor 729210389Sgabor /* Find out the correct return value according to the 730210389Sgabor results and the command line option. */ 731229081Sgabor exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 732210389Sgabor} 733