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