1 /* $NetBSD: grep.c,v 1.2 2004/05/05 14:34:55 cjep 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 30 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __RCSID("$NetBSD: grep.c,v 1.2 2004/05/05 14:34:55 cjep Exp $"); 35 #endif /* not lint */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <getopt.h> 43 #include <limits.h> 44 #include <regex.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <unistd.h> 49 50 #include "grep.h" 51 52 /* 53 * Upper bound of number of digits to represent an int in decimal 54 * 2^8n <= 10^3n. Allow a terminator. 55 */ 56 #define MAX_BUF_DIGITS (sizeof(int) * 3) + 1 57 58 /* Flags passed to regcomp() and regexec() */ 59 int cflags = REG_BASIC; 60 int eflags = REG_STARTEND; 61 62 int matchall; /* shortcut */ 63 int patterns, pattern_sz; 64 char **pattern; 65 regex_t *r_pattern; 66 67 /* For regex errors */ 68 char re_error[RE_ERROR_BUF + 1]; 69 70 /* Command-line flags */ 71 int Aflag; /* -A x: print x lines trailing each match */ 72 int Bflag; /* -B x: print x lines leading each match */ 73 int Eflag; /* -E: interpret pattern as extended regexp */ 74 int Fflag; /* -F: interpret pattern as list of fixed strings */ 75 int Gflag; /* -G: interpret pattern as basic regexp */ 76 int Hflag; /* -H: Always print filenames */ 77 int Lflag; /* -L: only show names of files with no matches */ 78 /*int Pflag; *//* -P: if -r, no symlinks are followed */ 79 /*int Sflag; *//* -S: if -r, follow all symlinks */ 80 int bflag; /* -b: show block numbers for each match */ 81 int cflag; /* -c: only show a count of matching lines */ 82 int hflag; /* -h: Never print filenames. -H overrides */ 83 int lflag; /* -l: only show names of files with matches */ 84 int mflag; /* -m: specify maximum line matches (per file) */ 85 int nflag; /* -n: show line numbers in front of matching lines */ 86 int oflag; /* -o: only print out matches */ 87 int qflag; /* -q: quiet mode (don't output anything) */ 88 int sflag; /* -s: silent mode (ignore errors) */ 89 int vflag; /* -v: only show non-matching lines */ 90 int wflag; /* -w: pattern must start and end on word boundaries */ 91 int xflag; /* -x: pattern must match entire line */ 92 int lbflag; /* --line-buffered */ 93 94 int colours = 0; /* Attempt to use terminal colours */ 95 const char *grep_colour = "01;32"; /* Default colour string, green */ 96 char *uc; 97 98 /* Characters to print after filenames */ 99 char fn_endchar = '\n'; 100 char fn_colonchar = ':'; 101 char fn_dashchar = '-'; 102 char line_endchar = '\n'; /* End of line character */ 103 104 int maxcount = 0; /* Maximum line matches per file */ 105 int output_filenames = 0; 106 107 /* Argv[0] flags */ 108 int zgrep; /* If we are invoked as zgrep */ 109 110 int binbehave = BIN_FILE_BIN; 111 int dirbehave = GREP_READ; 112 int devbehave = GREP_READ; 113 /*int linkbehave = LINK_FOLLOW;*/ 114 char *stdin_label; 115 116 enum { 117 BIN_OPT = CHAR_MAX + 1, 118 HELP_OPT, 119 LABEL_OPT, 120 MMAP_OPT, 121 LINK_OPT, 122 COLOUR_OPT, 123 LINEBUF_OPT 124 }; 125 126 /* Housekeeping */ 127 int first; /* flag whether or not this is our first match */ 128 int tail; /* lines left to print */ 129 130 static void 131 usage(void) 132 { 133 fprintf(stderr, "usage: %s %s %s\n", 134 getprogname(), 135 "[-[ABC] num] [-EFGHILVZabcdhilnoqrsvwxz]", 136 "[-D action] [-d action] [-e pattern] [-f file]"); 137 exit(2); 138 } 139 140 static char *optstr = "0123456789A:B:C:D:EFGHILUVZabcd:e:f:hilm:noqrsuvwxyz"; 141 142 struct option long_options[] = 143 { 144 {"binary-files", required_argument, NULL, BIN_OPT}, 145 {"help", no_argument, NULL, HELP_OPT}, 146 {"label", required_argument, NULL, LABEL_OPT}, 147 {"mmap", no_argument, NULL, MMAP_OPT}, 148 {"line-buffered", no_argument, NULL, LINEBUF_OPT}, 149 /* {"links", required_argument, NULL, LINK_OPT},*/ 150 {"after-context", required_argument, NULL, 'A'}, 151 {"before-context", required_argument, NULL, 'B'}, 152 {"color", optional_argument, NULL, COLOUR_OPT}, 153 {"colour", optional_argument, NULL, COLOUR_OPT}, 154 {"context", optional_argument, NULL, 'C'}, 155 {"devices", required_argument, NULL, 'D'}, 156 {"extended-regexp", no_argument, NULL, 'E'}, 157 {"fixed-strings", no_argument, NULL, 'F'}, 158 {"fixed-regexp", no_argument, NULL, 'F'}, 159 {"basic-regexp", no_argument, NULL, 'G'}, 160 {"with-filename", no_argument, NULL, 'H'}, 161 {"files-without-match", no_argument, NULL, 'L'}, 162 {"binary", no_argument, NULL, 'U'}, 163 {"version", no_argument, NULL, 'V'}, 164 {"null", no_argument, NULL, 'Z'}, 165 {"text", no_argument, NULL, 'a'}, 166 {"byte-offset", no_argument, NULL, 'b'}, 167 {"count", no_argument, NULL, 'c'}, 168 {"directories", required_argument, NULL, 'd'}, 169 {"regexp", required_argument, NULL, 'e'}, 170 {"file", required_argument, NULL, 'f'}, 171 {"no-filename", no_argument, NULL, 'h'}, 172 {"ignore-case", no_argument, NULL, 'i'}, 173 {"files-with-matches", no_argument, NULL, 'l'}, 174 {"max-count", required_argument, NULL, 'm'}, 175 {"line-number", no_argument, NULL, 'n'}, 176 {"only-matching", no_argument, NULL, 'o'}, 177 {"quiet", no_argument, NULL, 'q'}, 178 {"silent", no_argument, NULL, 'q'}, 179 {"recursive", no_argument, NULL, 'r'}, 180 {"no-messages", no_argument, NULL, 's'}, 181 {"unix-byte-offsets", no_argument, NULL, 'u'}, 182 {"invert-match", no_argument, NULL, 'v'}, 183 {"word-regexp", no_argument, NULL, 'w'}, 184 {"line-regexp", no_argument, NULL, 'x'}, 185 {"null-data", no_argument, NULL, 'z'}, 186 187 {NULL, no_argument, NULL, 0} 188 }; 189 190 static void 191 add_pattern(char *pat, size_t len) 192 { 193 if (len == 0 || matchall) { 194 matchall = 1; 195 return; 196 } 197 if (patterns == pattern_sz) { 198 pattern_sz *= 2; 199 pattern = grep_realloc(pattern, ++pattern_sz * sizeof(*pattern)); 200 } 201 if (pat[len - 1] == '\n') 202 --len; 203 pattern[patterns] = grep_malloc(len + 1); 204 strncpy(pattern[patterns], pat, len); 205 pattern[patterns][len] = '\0'; 206 ++patterns; 207 } 208 209 static void 210 read_patterns(char *fn) 211 { 212 FILE *f; 213 char *line; 214 size_t len; 215 int nl; 216 217 if ((f = fopen(fn, "r")) == NULL) 218 err(2, "%s", fn); 219 nl = 0; 220 while ((line = fgetln(f, &len)) != NULL) { 221 if (*line == '\n') { 222 ++nl; 223 continue; 224 } 225 if (nl) { 226 matchall = 1; 227 break; 228 } 229 nl = 0; 230 add_pattern(line, len); 231 } 232 if (ferror(f)) 233 err(2, "%s", fn); 234 fclose(f); 235 } 236 237 static int 238 check_context_arg(char const *str) { 239 char *ep; 240 long lval; 241 242 errno = 0; 243 lval = strtol(str, &ep, 10); 244 245 if (str[0] == '\0' || *ep != '\0') 246 errx(2, "Invalid context argument"); 247 248 if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) || 249 (lval > INT_MAX || lval < INT_MIN)) 250 errx(2, "Context argument out of range"); 251 252 return lval; 253 254 } 255 256 static int 257 grep_getopt(int argc, char *const *argv) 258 { 259 int c, ptr; 260 char buffer[MAX_BUF_DIGITS]; 261 262 ptr = 0; 263 while (c = getopt_long(argc, argv, optstr, long_options, 264 (int *)NULL), '0' <= c && 265 c <= '9' && ptr < MAX_BUF_DIGITS) { 266 267 /* Avoid leading zeros */ 268 if (ptr != 0 || (ptr == 0 && c != '0')) 269 buffer[ptr++] = c; 270 } 271 272 if (ptr >= MAX_BUF_DIGITS) 273 errx(2, "Context argument out of range"); 274 275 if (ptr) { 276 buffer[ptr] = '\0'; /* We now have a string of digits */ 277 Aflag = Bflag = check_context_arg(buffer); 278 } 279 280 return c; 281 } 282 283 int 284 main(int argc, char *argv[]) 285 { 286 const char *progname; 287 int c, i; 288 struct stat sb; 289 290 stdin_label = "(standard input)"; 291 292 progname = getprogname(); 293 switch (progname[0]) { 294 case 'e': 295 Eflag++; 296 break; 297 case 'f': 298 Fflag++; 299 break; 300 case 'g': 301 Gflag++; 302 break; 303 case 'z': 304 zgrep++; 305 switch (progname[1]) { 306 case 'e': 307 Eflag++; 308 break; 309 case 'f': 310 Fflag++; 311 break; 312 case 'g': 313 Gflag++; 314 break; 315 } 316 break; 317 } 318 319 while ((c = grep_getopt(argc, argv)) != -1) { 320 321 switch (c) { 322 323 case 'A': 324 Aflag = check_context_arg(optarg); 325 break; 326 case 'B': 327 Bflag = check_context_arg(optarg); 328 break; 329 case 'C': 330 if (optarg == NULL) 331 Aflag = Bflag = 2; 332 else 333 Aflag = Bflag = check_context_arg(optarg); 334 break; 335 case 'D': 336 if (strcmp("read", optarg) == 0) 337 devbehave = GREP_READ; 338 else if (strcmp("skip", optarg) == 0) 339 devbehave = GREP_SKIP; 340 else { 341 errx(2, "Unknown device option"); 342 } 343 break; 344 345 case 'E': 346 Fflag = Gflag = 0; 347 Eflag++; 348 break; 349 case 'F': 350 Eflag = Gflag = 0; 351 Fflag++; 352 break; 353 case 'G': 354 Eflag = Fflag = 0; 355 Gflag++; 356 break; 357 case 'H': 358 Hflag = 1; 359 break; 360 case 'I': 361 binbehave = BIN_FILE_SKIP; 362 break; 363 case 'L': 364 lflag = 0; 365 Lflag = qflag = 1; 366 break; 367 /* case 'P': 368 linkbehave = LINK_SKIP; 369 break; 370 case 'S': 371 linkbehave = LINK_FOLLOW; 372 break;*/ 373 case 'R': 374 case 'r': 375 dirbehave = GREP_RECURSE; 376 break; 377 case 'U': 378 case 'u': 379 /* these are here for compatability */ 380 break; 381 case 'V': 382 fprintf(stdout, "grep version %s\n", VERSION); 383 exit(0); 384 break; 385 case 'Z': 386 fn_colonchar = fn_endchar = fn_dashchar = 0; 387 break; 388 case 'a': 389 binbehave = BIN_FILE_TEXT; 390 break; 391 case 'b': 392 bflag = 1; 393 break; 394 case 'c': 395 cflag = 1; 396 break; 397 case 'd': 398 if (strcmp("read", optarg) == 0) 399 dirbehave = GREP_READ; 400 else if (strcmp("skip", optarg) == 0) 401 dirbehave = GREP_SKIP; 402 else if (strcmp("recurse", optarg) == 0) 403 dirbehave = GREP_RECURSE; 404 else { 405 errx(2, "Unknown directory option\n"); 406 } 407 break; 408 409 case 'e': 410 add_pattern(optarg, strlen(optarg)); 411 break; 412 case 'f': 413 read_patterns(optarg); 414 break; 415 case 'h': 416 hflag = 1; 417 break; 418 case 'i': 419 case 'y': 420 cflags |= REG_ICASE; 421 break; 422 case 'l': 423 Lflag = 0; 424 lflag = qflag = 1; 425 break; 426 case 'm': 427 mflag = 1; 428 maxcount = strtol(optarg, (char **)NULL, 10); 429 break; 430 case 'n': 431 nflag = 1; 432 break; 433 case 'o': 434 oflag = 1; 435 break; 436 case 'q': 437 qflag = 1; 438 break; 439 case 's': 440 sflag = 1; 441 break; 442 case 'v': 443 vflag = 1; 444 break; 445 case 'w': 446 wflag = 1; 447 break; 448 case 'x': 449 xflag = 1; 450 break; 451 case 'z': 452 line_endchar = 0; 453 break; 454 case BIN_OPT: 455 if (strcmp("binary", optarg) == 0) 456 binbehave = BIN_FILE_BIN; 457 else if (strcmp("without-match", optarg) == 0) 458 binbehave = BIN_FILE_SKIP; 459 else if (strcmp("text", optarg) == 0) 460 binbehave = BIN_FILE_TEXT; 461 else { 462 errx(2, "Unknown binary-files option\n"); 463 } 464 break; 465 466 case COLOUR_OPT: 467 if (optarg == NULL || strcmp("auto", optarg) == 0 || 468 strcmp("tty", optarg) == 0 || 469 strcmp("if-tty", optarg) == 0) { 470 471 /* Check that stdout is a terminal */ 472 if (isatty(STDOUT_FILENO) && 473 getenv("TERM") && 474 strcmp(getenv("TERM"), "dumb") != 0) 475 colours = 1; 476 else 477 colours = 0; 478 479 } else if (strcmp("always", optarg) == 0 || 480 strcmp("yes", optarg) == 0 || 481 strcmp("force", optarg) == 0) 482 colours = 1; 483 else if (strcmp("never", optarg) == 0 || 484 strcmp("no", optarg) == 0 || 485 strcmp("none", optarg) == 0) 486 colours = 0; 487 else 488 errx(2, "Unknown color option\n"); 489 490 uc = getenv("GREP_COLOR"); 491 if (colours == 1 && uc != NULL && *uc != '\0') 492 grep_colour = uc; 493 break; 494 case LABEL_OPT: 495 stdin_label = optarg; 496 break; 497 case MMAP_OPT: 498 break; 499 /* 500 * case LINK_OPT: 501 * if (strcmp("explicit", optarg) == 0) 502 * linkbehave = LINK_EXPLICIT; 503 * else if (strcmp("follow", optarg) == 0) 504 * linkbehave = LINK_FOLLOW; 505 * else if (strcmp("skip", optarg) == 0) 506 * linkbehave = LINK_SKIP; 507 * else { 508 * errx(2, "Unknown links option\n"); 509 * } 510 * break; 511 */ 512 case LINEBUF_OPT: 513 lbflag = 1; 514 break; 515 516 case HELP_OPT: 517 default: 518 usage(); 519 } 520 521 } 522 523 argc -= optind; 524 argv += optind; 525 526 if (argc == 0 && patterns == 0) 527 usage(); 528 if (patterns == 0) { 529 add_pattern(*argv, strlen(*argv)); 530 --argc; 531 ++argv; 532 } 533 534 if (Eflag) 535 cflags |= REG_EXTENDED; 536 else if (Fflag) 537 cflags |= REG_NOSPEC; 538 r_pattern = grep_malloc(patterns * sizeof(*r_pattern)); 539 for (i = 0; i < patterns; ++i) { 540 if ((c = regcomp(&r_pattern[i], pattern[i], cflags))) { 541 regerror(c, &r_pattern[i], re_error, RE_ERROR_BUF); 542 errx(2, "%s", re_error); 543 } 544 } 545 546 if ((argc > 1 && !hflag) || Hflag) 547 output_filenames = 1; 548 549 if (argc == 1 && !hflag && dirbehave == GREP_RECURSE) 550 if (!stat(*argv, &sb) && (sb.st_mode & S_IFMT) == S_IFDIR) 551 output_filenames = 1; 552 553 if (argc == 0) 554 exit(!procfile(NULL)); 555 556 if (lbflag) 557 setlinebuf(stdout); 558 559 if (dirbehave == GREP_RECURSE) 560 c = grep_tree(argv); 561 else 562 for (c = 0; argc--; ++argv) 563 c += procfile(*argv); 564 565 exit(!c); 566 } 567