1 /* $OpenBSD: grep.c,v 1.22 2003/07/16 19:08:21 millert 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/limits.h> 31 #include <sys/stat.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 Gflag; /* -G: interpret pattern as basic regexp */ 64 int Hflag; /* -H: if -R, follow explicitly listed symlinks */ 65 int Lflag; /* -L: only show names of files with no matches */ 66 int Pflag; /* -P: if -R, no symlinks are followed */ 67 int Rflag; /* -R: recursively search directory trees */ 68 int Sflag; /* -S: if -R, follow all symlinks */ 69 int Vflag; /* -V: display version information */ 70 #ifndef NOZ 71 int Zflag; /* -Z: decompress input before processing */ 72 #endif 73 int bflag; /* -b: show block numbers for each match */ 74 int cflag; /* -c: only show a count of matching lines */ 75 int hflag; /* -h: don't print filename headers */ 76 int iflag; /* -i: ignore case */ 77 int lflag; /* -l: only show names of files with matches */ 78 int nflag; /* -n: show line numbers in front of matching lines */ 79 int oflag; /* -o: always print file name */ 80 int qflag; /* -q: quiet mode (don't output anything) */ 81 int sflag; /* -s: silent mode (ignore errors) */ 82 int vflag; /* -v: only show non-matching lines */ 83 int wflag; /* -w: pattern must start and end on word boundaries */ 84 int xflag; /* -x: pattern must match entire line */ 85 86 int binbehave = BIN_FILE_BIN; 87 88 enum { 89 BIN_OPT = CHAR_MAX + 1, 90 HELP_OPT, 91 MMAP_OPT 92 }; 93 94 /* Housekeeping */ 95 int first; /* flag whether or not this is our first match */ 96 int tail; /* lines left to print */ 97 int lead; /* number of lines in leading context queue */ 98 int boleol; /* At least one pattern has a bol or eol */ 99 size_t maxPatternLen; /* Longest length of all patterns */ 100 101 extern char *__progname; 102 103 static void 104 usage(void) 105 { 106 fprintf(stderr, 107 #ifdef NOZ 108 "usage: %s [-[AB] num] [-CEFGHILPRSUVabchilnoqsvwx]" 109 #else 110 "usage: %s [-[AB] num] [-CEFGHILPRSUVZabchilnoqsvwx]" 111 #endif 112 " [-e pattern] [-f file] [file ...]\n", __progname); 113 exit(2); 114 } 115 116 #ifdef NOZ 117 static char *optstr = "0123456789A:B:CEFGHILPSRUVabce:f:hilnoqrsuvwxy"; 118 #else 119 static char *optstr = "0123456789A:B:CEFGHILPSRUVZabce:f:hilnoqrsuvwxy"; 120 #endif 121 122 struct option long_options[] = 123 { 124 {"binary-files", required_argument, NULL, BIN_OPT}, 125 {"help", no_argument, NULL, HELP_OPT}, 126 {"mmap", no_argument, NULL, MMAP_OPT}, 127 {"after-context", required_argument, NULL, 'A'}, 128 {"before-context", required_argument, NULL, 'B'}, 129 {"context", optional_argument, NULL, 'C'}, 130 {"devices", required_argument, NULL, 'D'}, 131 {"extended-regexp", no_argument, NULL, 'E'}, 132 {"fixed-strings", no_argument, NULL, 'F'}, 133 {"basic-regexp", no_argument, NULL, 'G'}, 134 {"binary", no_argument, NULL, 'U'}, 135 {"version", no_argument, NULL, 'V'}, 136 {"text", no_argument, NULL, 'a'}, 137 {"byte-offset", no_argument, NULL, 'b'}, 138 {"count", no_argument, NULL, 'c'}, 139 {"regexp", required_argument, NULL, 'e'}, 140 {"file", required_argument, NULL, 'f'}, 141 {"no-filename", no_argument, NULL, 'h'}, 142 {"ignore-case", no_argument, NULL, 'i'}, 143 {"files-without-match", no_argument, NULL, 'L'}, 144 {"files-with-matches", no_argument, NULL, 'l'}, 145 {"line-number", no_argument, NULL, 'n'}, 146 {"quiet", no_argument, NULL, 'q'}, 147 {"silent", no_argument, NULL, 'q'}, 148 {"recursive", no_argument, NULL, 'r'}, 149 {"no-messages", no_argument, NULL, 's'}, 150 {"revert-match", no_argument, NULL, 'v'}, 151 {"word-regexp", no_argument, NULL, 'w'}, 152 {"line-regexp", no_argument, NULL, 'x'}, 153 {"unix-byte-offsets", no_argument, NULL, 'u'}, 154 #ifndef NOZ 155 {"decompress", no_argument, NULL, 'Z'}, 156 #endif 157 {NULL, no_argument, NULL, 0} 158 }; 159 160 161 static void 162 add_pattern(char *pat, size_t len) 163 { 164 if (len == 0 || matchall) { 165 matchall = 1; 166 return; 167 } 168 if (patterns == pattern_sz) { 169 pattern_sz *= 2; 170 pattern = grep_realloc(pattern, ++pattern_sz * sizeof(*pattern)); 171 } 172 if (pat[len - 1] == '\n') 173 --len; 174 pattern[patterns] = grep_malloc(len + 1); 175 /* pat may not be NUL-terminated */ 176 memcpy(pattern[patterns], pat, len); 177 pattern[patterns][len] = '\0'; 178 ++patterns; 179 180 if (len > maxPatternLen) 181 maxPatternLen = len; 182 } 183 184 static void 185 read_patterns(char *fn) 186 { 187 FILE *f; 188 char *line; 189 size_t len; 190 int nl; 191 192 if ((f = fopen(fn, "r")) == NULL) 193 err(2, "%s", fn); 194 nl = 0; 195 while ((line = fgetln(f, &len)) != NULL) { 196 if (*line == '\n') { 197 ++nl; 198 continue; 199 } 200 if (nl) { 201 matchall = 1; 202 break; 203 } 204 nl = 0; 205 add_pattern(line, len); 206 } 207 if (ferror(f)) 208 err(2, "%s", fn); 209 fclose(f); 210 } 211 212 static void 213 free_patterns(void) 214 { 215 int i; 216 217 for (i = 0; i < patterns; i++) { 218 if (fg_pattern[i].pattern) 219 free(fg_pattern[i].pattern); 220 else 221 regfree(&r_pattern[i]); 222 free(pattern[i]); 223 } 224 225 free(fg_pattern); 226 free(r_pattern); 227 free(pattern); 228 } 229 230 int 231 main(int argc, char *argv[]) 232 { 233 int c, lastc, prevoptind, i; 234 long l; 235 char *ep; 236 237 switch (__progname[0]) { 238 case 'e': 239 Eflag++; 240 break; 241 case 'f': 242 Fflag++; 243 break; 244 case 'g': 245 Gflag++; 246 break; 247 #ifndef NOZ 248 case 'z': 249 Zflag++; 250 switch(__progname[1]) { 251 case 'e': 252 Eflag++; 253 break; 254 case 'f': 255 Fflag++; 256 break; 257 case 'g': 258 Gflag++; 259 break; 260 } 261 break; 262 #endif 263 } 264 265 lastc = '\0'; 266 prevoptind = 0; 267 while ((c = getopt_long(argc, argv, optstr, 268 long_options, NULL)) != -1) { 269 switch (c) { 270 case '0': case '1': case '2': case '3': case '4': 271 case '5': case '6': case '7': case '8': case '9': 272 if (optind == prevoptind && isdigit(lastc)) { 273 if (Aflag > INT_MAX / 10) 274 errx(2, "context out of range"); 275 Aflag = Bflag = (Aflag * 10) + (c - '0'); 276 } else 277 Aflag = Bflag = c - '0'; 278 break; 279 case 'A': 280 case 'B': 281 l = strtol(optarg, &ep, 10); 282 if (ep == optarg || *ep != '\0' || 283 l <= 0 || l >= INT_MAX) 284 errx(2, "context out of range"); 285 if (c == 'A') 286 Aflag = (int)l; 287 else 288 Bflag = (int)l; 289 break; 290 case 'C': 291 if (optarg == NULL) 292 Aflag = Bflag = 2; 293 else { 294 l = strtol(optarg, &ep, 10); 295 if (ep == optarg || *ep != '\0' || 296 l <= 0 || l >= INT_MAX) 297 errx(2, "context out of range"); 298 Aflag = Bflag = (int)l; 299 } 300 break; 301 case 'E': 302 Fflag = Gflag = 0; 303 Eflag++; 304 break; 305 case 'F': 306 Eflag = Gflag = 0; 307 Fflag++; 308 break; 309 case 'G': 310 Eflag = Fflag = 0; 311 Gflag++; 312 break; 313 case 'H': 314 Hflag++; 315 break; 316 case 'I': 317 binbehave = BIN_FILE_SKIP; 318 break; 319 case 'L': 320 lflag = 0; 321 Lflag = qflag = 1; 322 break; 323 case 'P': 324 Pflag++; 325 break; 326 case 'S': 327 Sflag++; 328 break; 329 case 'R': 330 case 'r': 331 Rflag++; 332 oflag++; 333 break; 334 case 'U': 335 binbehave = BIN_FILE_BIN; 336 break; 337 case 'V': 338 fprintf(stderr, "grep version %u.%u\n", VER_MAJ, VER_MIN); 339 exit(0); 340 break; 341 #ifndef NOZ 342 case 'Z': 343 Zflag++; 344 break; 345 #endif 346 case 'a': 347 binbehave = BIN_FILE_TEXT; 348 break; 349 case 'b': 350 bflag = 1; 351 break; 352 case 'c': 353 cflag = 1; 354 break; 355 case 'e': 356 add_pattern(optarg, strlen(optarg)); 357 break; 358 case 'f': 359 read_patterns(optarg); 360 break; 361 case 'h': 362 oflag = 0; 363 hflag = 1; 364 break; 365 case 'i': 366 case 'y': 367 iflag = 1; 368 cflags |= REG_ICASE; 369 break; 370 case 'l': 371 Lflag = 0; 372 lflag = qflag = 1; 373 break; 374 case 'n': 375 nflag = 1; 376 break; 377 case 'o': 378 hflag = 0; 379 oflag = 1; 380 break; 381 case 'q': 382 qflag = 1; 383 break; 384 case 's': 385 sflag = 1; 386 break; 387 case 'v': 388 vflag = 1; 389 break; 390 case 'w': 391 wflag = 1; 392 break; 393 case 'x': 394 xflag = 1; 395 break; 396 case BIN_OPT: 397 if (strcmp("binary", optarg) == 0) 398 binbehave = BIN_FILE_BIN; 399 else if (strcmp("without-match", optarg) == 0) 400 binbehave = BIN_FILE_SKIP; 401 else if (strcmp("text", optarg) == 0) 402 binbehave = BIN_FILE_TEXT; 403 else 404 errx(2, "Unknown binary-files option"); 405 break; 406 case 'u': 407 case MMAP_OPT: 408 /* default, compatibility */ 409 break; 410 case HELP_OPT: 411 default: 412 usage(); 413 } 414 lastc = c; 415 prevoptind = optind; 416 } 417 418 argc -= optind; 419 argv += optind; 420 421 if (argc == 0 && patterns == 0) 422 usage(); 423 424 if (patterns == 0) { 425 add_pattern(*argv, strlen(*argv)); 426 --argc; 427 ++argv; 428 } 429 430 if (Eflag) 431 cflags |= REG_EXTENDED; 432 else if (Fflag) 433 cflags |= REG_NOSPEC; 434 fg_pattern = grep_malloc(patterns * sizeof(*fg_pattern)); 435 r_pattern = grep_malloc(patterns * sizeof(*r_pattern)); 436 for (i = 0; i < patterns; ++i) { 437 /* Check if cheating is allowed */ 438 if (fastcomp(&fg_pattern[i], pattern[i])) { 439 /* Fall back to full regex library */ 440 if ((c = regcomp(&r_pattern[i], pattern[i], cflags))) { 441 regerror(c, &r_pattern[i], re_error, 442 RE_ERROR_BUF); 443 errx(2, "%s", re_error); 444 } 445 } 446 } 447 448 if ((argc == 0 || argc == 1) && !oflag) 449 hflag = 1; 450 451 if (argc == 0) 452 exit(!procfile(NULL)); 453 454 if (Rflag) 455 c = grep_tree(argv); 456 else 457 for (c = 0; argc--; ++argv) 458 c += procfile(*argv); 459 460 free_patterns(); 461 462 exit(!c); 463 } 464