| Jeff Sharkey | 3e8b158 | 2012-07-13 16:37:13 -0700 | [diff] [blame] | 1 | /*	$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $	*/ | 
|  | 2 | /* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/ | 
|  | 3 | /*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/ | 
|  | 4 |  | 
|  | 5 | /*- | 
|  | 6 | * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav | 
|  | 7 | * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org> | 
|  | 8 | * All rights reserved. | 
|  | 9 | * | 
|  | 10 | * Redistribution and use in source and binary forms, with or without | 
|  | 11 | * modification, are permitted provided that the following conditions | 
|  | 12 | * are met: | 
|  | 13 | * 1. Redistributions of source code must retain the above copyright | 
|  | 14 | *    notice, this list of conditions and the following disclaimer. | 
|  | 15 | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | 16 | *    notice, this list of conditions and the following disclaimer in the | 
|  | 17 | *    documentation and/or other materials provided with the distribution. | 
|  | 18 | * | 
|  | 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | 
|  | 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | 22 | * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | 
|  | 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | 29 | * SUCH DAMAGE. | 
|  | 30 | */ | 
|  | 31 |  | 
|  | 32 | #if HAVE_NBTOOL_CONFIG_H | 
|  | 33 | #include "nbtool_config.h" | 
|  | 34 | #endif | 
|  | 35 |  | 
|  | 36 | #include <sys/cdefs.h> | 
|  | 37 | __RCSID("$NetBSD: grep.c,v 1.11 2012/05/06 22:27:00 joerg Exp $"); | 
|  | 38 |  | 
|  | 39 | #include <sys/stat.h> | 
|  | 40 | #include <sys/types.h> | 
|  | 41 |  | 
|  | 42 | #include <ctype.h> | 
|  | 43 | #include <err.h> | 
|  | 44 | #include <errno.h> | 
|  | 45 | #include <getopt.h> | 
|  | 46 | #include <limits.h> | 
|  | 47 | #include <libgen.h> | 
|  | 48 | #include <locale.h> | 
|  | 49 | #include <stdbool.h> | 
|  | 50 | #include <stdio.h> | 
|  | 51 | #include <stdlib.h> | 
|  | 52 | #include <string.h> | 
|  | 53 | #include <unistd.h> | 
|  | 54 |  | 
|  | 55 | #include "grep.h" | 
|  | 56 |  | 
|  | 57 | #ifndef WITHOUT_NLS | 
|  | 58 | #include <nl_types.h> | 
|  | 59 | nl_catd	 catalog; | 
|  | 60 | #endif | 
|  | 61 |  | 
|  | 62 | /* | 
|  | 63 | * Default messags to use when NLS is disabled or no catalogue | 
|  | 64 | * is found. | 
|  | 65 | */ | 
|  | 66 | const char	*errstr[] = { | 
|  | 67 | "", | 
|  | 68 | /* 1*/	"(standard input)", | 
|  | 69 | /* 2*/	"cannot read bzip2 compressed file", | 
|  | 70 | /* 3*/	"unknown %s option", | 
|  | 71 | /* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n", | 
|  | 72 | /* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", | 
|  | 73 | /* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", | 
|  | 74 | /* 7*/	"\t[pattern] [file ...]\n", | 
|  | 75 | /* 8*/	"Binary file %s matches\n", | 
|  | 76 | /* 9*/	"%s (BSD grep) %s\n", | 
|  | 77 | }; | 
|  | 78 |  | 
|  | 79 | /* Flags passed to regcomp() and regexec() */ | 
|  | 80 | int		 cflags = 0; | 
|  | 81 | int		 eflags = REG_STARTEND; | 
|  | 82 |  | 
|  | 83 | /* Searching patterns */ | 
|  | 84 | unsigned int	 patterns, pattern_sz; | 
|  | 85 | char		**pattern; | 
|  | 86 | regex_t		*r_pattern; | 
|  | 87 | fastgrep_t	*fg_pattern; | 
|  | 88 |  | 
|  | 89 | /* Filename exclusion/inclusion patterns */ | 
|  | 90 | unsigned int	 fpatterns, fpattern_sz; | 
|  | 91 | unsigned int	 dpatterns, dpattern_sz; | 
|  | 92 | struct epat	*dpattern, *fpattern; | 
|  | 93 |  | 
|  | 94 | /* For regex errors  */ | 
|  | 95 | char	 re_error[RE_ERROR_BUF + 1]; | 
|  | 96 |  | 
|  | 97 | /* Command-line flags */ | 
|  | 98 | unsigned long long Aflag;	/* -A x: print x lines trailing each match */ | 
|  | 99 | unsigned long long Bflag;	/* -B x: print x lines leading each match */ | 
|  | 100 | bool	 Hflag;		/* -H: always print file name */ | 
|  | 101 | bool	 Lflag;		/* -L: only show names of files with no matches */ | 
|  | 102 | bool	 bflag;		/* -b: show block numbers for each match */ | 
|  | 103 | bool	 cflag;		/* -c: only show a count of matching lines */ | 
|  | 104 | bool	 hflag;		/* -h: don't print filename headers */ | 
|  | 105 | bool	 iflag;		/* -i: ignore case */ | 
|  | 106 | bool	 lflag;		/* -l: only show names of files with matches */ | 
|  | 107 | bool	 mflag;		/* -m x: stop reading the files after x matches */ | 
|  | 108 | unsigned long long mcount;	/* count for -m */ | 
|  | 109 | bool	 nflag;		/* -n: show line numbers in front of matching lines */ | 
|  | 110 | bool	 oflag;		/* -o: print only matching part */ | 
|  | 111 | bool	 qflag;		/* -q: quiet mode (don't output anything) */ | 
|  | 112 | bool	 sflag;		/* -s: silent mode (ignore errors) */ | 
|  | 113 | bool	 vflag;		/* -v: only show non-matching lines */ | 
|  | 114 | bool	 wflag;		/* -w: pattern must start and end on word boundaries */ | 
|  | 115 | bool	 xflag;		/* -x: pattern must match entire line */ | 
|  | 116 | bool	 lbflag;	/* --line-buffered */ | 
|  | 117 | bool	 nullflag;	/* --null */ | 
|  | 118 | bool	 nulldataflag;	/* --null-data */ | 
|  | 119 | unsigned char line_sep = '\n';	/* 0 for --null-data */ | 
|  | 120 | char	*label;		/* --label */ | 
|  | 121 | const char *color;	/* --color */ | 
|  | 122 | int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */ | 
|  | 123 | int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */ | 
|  | 124 | int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */ | 
|  | 125 | int	 devbehave = DEV_READ;		/* -D: handling of devices */ | 
|  | 126 | int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */ | 
|  | 127 | int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */ | 
|  | 128 |  | 
|  | 129 | bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */ | 
|  | 130 | bool	 fexclude, finclude;	/* --exclude and --include */ | 
|  | 131 |  | 
|  | 132 | enum { | 
|  | 133 | BIN_OPT = CHAR_MAX + 1, | 
|  | 134 | COLOR_OPT, | 
|  | 135 | DECOMPRESS_OPT, | 
|  | 136 | HELP_OPT, | 
|  | 137 | MMAP_OPT, | 
|  | 138 | LINEBUF_OPT, | 
|  | 139 | LABEL_OPT, | 
|  | 140 | R_EXCLUDE_OPT, | 
|  | 141 | R_INCLUDE_OPT, | 
|  | 142 | R_DEXCLUDE_OPT, | 
|  | 143 | R_DINCLUDE_OPT | 
|  | 144 | }; | 
|  | 145 |  | 
|  | 146 | static inline const char	*init_color(const char *); | 
|  | 147 |  | 
|  | 148 | /* Housekeeping */ | 
|  | 149 | int	 tail;		/* lines left to print */ | 
|  | 150 | bool	 notfound;	/* file not found */ | 
|  | 151 |  | 
|  | 152 | extern char	*__progname; | 
|  | 153 |  | 
|  | 154 | /* | 
|  | 155 | * Prints usage information and returns 2. | 
|  | 156 | */ | 
|  | 157 | __dead static void | 
|  | 158 | usage(void) | 
|  | 159 | { | 
|  | 160 | fprintf(stderr, getstr(4), __progname); | 
|  | 161 | fprintf(stderr, "%s", getstr(5)); | 
|  | 162 | fprintf(stderr, "%s", getstr(5)); | 
|  | 163 | fprintf(stderr, "%s", getstr(6)); | 
|  | 164 | fprintf(stderr, "%s", getstr(7)); | 
|  | 165 | exit(2); | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | static const char optstr[] = | 
|  | 169 | "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz"; | 
|  | 170 |  | 
|  | 171 | struct option long_options[] = | 
|  | 172 | { | 
|  | 173 | {"binary-files",	required_argument,	NULL, BIN_OPT}, | 
|  | 174 | {"decompress",          no_argument,            NULL, DECOMPRESS_OPT}, | 
|  | 175 | {"help",		no_argument,		NULL, HELP_OPT}, | 
|  | 176 | {"mmap",		no_argument,		NULL, MMAP_OPT}, | 
|  | 177 | {"line-buffered",	no_argument,		NULL, LINEBUF_OPT}, | 
|  | 178 | {"label",		required_argument,	NULL, LABEL_OPT}, | 
|  | 179 | {"color",		optional_argument,	NULL, COLOR_OPT}, | 
|  | 180 | {"colour",		optional_argument,	NULL, COLOR_OPT}, | 
|  | 181 | {"exclude",		required_argument,	NULL, R_EXCLUDE_OPT}, | 
|  | 182 | {"include",		required_argument,	NULL, R_INCLUDE_OPT}, | 
|  | 183 | {"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT}, | 
|  | 184 | {"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT}, | 
|  | 185 | {"after-context",	required_argument,	NULL, 'A'}, | 
|  | 186 | {"text",		no_argument,		NULL, 'a'}, | 
|  | 187 | {"before-context",	required_argument,	NULL, 'B'}, | 
|  | 188 | {"byte-offset",		no_argument,		NULL, 'b'}, | 
|  | 189 | {"context",		optional_argument,	NULL, 'C'}, | 
|  | 190 | {"count",		no_argument,		NULL, 'c'}, | 
|  | 191 | {"devices",		required_argument,	NULL, 'D'}, | 
|  | 192 | {"directories",		required_argument,	NULL, 'd'}, | 
|  | 193 | {"extended-regexp",	no_argument,		NULL, 'E'}, | 
|  | 194 | {"regexp",		required_argument,	NULL, 'e'}, | 
|  | 195 | {"fixed-strings",	no_argument,		NULL, 'F'}, | 
|  | 196 | {"file",		required_argument,	NULL, 'f'}, | 
|  | 197 | {"basic-regexp",	no_argument,		NULL, 'G'}, | 
|  | 198 | {"no-filename",		no_argument,		NULL, 'h'}, | 
|  | 199 | {"with-filename",	no_argument,		NULL, 'H'}, | 
|  | 200 | {"ignore-case",		no_argument,		NULL, 'i'}, | 
|  | 201 | {"bz2decompress",	no_argument,		NULL, 'J'}, | 
|  | 202 | {"files-with-matches",	no_argument,		NULL, 'l'}, | 
|  | 203 | {"files-without-match", no_argument,            NULL, 'L'}, | 
|  | 204 | {"max-count",		required_argument,	NULL, 'm'}, | 
|  | 205 | {"line-number",		no_argument,		NULL, 'n'}, | 
|  | 206 | {"only-matching",	no_argument,		NULL, 'o'}, | 
|  | 207 | {"quiet",		no_argument,		NULL, 'q'}, | 
|  | 208 | {"silent",		no_argument,		NULL, 'q'}, | 
|  | 209 | {"recursive",		no_argument,		NULL, 'r'}, | 
|  | 210 | {"no-messages",		no_argument,		NULL, 's'}, | 
|  | 211 | {"binary",		no_argument,		NULL, 'U'}, | 
|  | 212 | {"unix-byte-offsets",	no_argument,		NULL, 'u'}, | 
|  | 213 | {"invert-match",	no_argument,		NULL, 'v'}, | 
|  | 214 | {"version",		no_argument,		NULL, 'V'}, | 
|  | 215 | {"word-regexp",		no_argument,		NULL, 'w'}, | 
|  | 216 | {"line-regexp",		no_argument,		NULL, 'x'}, | 
|  | 217 | {"null",		no_argument,		NULL, 'Z'}, | 
|  | 218 | {"null-data",		no_argument,		NULL, 'z'}, | 
|  | 219 | {NULL,			no_argument,		NULL, 0} | 
|  | 220 | }; | 
|  | 221 |  | 
|  | 222 | /* | 
|  | 223 | * Adds a searching pattern to the internal array. | 
|  | 224 | */ | 
|  | 225 | static void | 
|  | 226 | add_pattern(char *pat, size_t len) | 
|  | 227 | { | 
|  | 228 |  | 
|  | 229 | /* TODO: Check for empty patterns and shortcut */ | 
|  | 230 |  | 
|  | 231 | /* Increase size if necessary */ | 
|  | 232 | if (patterns == pattern_sz) { | 
|  | 233 | pattern_sz *= 2; | 
|  | 234 | pattern = grep_realloc(pattern, ++pattern_sz * | 
|  | 235 | sizeof(*pattern)); | 
|  | 236 | } | 
|  | 237 | if (len > 0 && pat[len - 1] == '\n') | 
|  | 238 | --len; | 
|  | 239 | /* pat may not be NUL-terminated */ | 
|  | 240 | pattern[patterns] = grep_malloc(len + 1); | 
|  | 241 | memcpy(pattern[patterns], pat, len); | 
|  | 242 | pattern[patterns][len] = '\0'; | 
|  | 243 | ++patterns; | 
|  | 244 | } | 
|  | 245 |  | 
|  | 246 | /* | 
|  | 247 | * Adds a file include/exclude pattern to the internal array. | 
|  | 248 | */ | 
|  | 249 | static void | 
|  | 250 | add_fpattern(const char *pat, int mode) | 
|  | 251 | { | 
|  | 252 |  | 
|  | 253 | /* Increase size if necessary */ | 
|  | 254 | if (fpatterns == fpattern_sz) { | 
|  | 255 | fpattern_sz *= 2; | 
|  | 256 | fpattern = grep_realloc(fpattern, ++fpattern_sz * | 
|  | 257 | sizeof(struct epat)); | 
|  | 258 | } | 
|  | 259 | fpattern[fpatterns].pat = grep_strdup(pat); | 
|  | 260 | fpattern[fpatterns].mode = mode; | 
|  | 261 | ++fpatterns; | 
|  | 262 | } | 
|  | 263 |  | 
|  | 264 | /* | 
|  | 265 | * Adds a directory include/exclude pattern to the internal array. | 
|  | 266 | */ | 
|  | 267 | static void | 
|  | 268 | add_dpattern(const char *pat, int mode) | 
|  | 269 | { | 
|  | 270 |  | 
|  | 271 | /* Increase size if necessary */ | 
|  | 272 | if (dpatterns == dpattern_sz) { | 
|  | 273 | dpattern_sz *= 2; | 
|  | 274 | dpattern = grep_realloc(dpattern, ++dpattern_sz * | 
|  | 275 | sizeof(struct epat)); | 
|  | 276 | } | 
|  | 277 | dpattern[dpatterns].pat = grep_strdup(pat); | 
|  | 278 | dpattern[dpatterns].mode = mode; | 
|  | 279 | ++dpatterns; | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | /* | 
|  | 283 | * Reads searching patterns from a file and adds them with add_pattern(). | 
|  | 284 | */ | 
|  | 285 | static void | 
|  | 286 | read_patterns(const char *fn) | 
|  | 287 | { | 
|  | 288 | FILE *f; | 
|  | 289 | char *line; | 
|  | 290 | size_t len; | 
|  | 291 | ssize_t rlen; | 
|  | 292 |  | 
|  | 293 | if ((f = fopen(fn, "r")) == NULL) | 
|  | 294 | err(2, "%s", fn); | 
|  | 295 | line = NULL; | 
|  | 296 | len = 0; | 
|  | 297 | #ifndef ANDROID | 
|  | 298 | while ((rlen = getline(&line, &len, f)) != -1) | 
|  | 299 | add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); | 
|  | 300 | #endif | 
|  | 301 | free(line); | 
|  | 302 | if (ferror(f)) | 
|  | 303 | err(2, "%s", fn); | 
|  | 304 | fclose(f); | 
|  | 305 | } | 
|  | 306 |  | 
|  | 307 | static inline const char * | 
|  | 308 | init_color(const char *d) | 
|  | 309 | { | 
|  | 310 | char *c; | 
|  | 311 |  | 
|  | 312 | c = getenv("GREP_COLOR"); | 
|  | 313 | return (c != NULL ? c : d); | 
|  | 314 | } | 
|  | 315 |  | 
|  | 316 | int | 
|  | 317 | grep_main(int argc, char *argv[]) | 
|  | 318 | { | 
|  | 319 | char **aargv, **eargv, *eopts; | 
|  | 320 | char *ep; | 
|  | 321 | unsigned long long l; | 
|  | 322 | unsigned int aargc, eargc, i, j; | 
|  | 323 | int c, lastc, needpattern, newarg, prevoptind; | 
|  | 324 |  | 
|  | 325 | setlocale(LC_ALL, ""); | 
|  | 326 |  | 
|  | 327 | #ifndef WITHOUT_NLS | 
|  | 328 | catalog = catopen("grep", NL_CAT_LOCALE); | 
|  | 329 | #endif | 
|  | 330 |  | 
|  | 331 | /* Check what is the program name of the binary.  In this | 
|  | 332 | way we can have all the funcionalities in one binary | 
|  | 333 | without the need of scripting and using ugly hacks. */ | 
|  | 334 | switch (__progname[0]) { | 
|  | 335 | case 'e': | 
|  | 336 | grepbehave = GREP_EXTENDED; | 
|  | 337 | break; | 
|  | 338 | case 'f': | 
|  | 339 | grepbehave = GREP_FIXED; | 
|  | 340 | break; | 
|  | 341 | case 'g': | 
|  | 342 | grepbehave = GREP_BASIC; | 
|  | 343 | break; | 
|  | 344 | case 'z': | 
|  | 345 | filebehave = FILE_GZIP; | 
|  | 346 | switch(__progname[1]) { | 
|  | 347 | case 'e': | 
|  | 348 | grepbehave = GREP_EXTENDED; | 
|  | 349 | break; | 
|  | 350 | case 'f': | 
|  | 351 | grepbehave = GREP_FIXED; | 
|  | 352 | break; | 
|  | 353 | case 'g': | 
|  | 354 | grepbehave = GREP_BASIC; | 
|  | 355 | break; | 
|  | 356 | } | 
|  | 357 | break; | 
|  | 358 | } | 
|  | 359 |  | 
|  | 360 | lastc = '\0'; | 
|  | 361 | newarg = 1; | 
|  | 362 | prevoptind = 1; | 
|  | 363 | needpattern = 1; | 
|  | 364 |  | 
|  | 365 | eopts = getenv("GREP_OPTIONS"); | 
|  | 366 |  | 
|  | 367 | /* support for extra arguments in GREP_OPTIONS */ | 
|  | 368 | eargc = 0; | 
|  | 369 | if (eopts != NULL) { | 
|  | 370 | char *str; | 
|  | 371 |  | 
|  | 372 | /* make an estimation of how many extra arguments we have */ | 
|  | 373 | for (j = 0; j < strlen(eopts); j++) | 
|  | 374 | if (eopts[j] == ' ') | 
|  | 375 | eargc++; | 
|  | 376 |  | 
|  | 377 | eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); | 
|  | 378 |  | 
|  | 379 | eargc = 0; | 
|  | 380 | /* parse extra arguments */ | 
|  | 381 | while ((str = strsep(&eopts, " ")) != NULL) | 
|  | 382 | eargv[eargc++] = grep_strdup(str); | 
|  | 383 |  | 
|  | 384 | aargv = (char **)grep_calloc(eargc + argc + 1, | 
|  | 385 | sizeof(char *)); | 
|  | 386 |  | 
|  | 387 | aargv[0] = argv[0]; | 
|  | 388 | for (i = 0; i < eargc; i++) | 
|  | 389 | aargv[i + 1] = eargv[i]; | 
|  | 390 | for (j = 1; j < (unsigned int)argc; j++, i++) | 
|  | 391 | aargv[i + 1] = argv[j]; | 
|  | 392 |  | 
|  | 393 | aargc = eargc + argc; | 
|  | 394 | } else { | 
|  | 395 | aargv = argv; | 
|  | 396 | aargc = argc; | 
|  | 397 | } | 
|  | 398 |  | 
|  | 399 | while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != | 
|  | 400 | -1)) { | 
|  | 401 | switch (c) { | 
|  | 402 | case '0': case '1': case '2': case '3': case '4': | 
|  | 403 | case '5': case '6': case '7': case '8': case '9': | 
|  | 404 | if (newarg || !isdigit(lastc)) | 
|  | 405 | Aflag = 0; | 
|  | 406 | else if (Aflag > LLONG_MAX / 10) { | 
|  | 407 | errno = ERANGE; | 
|  | 408 | err(2, NULL); | 
|  | 409 | } | 
|  | 410 | Aflag = Bflag = (Aflag * 10) + (c - '0'); | 
|  | 411 | break; | 
|  | 412 | case 'C': | 
|  | 413 | if (optarg == NULL) { | 
|  | 414 | Aflag = Bflag = 2; | 
|  | 415 | break; | 
|  | 416 | } | 
|  | 417 | /* FALLTHROUGH */ | 
|  | 418 | case 'A': | 
|  | 419 | /* FALLTHROUGH */ | 
|  | 420 | case 'B': | 
|  | 421 | errno = 0; | 
|  | 422 | l = strtoull(optarg, &ep, 10); | 
|  | 423 | if (((errno == ERANGE) && (l == ULLONG_MAX)) || | 
|  | 424 | ((errno == EINVAL) && (l == 0))) | 
|  | 425 | err(2, NULL); | 
|  | 426 | else if (ep[0] != '\0') { | 
|  | 427 | errno = EINVAL; | 
|  | 428 | err(2, NULL); | 
|  | 429 | } | 
|  | 430 | if (c == 'A') | 
|  | 431 | Aflag = l; | 
|  | 432 | else if (c == 'B') | 
|  | 433 | Bflag = l; | 
|  | 434 | else | 
|  | 435 | Aflag = Bflag = l; | 
|  | 436 | break; | 
|  | 437 | case 'a': | 
|  | 438 | binbehave = BINFILE_TEXT; | 
|  | 439 | break; | 
|  | 440 | case 'b': | 
|  | 441 | bflag = true; | 
|  | 442 | break; | 
|  | 443 | case 'c': | 
|  | 444 | cflag = true; | 
|  | 445 | break; | 
|  | 446 | case 'D': | 
|  | 447 | if (strcasecmp(optarg, "skip") == 0) | 
|  | 448 | devbehave = DEV_SKIP; | 
|  | 449 | else if (strcasecmp(optarg, "read") == 0) | 
|  | 450 | devbehave = DEV_READ; | 
|  | 451 | else | 
|  | 452 | errx(2, getstr(3), "--devices"); | 
|  | 453 | break; | 
|  | 454 | case 'd': | 
|  | 455 | if (strcasecmp("recurse", optarg) == 0) { | 
|  | 456 | Hflag = true; | 
|  | 457 | dirbehave = DIR_RECURSE; | 
|  | 458 | } else if (strcasecmp("skip", optarg) == 0) | 
|  | 459 | dirbehave = DIR_SKIP; | 
|  | 460 | else if (strcasecmp("read", optarg) == 0) | 
|  | 461 | dirbehave = DIR_READ; | 
|  | 462 | else | 
|  | 463 | errx(2, getstr(3), "--directories"); | 
|  | 464 | break; | 
|  | 465 | case 'E': | 
|  | 466 | grepbehave = GREP_EXTENDED; | 
|  | 467 | break; | 
|  | 468 | case 'e': | 
|  | 469 | add_pattern(optarg, strlen(optarg)); | 
|  | 470 | needpattern = 0; | 
|  | 471 | break; | 
|  | 472 | case 'F': | 
|  | 473 | grepbehave = GREP_FIXED; | 
|  | 474 | break; | 
|  | 475 | case 'f': | 
|  | 476 | read_patterns(optarg); | 
|  | 477 | needpattern = 0; | 
|  | 478 | break; | 
|  | 479 | case 'G': | 
|  | 480 | grepbehave = GREP_BASIC; | 
|  | 481 | break; | 
|  | 482 | case 'H': | 
|  | 483 | Hflag = true; | 
|  | 484 | break; | 
|  | 485 | case 'h': | 
|  | 486 | Hflag = false; | 
|  | 487 | hflag = true; | 
|  | 488 | break; | 
|  | 489 | case 'I': | 
|  | 490 | binbehave = BINFILE_SKIP; | 
|  | 491 | break; | 
|  | 492 | case 'i': | 
|  | 493 | case 'y': | 
|  | 494 | iflag =  true; | 
|  | 495 | cflags |= REG_ICASE; | 
|  | 496 | break; | 
|  | 497 | case 'J': | 
|  | 498 | filebehave = FILE_BZIP; | 
|  | 499 | break; | 
|  | 500 | case 'L': | 
|  | 501 | lflag = false; | 
|  | 502 | Lflag = true; | 
|  | 503 | break; | 
|  | 504 | case 'l': | 
|  | 505 | Lflag = false; | 
|  | 506 | lflag = true; | 
|  | 507 | break; | 
|  | 508 | case 'm': | 
|  | 509 | mflag = true; | 
|  | 510 | errno = 0; | 
|  | 511 | mcount = strtoull(optarg, &ep, 10); | 
|  | 512 | if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || | 
|  | 513 | ((errno == EINVAL) && (mcount == 0))) | 
|  | 514 | err(2, NULL); | 
|  | 515 | else if (ep[0] != '\0') { | 
|  | 516 | errno = EINVAL; | 
|  | 517 | err(2, NULL); | 
|  | 518 | } | 
|  | 519 | break; | 
|  | 520 | case 'n': | 
|  | 521 | nflag = true; | 
|  | 522 | break; | 
|  | 523 | case 'O': | 
|  | 524 | linkbehave = LINK_EXPLICIT; | 
|  | 525 | break; | 
|  | 526 | case 'o': | 
|  | 527 | oflag = true; | 
|  | 528 | break; | 
|  | 529 | case 'p': | 
|  | 530 | linkbehave = LINK_SKIP; | 
|  | 531 | break; | 
|  | 532 | case 'q': | 
|  | 533 | qflag = true; | 
|  | 534 | break; | 
|  | 535 | case 'S': | 
|  | 536 | linkbehave = LINK_READ; | 
|  | 537 | break; | 
|  | 538 | case 'R': | 
|  | 539 | case 'r': | 
|  | 540 | dirbehave = DIR_RECURSE; | 
|  | 541 | Hflag = true; | 
|  | 542 | break; | 
|  | 543 | case 's': | 
|  | 544 | sflag = true; | 
|  | 545 | break; | 
|  | 546 | case 'U': | 
|  | 547 | binbehave = BINFILE_BIN; | 
|  | 548 | break; | 
|  | 549 | case 'u': | 
|  | 550 | case MMAP_OPT: | 
|  | 551 | /* noop, compatibility */ | 
|  | 552 | break; | 
|  | 553 | case 'V': | 
|  | 554 | printf(getstr(9), __progname, VERSION); | 
|  | 555 | exit(0); | 
|  | 556 | case 'v': | 
|  | 557 | vflag = true; | 
|  | 558 | break; | 
|  | 559 | case 'w': | 
|  | 560 | wflag = true; | 
|  | 561 | break; | 
|  | 562 | case 'x': | 
|  | 563 | xflag = true; | 
|  | 564 | break; | 
|  | 565 | case 'Z': | 
|  | 566 | nullflag = true; | 
|  | 567 | break; | 
|  | 568 | case 'z': | 
|  | 569 | nulldataflag = true; | 
|  | 570 | line_sep = '\0'; | 
|  | 571 | break; | 
|  | 572 | case BIN_OPT: | 
|  | 573 | if (strcasecmp("binary", optarg) == 0) | 
|  | 574 | binbehave = BINFILE_BIN; | 
|  | 575 | else if (strcasecmp("without-match", optarg) == 0) | 
|  | 576 | binbehave = BINFILE_SKIP; | 
|  | 577 | else if (strcasecmp("text", optarg) == 0) | 
|  | 578 | binbehave = BINFILE_TEXT; | 
|  | 579 | else | 
|  | 580 | errx(2, getstr(3), "--binary-files"); | 
|  | 581 | break; | 
|  | 582 | case COLOR_OPT: | 
|  | 583 | color = NULL; | 
|  | 584 | if (optarg == NULL || strcasecmp("auto", optarg) == 0 || | 
|  | 585 | strcasecmp("tty", optarg) == 0 || | 
|  | 586 | strcasecmp("if-tty", optarg) == 0) { | 
|  | 587 | char *term; | 
|  | 588 |  | 
|  | 589 | term = getenv("TERM"); | 
|  | 590 | if (isatty(STDOUT_FILENO) && term != NULL && | 
|  | 591 | strcasecmp(term, "dumb") != 0) | 
|  | 592 | color = init_color("01;31"); | 
|  | 593 | } else if (strcasecmp("always", optarg) == 0 || | 
|  | 594 | strcasecmp("yes", optarg) == 0 || | 
|  | 595 | strcasecmp("force", optarg) == 0) { | 
|  | 596 | color = init_color("01;31"); | 
|  | 597 | } else if (strcasecmp("never", optarg) != 0 && | 
|  | 598 | strcasecmp("none", optarg) != 0 && | 
|  | 599 | strcasecmp("no", optarg) != 0) | 
|  | 600 | errx(2, getstr(3), "--color"); | 
|  | 601 | break; | 
|  | 602 | case DECOMPRESS_OPT: | 
|  | 603 | filebehave = FILE_GZIP; | 
|  | 604 | break; | 
|  | 605 | case LABEL_OPT: | 
|  | 606 | label = optarg; | 
|  | 607 | break; | 
|  | 608 | case LINEBUF_OPT: | 
|  | 609 | lbflag = true; | 
|  | 610 | break; | 
|  | 611 | case R_INCLUDE_OPT: | 
|  | 612 | finclude = true; | 
|  | 613 | add_fpattern(optarg, INCL_PAT); | 
|  | 614 | break; | 
|  | 615 | case R_EXCLUDE_OPT: | 
|  | 616 | fexclude = true; | 
|  | 617 | add_fpattern(optarg, EXCL_PAT); | 
|  | 618 | break; | 
|  | 619 | case R_DINCLUDE_OPT: | 
|  | 620 | dinclude = true; | 
|  | 621 | add_dpattern(optarg, INCL_PAT); | 
|  | 622 | break; | 
|  | 623 | case R_DEXCLUDE_OPT: | 
|  | 624 | dexclude = true; | 
|  | 625 | add_dpattern(optarg, EXCL_PAT); | 
|  | 626 | break; | 
|  | 627 | case HELP_OPT: | 
|  | 628 | default: | 
|  | 629 | usage(); | 
|  | 630 | } | 
|  | 631 | lastc = c; | 
|  | 632 | newarg = optind != prevoptind; | 
|  | 633 | prevoptind = optind; | 
|  | 634 | } | 
|  | 635 | aargc -= optind; | 
|  | 636 | aargv += optind; | 
|  | 637 |  | 
|  | 638 | /* Fail if we don't have any pattern */ | 
|  | 639 | if (aargc == 0 && needpattern) | 
|  | 640 | usage(); | 
|  | 641 |  | 
|  | 642 | /* Process patterns from command line */ | 
|  | 643 | if (aargc != 0 && needpattern) { | 
|  | 644 | add_pattern(*aargv, strlen(*aargv)); | 
|  | 645 | --aargc; | 
|  | 646 | ++aargv; | 
|  | 647 | } | 
|  | 648 |  | 
|  | 649 | switch (grepbehave) { | 
|  | 650 | case GREP_FIXED: | 
|  | 651 | case GREP_BASIC: | 
|  | 652 | break; | 
|  | 653 | case GREP_EXTENDED: | 
|  | 654 | cflags |= REG_EXTENDED; | 
|  | 655 | break; | 
|  | 656 | default: | 
|  | 657 | /* NOTREACHED */ | 
|  | 658 | usage(); | 
|  | 659 | } | 
|  | 660 |  | 
|  | 661 | fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); | 
|  | 662 | r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); | 
|  | 663 | /* | 
|  | 664 | * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. | 
|  | 665 | * Optimizations should be done there. | 
|  | 666 | */ | 
|  | 667 | /* Check if cheating is allowed (always is for fgrep). */ | 
|  | 668 | if (grepbehave == GREP_FIXED) { | 
|  | 669 | for (i = 0; i < patterns; ++i) | 
|  | 670 | fgrepcomp(&fg_pattern[i], pattern[i]); | 
|  | 671 | } else { | 
|  | 672 | for (i = 0; i < patterns; ++i) { | 
|  | 673 | if (fastcomp(&fg_pattern[i], pattern[i])) { | 
|  | 674 | /* Fall back to full regex library */ | 
|  | 675 | c = regcomp(&r_pattern[i], pattern[i], cflags); | 
|  | 676 | if (c != 0) { | 
|  | 677 | regerror(c, &r_pattern[i], re_error, | 
|  | 678 | RE_ERROR_BUF); | 
|  | 679 | errx(2, "%s", re_error); | 
|  | 680 | } | 
|  | 681 | } | 
|  | 682 | } | 
|  | 683 | } | 
|  | 684 |  | 
|  | 685 | if (lbflag) | 
|  | 686 | setlinebuf(stdout); | 
|  | 687 |  | 
|  | 688 | if ((aargc == 0 || aargc == 1) && !Hflag) | 
|  | 689 | hflag = true; | 
|  | 690 |  | 
|  | 691 | if (aargc == 0) | 
|  | 692 | exit(!procfile("-")); | 
|  | 693 |  | 
|  | 694 | if (dirbehave == DIR_RECURSE) | 
|  | 695 | c = grep_tree(aargv); | 
|  | 696 | else | 
|  | 697 | for (c = 0; aargc--; ++aargv) { | 
|  | 698 | if ((finclude || fexclude) && !file_matching(*aargv)) | 
|  | 699 | continue; | 
|  | 700 | c+= procfile(*aargv); | 
|  | 701 | } | 
|  | 702 |  | 
|  | 703 | #ifndef WITHOUT_NLS | 
|  | 704 | catclose(catalog); | 
|  | 705 | #endif | 
|  | 706 |  | 
|  | 707 | /* Find out the correct return value according to the | 
|  | 708 | results and the command line option. */ | 
|  | 709 | exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); | 
|  | 710 | } |