blob: 5a4fa0cfd643f3b30fce8ea56e9a970ef077d502 [file] [log] [blame]
Jeff Sharkey3e8b1582012-07-13 16:37:13 -07001/* $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>
59nl_catd catalog;
60#endif
61
62/*
63 * Default messags to use when NLS is disabled or no catalogue
64 * is found.
65 */
66const 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() */
80int cflags = 0;
81int eflags = REG_STARTEND;
82
83/* Searching patterns */
84unsigned int patterns, pattern_sz;
85char **pattern;
86regex_t *r_pattern;
87fastgrep_t *fg_pattern;
88
89/* Filename exclusion/inclusion patterns */
90unsigned int fpatterns, fpattern_sz;
91unsigned int dpatterns, dpattern_sz;
92struct epat *dpattern, *fpattern;
93
94/* For regex errors */
95char re_error[RE_ERROR_BUF + 1];
96
97/* Command-line flags */
98unsigned long long Aflag; /* -A x: print x lines trailing each match */
99unsigned long long Bflag; /* -B x: print x lines leading each match */
100bool Hflag; /* -H: always print file name */
101bool Lflag; /* -L: only show names of files with no matches */
102bool bflag; /* -b: show block numbers for each match */
103bool cflag; /* -c: only show a count of matching lines */
104bool hflag; /* -h: don't print filename headers */
105bool iflag; /* -i: ignore case */
106bool lflag; /* -l: only show names of files with matches */
107bool mflag; /* -m x: stop reading the files after x matches */
108unsigned long long mcount; /* count for -m */
109bool nflag; /* -n: show line numbers in front of matching lines */
110bool oflag; /* -o: print only matching part */
111bool qflag; /* -q: quiet mode (don't output anything) */
112bool sflag; /* -s: silent mode (ignore errors) */
113bool vflag; /* -v: only show non-matching lines */
114bool wflag; /* -w: pattern must start and end on word boundaries */
115bool xflag; /* -x: pattern must match entire line */
116bool lbflag; /* --line-buffered */
117bool nullflag; /* --null */
118bool nulldataflag; /* --null-data */
119unsigned char line_sep = '\n'; /* 0 for --null-data */
120char *label; /* --label */
121const char *color; /* --color */
122int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
123int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
124int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
125int devbehave = DEV_READ; /* -D: handling of devices */
126int dirbehave = DIR_READ; /* -dRr: handling of directories */
127int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
128
129bool dexclude, dinclude; /* --exclude-dir and --include-dir */
130bool fexclude, finclude; /* --exclude and --include */
131
132enum {
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
146static inline const char *init_color(const char *);
147
148/* Housekeeping */
149int tail; /* lines left to print */
150bool notfound; /* file not found */
151
152extern char *__progname;
153
154/*
155 * Prints usage information and returns 2.
156 */
157__dead static void
158usage(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
168static const char optstr[] =
169 "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
170
171struct 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 */
225static void
226add_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 */
249static void
250add_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 */
267static void
268add_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 */
285static void
286read_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;
Jeff Sharkey3e8b1582012-07-13 16:37:13 -0700297 while ((rlen = getline(&line, &len, f)) != -1)
298 add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
Jeff Sharkey3e8b1582012-07-13 16:37:13 -0700299 free(line);
300 if (ferror(f))
301 err(2, "%s", fn);
302 fclose(f);
303}
304
305static inline const char *
306init_color(const char *d)
307{
308 char *c;
309
310 c = getenv("GREP_COLOR");
311 return (c != NULL ? c : d);
312}
313
314int
315grep_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}