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 while ((rlen = getline(&line, &len, f)) != -1) 298 add_pattern(line, *line == '\n' ? 0 : (size_t)rlen); 299 free(line); 300 if (ferror(f)) 301 err(2, "%s", fn); 302 fclose(f); 303 } 304 305 static inline const char * 306 init_color(const char *d) 307 { 308 char *c; 309 310 c = getenv("GREP_COLOR"); 311 return (c != NULL ? c : d); 312 } 313 314 int 315 main(int argc, char *argv[]) 316 { 317 char **aargv, **eargv, *eopts; 318 char *ep; 319 unsigned long long l; 320 unsigned int aargc, eargc, i, j; 321 int c, lastc, needpattern, newarg, prevoptind; 322 323 setlocale(LC_ALL, ""); 324 325 #ifndef WITHOUT_NLS 326 catalog = catopen("grep", NL_CAT_LOCALE); 327 #endif 328 329 /* Check what is the program name of the binary. In this 330 way we can have all the funcionalities in one binary 331 without the need of scripting and using ugly hacks. */ 332 switch (__progname[0]) { 333 case 'e': 334 grepbehave = GREP_EXTENDED; 335 break; 336 case 'f': 337 grepbehave = GREP_FIXED; 338 break; 339 case 'g': 340 grepbehave = GREP_BASIC; 341 break; 342 case 'z': 343 filebehave = FILE_GZIP; 344 switch(__progname[1]) { 345 case 'e': 346 grepbehave = GREP_EXTENDED; 347 break; 348 case 'f': 349 grepbehave = GREP_FIXED; 350 break; 351 case 'g': 352 grepbehave = GREP_BASIC; 353 break; 354 } 355 break; 356 } 357 358 lastc = '\0'; 359 newarg = 1; 360 prevoptind = 1; 361 needpattern = 1; 362 363 eopts = getenv("GREP_OPTIONS"); 364 365 /* support for extra arguments in GREP_OPTIONS */ 366 eargc = 0; 367 if (eopts != NULL) { 368 char *str; 369 370 /* make an estimation of how many extra arguments we have */ 371 for (j = 0; j < strlen(eopts); j++) 372 if (eopts[j] == ' ') 373 eargc++; 374 375 eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1)); 376 377 eargc = 0; 378 /* parse extra arguments */ 379 while ((str = strsep(&eopts, " ")) != NULL) 380 eargv[eargc++] = grep_strdup(str); 381 382 aargv = (char **)grep_calloc(eargc + argc + 1, 383 sizeof(char *)); 384 385 aargv[0] = argv[0]; 386 for (i = 0; i < eargc; i++) 387 aargv[i + 1] = eargv[i]; 388 for (j = 1; j < (unsigned int)argc; j++, i++) 389 aargv[i + 1] = argv[j]; 390 391 aargc = eargc + argc; 392 } else { 393 aargv = argv; 394 aargc = argc; 395 } 396 397 while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) != 398 -1)) { 399 switch (c) { 400 case '0': case '1': case '2': case '3': case '4': 401 case '5': case '6': case '7': case '8': case '9': 402 if (newarg || !isdigit(lastc)) 403 Aflag = 0; 404 else if (Aflag > LLONG_MAX / 10) { 405 errno = ERANGE; 406 err(2, NULL); 407 } 408 Aflag = Bflag = (Aflag * 10) + (c - '0'); 409 break; 410 case 'C': 411 if (optarg == NULL) { 412 Aflag = Bflag = 2; 413 break; 414 } 415 /* FALLTHROUGH */ 416 case 'A': 417 /* FALLTHROUGH */ 418 case 'B': 419 errno = 0; 420 l = strtoull(optarg, &ep, 10); 421 if (((errno == ERANGE) && (l == ULLONG_MAX)) || 422 ((errno == EINVAL) && (l == 0))) 423 err(2, NULL); 424 else if (ep[0] != '\0') { 425 errno = EINVAL; 426 err(2, NULL); 427 } 428 if (c == 'A') 429 Aflag = l; 430 else if (c == 'B') 431 Bflag = l; 432 else 433 Aflag = Bflag = l; 434 break; 435 case 'a': 436 binbehave = BINFILE_TEXT; 437 break; 438 case 'b': 439 bflag = true; 440 break; 441 case 'c': 442 cflag = true; 443 break; 444 case 'D': 445 if (strcasecmp(optarg, "skip") == 0) 446 devbehave = DEV_SKIP; 447 else if (strcasecmp(optarg, "read") == 0) 448 devbehave = DEV_READ; 449 else 450 errx(2, getstr(3), "--devices"); 451 break; 452 case 'd': 453 if (strcasecmp("recurse", optarg) == 0) { 454 Hflag = true; 455 dirbehave = DIR_RECURSE; 456 } else if (strcasecmp("skip", optarg) == 0) 457 dirbehave = DIR_SKIP; 458 else if (strcasecmp("read", optarg) == 0) 459 dirbehave = DIR_READ; 460 else 461 errx(2, getstr(3), "--directories"); 462 break; 463 case 'E': 464 grepbehave = GREP_EXTENDED; 465 break; 466 case 'e': 467 add_pattern(optarg, strlen(optarg)); 468 needpattern = 0; 469 break; 470 case 'F': 471 grepbehave = GREP_FIXED; 472 break; 473 case 'f': 474 read_patterns(optarg); 475 needpattern = 0; 476 break; 477 case 'G': 478 grepbehave = GREP_BASIC; 479 break; 480 case 'H': 481 Hflag = true; 482 break; 483 case 'h': 484 Hflag = false; 485 hflag = true; 486 break; 487 case 'I': 488 binbehave = BINFILE_SKIP; 489 break; 490 case 'i': 491 case 'y': 492 iflag = true; 493 cflags |= REG_ICASE; 494 break; 495 case 'J': 496 filebehave = FILE_BZIP; 497 break; 498 case 'L': 499 lflag = false; 500 Lflag = true; 501 break; 502 case 'l': 503 Lflag = false; 504 lflag = true; 505 break; 506 case 'm': 507 mflag = true; 508 errno = 0; 509 mcount = strtoull(optarg, &ep, 10); 510 if (((errno == ERANGE) && (mcount == ULLONG_MAX)) || 511 ((errno == EINVAL) && (mcount == 0))) 512 err(2, NULL); 513 else if (ep[0] != '\0') { 514 errno = EINVAL; 515 err(2, NULL); 516 } 517 break; 518 case 'n': 519 nflag = true; 520 break; 521 case 'O': 522 linkbehave = LINK_EXPLICIT; 523 break; 524 case 'o': 525 oflag = true; 526 break; 527 case 'p': 528 linkbehave = LINK_SKIP; 529 break; 530 case 'q': 531 qflag = true; 532 break; 533 case 'S': 534 linkbehave = LINK_READ; 535 break; 536 case 'R': 537 case 'r': 538 dirbehave = DIR_RECURSE; 539 Hflag = true; 540 break; 541 case 's': 542 sflag = true; 543 break; 544 case 'U': 545 binbehave = BINFILE_BIN; 546 break; 547 case 'u': 548 case MMAP_OPT: 549 /* noop, compatibility */ 550 break; 551 case 'V': 552 printf(getstr(9), __progname, VERSION); 553 exit(0); 554 case 'v': 555 vflag = true; 556 break; 557 case 'w': 558 wflag = true; 559 break; 560 case 'x': 561 xflag = true; 562 break; 563 case 'Z': 564 nullflag = true; 565 break; 566 case 'z': 567 nulldataflag = true; 568 line_sep = '\0'; 569 break; 570 case BIN_OPT: 571 if (strcasecmp("binary", optarg) == 0) 572 binbehave = BINFILE_BIN; 573 else if (strcasecmp("without-match", optarg) == 0) 574 binbehave = BINFILE_SKIP; 575 else if (strcasecmp("text", optarg) == 0) 576 binbehave = BINFILE_TEXT; 577 else 578 errx(2, getstr(3), "--binary-files"); 579 break; 580 case COLOR_OPT: 581 color = NULL; 582 if (optarg == NULL || strcasecmp("auto", optarg) == 0 || 583 strcasecmp("tty", optarg) == 0 || 584 strcasecmp("if-tty", optarg) == 0) { 585 char *term; 586 587 term = getenv("TERM"); 588 if (isatty(STDOUT_FILENO) && term != NULL && 589 strcasecmp(term, "dumb") != 0) 590 color = init_color("01;31"); 591 } else if (strcasecmp("always", optarg) == 0 || 592 strcasecmp("yes", optarg) == 0 || 593 strcasecmp("force", optarg) == 0) { 594 color = init_color("01;31"); 595 } else if (strcasecmp("never", optarg) != 0 && 596 strcasecmp("none", optarg) != 0 && 597 strcasecmp("no", optarg) != 0) 598 errx(2, getstr(3), "--color"); 599 break; 600 case DECOMPRESS_OPT: 601 filebehave = FILE_GZIP; 602 break; 603 case LABEL_OPT: 604 label = optarg; 605 break; 606 case LINEBUF_OPT: 607 lbflag = true; 608 break; 609 case R_INCLUDE_OPT: 610 finclude = true; 611 add_fpattern(optarg, INCL_PAT); 612 break; 613 case R_EXCLUDE_OPT: 614 fexclude = true; 615 add_fpattern(optarg, EXCL_PAT); 616 break; 617 case R_DINCLUDE_OPT: 618 dinclude = true; 619 add_dpattern(optarg, INCL_PAT); 620 break; 621 case R_DEXCLUDE_OPT: 622 dexclude = true; 623 add_dpattern(optarg, EXCL_PAT); 624 break; 625 case HELP_OPT: 626 default: 627 usage(); 628 } 629 lastc = c; 630 newarg = optind != prevoptind; 631 prevoptind = optind; 632 } 633 aargc -= optind; 634 aargv += optind; 635 636 /* Fail if we don't have any pattern */ 637 if (aargc == 0 && needpattern) 638 usage(); 639 640 /* Process patterns from command line */ 641 if (aargc != 0 && needpattern) { 642 add_pattern(*aargv, strlen(*aargv)); 643 --aargc; 644 ++aargv; 645 } 646 647 switch (grepbehave) { 648 case GREP_FIXED: 649 case GREP_BASIC: 650 break; 651 case GREP_EXTENDED: 652 cflags |= REG_EXTENDED; 653 break; 654 default: 655 /* NOTREACHED */ 656 usage(); 657 } 658 659 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern)); 660 r_pattern = grep_calloc(patterns, sizeof(*r_pattern)); 661 /* 662 * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance. 663 * Optimizations should be done there. 664 */ 665 /* Check if cheating is allowed (always is for fgrep). */ 666 if (grepbehave == GREP_FIXED) { 667 for (i = 0; i < patterns; ++i) 668 fgrepcomp(&fg_pattern[i], pattern[i]); 669 } else { 670 for (i = 0; i < patterns; ++i) { 671 if (fastcomp(&fg_pattern[i], pattern[i])) { 672 /* Fall back to full regex library */ 673 c = regcomp(&r_pattern[i], pattern[i], cflags); 674 if (c != 0) { 675 regerror(c, &r_pattern[i], re_error, 676 RE_ERROR_BUF); 677 errx(2, "%s", re_error); 678 } 679 } 680 } 681 } 682 683 if (lbflag) 684 setlinebuf(stdout); 685 686 if ((aargc == 0 || aargc == 1) && !Hflag) 687 hflag = true; 688 689 if (aargc == 0) 690 exit(!procfile("-")); 691 692 if (dirbehave == DIR_RECURSE) 693 c = grep_tree(aargv); 694 else 695 for (c = 0; aargc--; ++aargv) { 696 if ((finclude || fexclude) && !file_matching(*aargv)) 697 continue; 698 c+= procfile(*aargv); 699 } 700 701 #ifndef WITHOUT_NLS 702 catclose(catalog); 703 #endif 704 705 /* Find out the correct return value according to the 706 results and the command line option. */ 707 exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1)); 708 } 709