1 /* $OpenBSD: grep.c,v 1.55 2015/11/28 01:17:12 gsoares Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 #include <sys/queue.h> 32 33 #include <ctype.h> 34 #include <err.h> 35 #include <errno.h> 36 #include <getopt.h> 37 #include <regex.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include "grep.h" 44 45 /* Flags passed to regcomp() and regexec() */ 46 int cflags; 47 int eflags = REG_STARTEND; 48 49 int matchall; /* shortcut */ 50 int patterns, pattern_sz; 51 char **pattern; 52 regex_t *r_pattern; 53 fastgrep_t *fg_pattern; 54 55 /* For regex errors */ 56 char re_error[RE_ERROR_BUF + 1]; 57 58 /* Command-line flags */ 59 int Aflag; /* -A x: print x lines trailing each match */ 60 int Bflag; /* -B x: print x lines leading each match */ 61 int Eflag; /* -E: interpret pattern as extended regexp */ 62 int Fflag; /* -F: interpret pattern as list of fixed strings */ 63 int Hflag; /* -H: always print filename header */ 64 int Lflag; /* -L: only show names of files with no matches */ 65 int Rflag; /* -R: recursively search directory trees */ 66 #ifndef NOZ 67 int Zflag; /* -Z: decompress input before processing */ 68 #endif 69 int bflag; /* -b: show block numbers for each match */ 70 int cflag; /* -c: only show a count of matching lines */ 71 int hflag; /* -h: don't print filename headers */ 72 int iflag; /* -i: ignore case */ 73 int lflag; /* -l: only show names of files with matches */ 74 int nflag; /* -n: show line numbers in front of matching lines */ 75 int oflag; /* -o: print each match */ 76 int qflag; /* -q: quiet mode (don't output anything) */ 77 int sflag; /* -s: silent mode (ignore errors) */ 78 int vflag; /* -v: only show non-matching lines */ 79 int wflag; /* -w: pattern must start and end on word boundaries */ 80 int xflag; /* -x: pattern must match entire line */ 81 int lbflag; /* --line-buffered */ 82 83 int binbehave = BIN_FILE_BIN; 84 85 enum { 86 BIN_OPT = CHAR_MAX + 1, 87 HELP_OPT, 88 MMAP_OPT, 89 LINEBUF_OPT 90 }; 91 92 /* Housekeeping */ 93 int first; /* flag whether or not this is our first match */ 94 int tail; /* lines left to print */ 95 int file_err; /* file reading error */ 96 97 struct patfile { 98 const char *pf_file; 99 SLIST_ENTRY(patfile) pf_next; 100 }; 101 SLIST_HEAD(, patfile) patfilelh; 102 103 extern char *__progname; 104 105 static void 106 usage(void) 107 { 108 fprintf(stderr, 109 #ifdef NOZ 110 "usage: %s [-abcEFGHhIiLlnoqRsUVvwx] [-A num] [-B num] [-C[num]]\n" 111 #else 112 "usage: %s [-abcEFGHhIiLlnoqRsUVvwxZ] [-A num] [-B num] [-C[num]]\n" 113 #endif 114 "\t[-e pattern] [-f file] [--binary-files=value] [--context[=num]]\n" 115 "\t[--line-buffered] [pattern] [file ...]\n", __progname); 116 exit(2); 117 } 118 119 #ifdef NOZ 120 static const char optstr[] = "0123456789A:B:CEFGHILRUVabce:f:hilnoqrsuvwxy"; 121 #else 122 static const char optstr[] = "0123456789A:B:CEFGHILRUVZabce:f:hilnoqrsuvwxy"; 123 #endif 124 125 static const struct option long_options[] = 126 { 127 {"binary-files", required_argument, NULL, BIN_OPT}, 128 {"help", no_argument, NULL, HELP_OPT}, 129 {"mmap", no_argument, NULL, MMAP_OPT}, 130 {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 131 {"after-context", required_argument, NULL, 'A'}, 132 {"before-context", required_argument, NULL, 'B'}, 133 {"context", optional_argument, NULL, 'C'}, 134 {"devices", required_argument, NULL, 'D'}, 135 {"extended-regexp", no_argument, NULL, 'E'}, 136 {"fixed-strings", no_argument, NULL, 'F'}, 137 {"basic-regexp", no_argument, NULL, 'G'}, 138 {"with-filename", no_argument, NULL, 'H'}, 139 {"binary", no_argument, NULL, 'U'}, 140 {"version", no_argument, NULL, 'V'}, 141 {"text", no_argument, NULL, 'a'}, 142 {"byte-offset", no_argument, NULL, 'b'}, 143 {"count", no_argument, NULL, 'c'}, 144 {"regexp", required_argument, NULL, 'e'}, 145 {"file", required_argument, NULL, 'f'}, 146 {"no-filename", no_argument, NULL, 'h'}, 147 {"ignore-case", no_argument, NULL, 'i'}, 148 {"files-without-match", no_argument, NULL, 'L'}, 149 {"files-with-matches", no_argument, NULL, 'l'}, 150 {"line-number", no_argument, NULL, 'n'}, 151 {"quiet", no_argument, NULL, 'q'}, 152 {"silent", no_argument, NULL, 'q'}, 153 {"recursive", no_argument, NULL, 'r'}, 154 {"no-messages", no_argument, NULL, 's'}, 155 {"revert-match", no_argument, NULL, 'v'}, 156 {"word-regexp", no_argument, NULL, 'w'}, 157 {"line-regexp", no_argument, NULL, 'x'}, 158 {"unix-byte-offsets", no_argument, NULL, 'u'}, 159 #ifndef NOZ 160 {"decompress", no_argument, NULL, 'Z'}, 161 #endif 162 {NULL, no_argument, NULL, 0} 163 }; 164 165 166 static void 167 add_pattern(char *pat, size_t len) 168 { 169 if (!xflag && (len == 0 || matchall)) { 170 matchall = 1; 171 return; 172 } 173 if (patterns == pattern_sz) { 174 pattern_sz *= 2; 175 pattern = grep_reallocarray(pattern, ++pattern_sz, sizeof(*pattern)); 176 } 177 if (len > 0 && pat[len - 1] == '\n') 178 --len; 179 /* pat may not be NUL-terminated */ 180 if (wflag && !Fflag) { 181 int bol = 0, eol = 0, extra; 182 if (pat[0] == '^') 183 bol = 1; 184 if (len > 0 && pat[len - 1] == '$') 185 eol = 1; 186 extra = Eflag ? 2 : 4; 187 pattern[patterns] = grep_malloc(len + 15 + extra); 188 snprintf(pattern[patterns], len + 15 + extra, 189 "%s[[:<:]]%s%.*s%s[[:>:]]%s", 190 bol ? "^" : "", 191 Eflag ? "(" : "\\(", 192 (int)len - bol - eol, pat + bol, 193 Eflag ? ")" : "\\)", 194 eol ? "$" : ""); 195 len += 14 + extra; 196 } else { 197 pattern[patterns] = grep_malloc(len + 1); 198 memcpy(pattern[patterns], pat, len); 199 pattern[patterns][len] = '\0'; 200 } 201 ++patterns; 202 } 203 204 static void 205 add_patterns(char *pats) 206 { 207 char *nl; 208 209 while ((nl = strchr(pats, '\n')) != NULL) { 210 add_pattern(pats, nl - pats); 211 pats = nl + 1; 212 } 213 add_pattern(pats, strlen(pats)); 214 } 215 216 static void 217 read_patterns(const char *fn) 218 { 219 FILE *f; 220 char *line; 221 size_t len; 222 223 if ((f = fopen(fn, "r")) == NULL) 224 err(2, "%s", fn); 225 while ((line = fgetln(f, &len)) != NULL) 226 add_pattern(line, *line == '\n' ? 0 : len); 227 if (ferror(f)) 228 err(2, "%s", fn); 229 fclose(f); 230 } 231 232 int 233 main(int argc, char *argv[]) 234 { 235 int c, lastc, prevoptind, newarg, i, needpattern, exprs, expr_sz; 236 struct patfile *patfile, *pf_next; 237 long l; 238 char **expr; 239 const char *errstr; 240 241 if (pledge("stdio rpath", NULL) == -1) 242 err(2, "pledge"); 243 244 SLIST_INIT(&patfilelh); 245 switch (__progname[0]) { 246 case 'e': 247 Eflag = 1; 248 break; 249 case 'f': 250 Fflag = 1; 251 break; 252 #ifndef NOZ 253 case 'z': 254 Zflag = 1; 255 switch(__progname[1]) { 256 case 'e': 257 Eflag = 1; 258 break; 259 case 'f': 260 Fflag = 1; 261 break; 262 } 263 break; 264 #endif 265 } 266 267 lastc = '\0'; 268 newarg = 1; 269 prevoptind = 1; 270 needpattern = 1; 271 expr_sz = exprs = 0; 272 expr = NULL; 273 while ((c = getopt_long(argc, argv, optstr, 274 long_options, NULL)) != -1) { 275 switch (c) { 276 case '0': case '1': case '2': case '3': case '4': 277 case '5': case '6': case '7': case '8': case '9': 278 if (newarg || !isdigit(lastc)) 279 Aflag = 0; 280 else if (Aflag > INT_MAX / 10) 281 errx(2, "context out of range"); 282 Aflag = Bflag = (Aflag * 10) + (c - '0'); 283 break; 284 case 'A': 285 case 'B': 286 l = strtonum(optarg, 1, INT_MAX, &errstr); 287 if (errstr != NULL) 288 errx(2, "context %s", errstr); 289 if (c == 'A') 290 Aflag = (int)l; 291 else 292 Bflag = (int)l; 293 break; 294 case 'C': 295 if (optarg == NULL) 296 Aflag = Bflag = 2; 297 else { 298 l = strtonum(optarg, 1, INT_MAX, &errstr); 299 if (errstr != NULL) 300 errx(2, "context %s", errstr); 301 Aflag = Bflag = (int)l; 302 } 303 break; 304 case 'E': 305 Fflag = 0; 306 Eflag = 1; 307 break; 308 case 'F': 309 Eflag = 0; 310 Fflag = 1; 311 break; 312 case 'G': 313 Eflag = Fflag = 0; 314 break; 315 case 'H': 316 Hflag = 1; 317 break; 318 case 'I': 319 binbehave = BIN_FILE_SKIP; 320 break; 321 case 'L': 322 lflag = 0; 323 Lflag = qflag = 1; 324 break; 325 case 'R': 326 case 'r': 327 Rflag = 1; 328 break; 329 case 'U': 330 binbehave = BIN_FILE_BIN; 331 break; 332 case 'V': 333 fprintf(stderr, "grep version %u.%u\n", VER_MAJ, VER_MIN); 334 exit(0); 335 break; 336 #ifndef NOZ 337 case 'Z': 338 Zflag = 1; 339 break; 340 #endif 341 case 'a': 342 binbehave = BIN_FILE_TEXT; 343 break; 344 case 'b': 345 bflag = 1; 346 break; 347 case 'c': 348 cflag = 1; 349 break; 350 case 'e': 351 /* defer adding of expressions until all arguments are parsed */ 352 if (exprs == expr_sz) { 353 expr_sz *= 2; 354 expr = grep_reallocarray(expr, ++expr_sz, 355 sizeof(*expr)); 356 } 357 needpattern = 0; 358 expr[exprs] = optarg; 359 ++exprs; 360 break; 361 case 'f': 362 patfile = grep_malloc(sizeof(*patfile)); 363 patfile->pf_file = optarg; 364 SLIST_INSERT_HEAD(&patfilelh, patfile, pf_next); 365 needpattern = 0; 366 break; 367 case 'h': 368 hflag = 1; 369 break; 370 case 'i': 371 case 'y': 372 iflag = 1; 373 cflags |= REG_ICASE; 374 break; 375 case 'l': 376 Lflag = 0; 377 lflag = qflag = 1; 378 break; 379 case 'n': 380 nflag = 1; 381 break; 382 case 'o': 383 oflag = 1; 384 break; 385 case 'q': 386 qflag = 1; 387 break; 388 case 's': 389 sflag = 1; 390 break; 391 case 'v': 392 vflag = 1; 393 break; 394 case 'w': 395 wflag = 1; 396 break; 397 case 'x': 398 xflag = 1; 399 break; 400 case BIN_OPT: 401 if (strcmp("binary", optarg) == 0) 402 binbehave = BIN_FILE_BIN; 403 else if (strcmp("without-match", optarg) == 0) 404 binbehave = BIN_FILE_SKIP; 405 else if (strcmp("text", optarg) == 0) 406 binbehave = BIN_FILE_TEXT; 407 else 408 errx(2, "Unknown binary-files option"); 409 break; 410 case 'u': 411 case MMAP_OPT: 412 /* default, compatibility */ 413 break; 414 case LINEBUF_OPT: 415 lbflag = 1; 416 break; 417 case HELP_OPT: 418 default: 419 usage(); 420 } 421 lastc = c; 422 newarg = optind != prevoptind; 423 prevoptind = optind; 424 } 425 argc -= optind; 426 argv += optind; 427 428 for (i = 0; i < exprs; i++) 429 add_patterns(expr[i]); 430 free(expr); 431 expr = NULL; 432 433 for (patfile = SLIST_FIRST(&patfilelh); patfile != NULL; 434 patfile = pf_next) { 435 pf_next = SLIST_NEXT(patfile, pf_next); 436 read_patterns(patfile->pf_file); 437 free(patfile); 438 } 439 440 if (argc == 0 && needpattern) 441 usage(); 442 443 if (argc != 0 && needpattern) { 444 add_patterns(*argv); 445 --argc; 446 ++argv; 447 } 448 449 if (Rflag && argc == 0) 450 warnx("warning: recursive search of stdin"); 451 if (Eflag) 452 cflags |= REG_EXTENDED; 453 if (Fflag) 454 cflags |= REG_NOSPEC; 455 #ifdef SMALL 456 /* Sorry, this won't work */ 457 if (Fflag && wflag) 458 errx(1, "Can't use small fgrep with -w"); 459 #endif 460 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 461 r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 462 for (i = 0; i < patterns; ++i) { 463 /* Check if cheating is allowed (always is for fgrep). */ 464 #ifndef SMALL 465 if (Fflag) { 466 fgrepcomp(&fg_pattern[i], pattern[i]); 467 } else 468 #endif 469 { 470 if (fastcomp(&fg_pattern[i], pattern[i])) { 471 /* Fall back to full regex library */ 472 c = regcomp(&r_pattern[i], pattern[i], cflags); 473 if (c != 0) { 474 regerror(c, &r_pattern[i], re_error, 475 RE_ERROR_BUF); 476 errx(2, "%s", re_error); 477 } 478 } 479 } 480 } 481 482 if (lbflag) 483 setvbuf(stdout, NULL, _IOLBF, 0); 484 485 if ((argc == 0 || argc == 1) && !Rflag && !Hflag) 486 hflag = 1; 487 488 if (argc == 0) 489 exit(!procfile(NULL)); 490 491 if (Rflag) 492 c = grep_tree(argv); 493 else 494 for (c = 0; argc--; ++argv) 495 c += procfile(*argv); 496 497 exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1)); 498 } 499