Home | History | Annotate | Line # | Download | only in grep
grep.c revision 1.3.40.1
      1  1.3.40.1  bouyer /*	$NetBSD: grep.c,v 1.3.40.1 2011/02/17 12:00:55 bouyer Exp $	*/
      2  1.3.40.1  bouyer /* 	$FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $	*/
      3  1.3.40.1  bouyer /*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
      4       1.2    cjep 
      5       1.1    cjep /*-
      6  1.3.40.1  bouyer  * Copyright (c) 1999 James Howard and Dag-Erling Codan Smrgrav
      7  1.3.40.1  bouyer  * Copyright (C) 2008-2009 Gabor Kovesdan <gabor (at) FreeBSD.org>
      8       1.1    cjep  * All rights reserved.
      9       1.1    cjep  *
     10       1.1    cjep  * Redistribution and use in source and binary forms, with or without
     11       1.1    cjep  * modification, are permitted provided that the following conditions
     12       1.1    cjep  * are met:
     13       1.1    cjep  * 1. Redistributions of source code must retain the above copyright
     14       1.1    cjep  *    notice, this list of conditions and the following disclaimer.
     15       1.1    cjep  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1    cjep  *    notice, this list of conditions and the following disclaimer in the
     17       1.1    cjep  *    documentation and/or other materials provided with the distribution.
     18       1.1    cjep  *
     19       1.1    cjep  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     20       1.1    cjep  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21       1.1    cjep  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22       1.1    cjep  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     23       1.1    cjep  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24       1.1    cjep  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25       1.1    cjep  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26       1.1    cjep  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27       1.1    cjep  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28       1.1    cjep  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29       1.1    cjep  * SUCH DAMAGE.
     30       1.1    cjep  */
     31       1.2    cjep 
     32       1.2    cjep #include <sys/cdefs.h>
     33  1.3.40.1  bouyer __RCSID("$NetBSD: grep.c,v 1.3.40.1 2011/02/17 12:00:55 bouyer Exp $");
     34       1.1    cjep 
     35       1.1    cjep #include <sys/stat.h>
     36  1.3.40.1  bouyer #include <sys/types.h>
     37       1.1    cjep 
     38  1.3.40.1  bouyer #include <ctype.h>
     39       1.1    cjep #include <err.h>
     40       1.1    cjep #include <errno.h>
     41       1.1    cjep #include <getopt.h>
     42       1.2    cjep #include <limits.h>
     43  1.3.40.1  bouyer #include <libgen.h>
     44  1.3.40.1  bouyer #include <locale.h>
     45  1.3.40.1  bouyer #include <stdbool.h>
     46       1.1    cjep #include <stdio.h>
     47       1.1    cjep #include <stdlib.h>
     48       1.1    cjep #include <string.h>
     49       1.1    cjep #include <unistd.h>
     50       1.1    cjep 
     51       1.1    cjep #include "grep.h"
     52       1.1    cjep 
     53  1.3.40.1  bouyer #ifndef WITHOUT_NLS
     54  1.3.40.1  bouyer #include <nl_types.h>
     55  1.3.40.1  bouyer nl_catd	 catalog;
     56  1.3.40.1  bouyer #endif
     57  1.3.40.1  bouyer 
     58  1.3.40.1  bouyer /*
     59  1.3.40.1  bouyer  * Default messags to use when NLS is disabled or no catalogue
     60  1.3.40.1  bouyer  * is found.
     61       1.2    cjep  */
     62  1.3.40.1  bouyer const char	*errstr[] = {
     63  1.3.40.1  bouyer 	"",
     64  1.3.40.1  bouyer /* 1*/	"(standard input)",
     65  1.3.40.1  bouyer /* 2*/	"cannot read bzip2 compressed file",
     66  1.3.40.1  bouyer /* 3*/	"unknown %s option",
     67  1.3.40.1  bouyer /* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
     68  1.3.40.1  bouyer /* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
     69  1.3.40.1  bouyer /* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
     70  1.3.40.1  bouyer /* 7*/	"\t[--null] [pattern] [file ...]\n",
     71  1.3.40.1  bouyer /* 8*/	"Binary file %s matches\n",
     72  1.3.40.1  bouyer /* 9*/	"%s (BSD grep) %s\n",
     73  1.3.40.1  bouyer };
     74       1.2    cjep 
     75       1.1    cjep /* Flags passed to regcomp() and regexec() */
     76  1.3.40.1  bouyer int		 cflags = 0;
     77  1.3.40.1  bouyer int		 eflags = REG_STARTEND;
     78  1.3.40.1  bouyer 
     79  1.3.40.1  bouyer /* Shortcut for matching all cases like empty regex */
     80  1.3.40.1  bouyer bool		 matchall;
     81       1.1    cjep 
     82  1.3.40.1  bouyer /* Searching patterns */
     83  1.3.40.1  bouyer unsigned int	 patterns, pattern_sz;
     84  1.3.40.1  bouyer char		**pattern;
     85  1.3.40.1  bouyer regex_t		*r_pattern;
     86  1.3.40.1  bouyer fastgrep_t	*fg_pattern;
     87  1.3.40.1  bouyer 
     88  1.3.40.1  bouyer /* Filename exclusion/inclusion patterns */
     89  1.3.40.1  bouyer unsigned int	 fpatterns, fpattern_sz;
     90  1.3.40.1  bouyer unsigned int	 dpatterns, dpattern_sz;
     91  1.3.40.1  bouyer struct epat	*dpattern, *fpattern;
     92       1.1    cjep 
     93       1.1    cjep /* For regex errors  */
     94  1.3.40.1  bouyer char	 re_error[RE_ERROR_BUF + 1];
     95       1.1    cjep 
     96       1.1    cjep /* Command-line flags */
     97  1.3.40.1  bouyer unsigned long long Aflag;	/* -A x: print x lines trailing each match */
     98  1.3.40.1  bouyer unsigned long long Bflag;	/* -B x: print x lines leading each match */
     99  1.3.40.1  bouyer bool	 Hflag;		/* -H: always print file name */
    100  1.3.40.1  bouyer bool	 Lflag;		/* -L: only show names of files with no matches */
    101  1.3.40.1  bouyer bool	 bflag;		/* -b: show block numbers for each match */
    102  1.3.40.1  bouyer bool	 cflag;		/* -c: only show a count of matching lines */
    103  1.3.40.1  bouyer bool	 hflag;		/* -h: don't print filename headers */
    104  1.3.40.1  bouyer bool	 iflag;		/* -i: ignore case */
    105  1.3.40.1  bouyer bool	 lflag;		/* -l: only show names of files with matches */
    106  1.3.40.1  bouyer bool	 mflag;		/* -m x: stop reading the files after x matches */
    107  1.3.40.1  bouyer unsigned long long mcount;	/* count for -m */
    108  1.3.40.1  bouyer bool	 nflag;		/* -n: show line numbers in front of matching lines */
    109  1.3.40.1  bouyer bool	 oflag;		/* -o: print only matching part */
    110  1.3.40.1  bouyer bool	 qflag;		/* -q: quiet mode (don't output anything) */
    111  1.3.40.1  bouyer bool	 sflag;		/* -s: silent mode (ignore errors) */
    112  1.3.40.1  bouyer bool	 vflag;		/* -v: only show non-matching lines */
    113  1.3.40.1  bouyer bool	 wflag;		/* -w: pattern must start and end on word boundaries */
    114  1.3.40.1  bouyer bool	 xflag;		/* -x: pattern must match entire line */
    115  1.3.40.1  bouyer bool	 lbflag;	/* --line-buffered */
    116  1.3.40.1  bouyer bool	 nullflag;	/* --null */
    117  1.3.40.1  bouyer char	*label;		/* --label */
    118  1.3.40.1  bouyer const char *color;	/* --color */
    119  1.3.40.1  bouyer int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
    120  1.3.40.1  bouyer int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
    121  1.3.40.1  bouyer int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
    122  1.3.40.1  bouyer int	 devbehave = DEV_READ;		/* -D: handling of devices */
    123  1.3.40.1  bouyer int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
    124  1.3.40.1  bouyer int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
    125  1.3.40.1  bouyer 
    126  1.3.40.1  bouyer bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
    127  1.3.40.1  bouyer bool	 fexclude, finclude;	/* --exclude and --include */
    128       1.2    cjep 
    129       1.2    cjep enum {
    130       1.2    cjep 	BIN_OPT = CHAR_MAX + 1,
    131  1.3.40.1  bouyer 	COLOR_OPT,
    132       1.2    cjep 	HELP_OPT,
    133       1.2    cjep 	MMAP_OPT,
    134  1.3.40.1  bouyer 	LINEBUF_OPT,
    135  1.3.40.1  bouyer 	LABEL_OPT,
    136  1.3.40.1  bouyer 	NULL_OPT,
    137  1.3.40.1  bouyer 	R_EXCLUDE_OPT,
    138  1.3.40.1  bouyer 	R_INCLUDE_OPT,
    139  1.3.40.1  bouyer 	R_DEXCLUDE_OPT,
    140  1.3.40.1  bouyer 	R_DINCLUDE_OPT
    141       1.2    cjep };
    142       1.1    cjep 
    143  1.3.40.1  bouyer static inline const char	*init_color(const char *);
    144  1.3.40.1  bouyer 
    145       1.1    cjep /* Housekeeping */
    146  1.3.40.1  bouyer bool	 first = true;	/* flag whether we are processing the first match */
    147  1.3.40.1  bouyer bool	 prev;		/* flag whether or not the previous line matched */
    148  1.3.40.1  bouyer int	 tail;		/* lines left to print */
    149  1.3.40.1  bouyer bool	 notfound;	/* file not found */
    150  1.3.40.1  bouyer 
    151  1.3.40.1  bouyer extern char	*__progname;
    152       1.1    cjep 
    153  1.3.40.1  bouyer /*
    154  1.3.40.1  bouyer  * Prints usage information and returns 2.
    155  1.3.40.1  bouyer  */
    156       1.1    cjep static void
    157       1.1    cjep usage(void)
    158       1.1    cjep {
    159  1.3.40.1  bouyer 	fprintf(stderr, getstr(4), __progname);
    160  1.3.40.1  bouyer 	fprintf(stderr, "%s", getstr(5));
    161  1.3.40.1  bouyer 	fprintf(stderr, "%s", getstr(5));
    162  1.3.40.1  bouyer 	fprintf(stderr, "%s", getstr(6));
    163  1.3.40.1  bouyer 	fprintf(stderr, "%s", getstr(7));
    164       1.1    cjep 	exit(2);
    165       1.1    cjep }
    166       1.1    cjep 
    167  1.3.40.1  bouyer static const char	*optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
    168       1.1    cjep 
    169       1.2    cjep struct option long_options[] =
    170       1.1    cjep {
    171  1.3.40.1  bouyer 	{"binary-files",	required_argument,	NULL, BIN_OPT},
    172  1.3.40.1  bouyer 	{"help",		no_argument,		NULL, HELP_OPT},
    173  1.3.40.1  bouyer 	{"mmap",		no_argument,		NULL, MMAP_OPT},
    174  1.3.40.1  bouyer 	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
    175  1.3.40.1  bouyer 	{"label",		required_argument,	NULL, LABEL_OPT},
    176  1.3.40.1  bouyer 	{"null",		no_argument,		NULL, NULL_OPT},
    177  1.3.40.1  bouyer 	{"color",		optional_argument,	NULL, COLOR_OPT},
    178  1.3.40.1  bouyer 	{"colour",		optional_argument,	NULL, COLOR_OPT},
    179  1.3.40.1  bouyer 	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
    180  1.3.40.1  bouyer 	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
    181  1.3.40.1  bouyer 	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
    182  1.3.40.1  bouyer 	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
    183  1.3.40.1  bouyer 	{"after-context",	required_argument,	NULL, 'A'},
    184  1.3.40.1  bouyer 	{"text",		no_argument,		NULL, 'a'},
    185  1.3.40.1  bouyer 	{"before-context",	required_argument,	NULL, 'B'},
    186  1.3.40.1  bouyer 	{"byte-offset",		no_argument,		NULL, 'b'},
    187  1.3.40.1  bouyer 	{"context",		optional_argument,	NULL, 'C'},
    188  1.3.40.1  bouyer 	{"count",		no_argument,		NULL, 'c'},
    189  1.3.40.1  bouyer 	{"devices",		required_argument,	NULL, 'D'},
    190  1.3.40.1  bouyer         {"directories",		required_argument,	NULL, 'd'},
    191  1.3.40.1  bouyer 	{"extended-regexp",	no_argument,		NULL, 'E'},
    192  1.3.40.1  bouyer 	{"regexp",		required_argument,	NULL, 'e'},
    193  1.3.40.1  bouyer 	{"fixed-strings",	no_argument,		NULL, 'F'},
    194  1.3.40.1  bouyer 	{"file",		required_argument,	NULL, 'f'},
    195  1.3.40.1  bouyer 	{"basic-regexp",	no_argument,		NULL, 'G'},
    196  1.3.40.1  bouyer 	{"no-filename",		no_argument,		NULL, 'h'},
    197  1.3.40.1  bouyer 	{"with-filename",	no_argument,		NULL, 'H'},
    198  1.3.40.1  bouyer 	{"ignore-case",		no_argument,		NULL, 'i'},
    199  1.3.40.1  bouyer 	{"bz2decompress",	no_argument,		NULL, 'J'},
    200  1.3.40.1  bouyer 	{"files-with-matches",	no_argument,		NULL, 'l'},
    201  1.3.40.1  bouyer 	{"files-without-match", no_argument,            NULL, 'L'},
    202  1.3.40.1  bouyer 	{"max-count",		required_argument,	NULL, 'm'},
    203  1.3.40.1  bouyer 	{"line-number",		no_argument,		NULL, 'n'},
    204  1.3.40.1  bouyer 	{"only-matching",	no_argument,		NULL, 'o'},
    205  1.3.40.1  bouyer 	{"quiet",		no_argument,		NULL, 'q'},
    206  1.3.40.1  bouyer 	{"silent",		no_argument,		NULL, 'q'},
    207  1.3.40.1  bouyer 	{"recursive",		no_argument,		NULL, 'r'},
    208  1.3.40.1  bouyer 	{"no-messages",		no_argument,		NULL, 's'},
    209  1.3.40.1  bouyer 	{"binary",		no_argument,		NULL, 'U'},
    210  1.3.40.1  bouyer 	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
    211  1.3.40.1  bouyer 	{"invert-match",	no_argument,		NULL, 'v'},
    212  1.3.40.1  bouyer 	{"version",		no_argument,		NULL, 'V'},
    213  1.3.40.1  bouyer 	{"word-regexp",		no_argument,		NULL, 'w'},
    214  1.3.40.1  bouyer 	{"line-regexp",		no_argument,		NULL, 'x'},
    215  1.3.40.1  bouyer 	{"decompress",          no_argument,            NULL, 'Z'},
    216  1.3.40.1  bouyer 	{NULL,			no_argument,		NULL, 0}
    217       1.1    cjep };
    218       1.1    cjep 
    219  1.3.40.1  bouyer /*
    220  1.3.40.1  bouyer  * Adds a searching pattern to the internal array.
    221  1.3.40.1  bouyer  */
    222       1.1    cjep static void
    223       1.1    cjep add_pattern(char *pat, size_t len)
    224       1.1    cjep {
    225  1.3.40.1  bouyer 
    226  1.3.40.1  bouyer 	/* Check if we can do a shortcut */
    227       1.1    cjep 	if (len == 0 || matchall) {
    228  1.3.40.1  bouyer 		matchall = true;
    229       1.1    cjep 		return;
    230       1.1    cjep 	}
    231  1.3.40.1  bouyer 	/* Increase size if necessary */
    232       1.1    cjep 	if (patterns == pattern_sz) {
    233       1.1    cjep 		pattern_sz *= 2;
    234  1.3.40.1  bouyer 		pattern = grep_realloc(pattern, ++pattern_sz *
    235  1.3.40.1  bouyer 		    sizeof(*pattern));
    236       1.1    cjep 	}
    237  1.3.40.1  bouyer 	if (len > 0 && pat[len - 1] == '\n')
    238       1.1    cjep 		--len;
    239  1.3.40.1  bouyer 	/* pat may not be NUL-terminated */
    240       1.2    cjep 	pattern[patterns] = grep_malloc(len + 1);
    241  1.3.40.1  bouyer 	memcpy(pattern[patterns], pat, len);
    242       1.1    cjep 	pattern[patterns][len] = '\0';
    243       1.1    cjep 	++patterns;
    244       1.1    cjep }
    245       1.1    cjep 
    246  1.3.40.1  bouyer /*
    247  1.3.40.1  bouyer  * Adds a file include/exclude pattern to the internal array.
    248  1.3.40.1  bouyer  */
    249       1.1    cjep static void
    250  1.3.40.1  bouyer add_fpattern(const char *pat, int mode)
    251  1.3.40.1  bouyer {
    252  1.3.40.1  bouyer 
    253  1.3.40.1  bouyer 	/* Increase size if necessary */
    254  1.3.40.1  bouyer 	if (fpatterns == fpattern_sz) {
    255  1.3.40.1  bouyer 		fpattern_sz *= 2;
    256  1.3.40.1  bouyer 		fpattern = grep_realloc(fpattern, ++fpattern_sz *
    257  1.3.40.1  bouyer 		    sizeof(struct epat));
    258  1.3.40.1  bouyer 	}
    259  1.3.40.1  bouyer 	fpattern[fpatterns].pat = grep_strdup(pat);
    260  1.3.40.1  bouyer 	fpattern[fpatterns].mode = mode;
    261  1.3.40.1  bouyer 	++fpatterns;
    262  1.3.40.1  bouyer }
    263  1.3.40.1  bouyer 
    264  1.3.40.1  bouyer /*
    265  1.3.40.1  bouyer  * Adds a directory include/exclude pattern to the internal array.
    266  1.3.40.1  bouyer  */
    267  1.3.40.1  bouyer static void
    268  1.3.40.1  bouyer add_dpattern(const char *pat, int mode)
    269  1.3.40.1  bouyer {
    270  1.3.40.1  bouyer 
    271  1.3.40.1  bouyer 	/* Increase size if necessary */
    272  1.3.40.1  bouyer 	if (dpatterns == dpattern_sz) {
    273  1.3.40.1  bouyer 		dpattern_sz *= 2;
    274  1.3.40.1  bouyer 		dpattern = grep_realloc(dpattern, ++dpattern_sz *
    275  1.3.40.1  bouyer 		    sizeof(struct epat));
    276  1.3.40.1  bouyer 	}
    277  1.3.40.1  bouyer 	dpattern[dpatterns].pat = grep_strdup(pat);
    278  1.3.40.1  bouyer 	dpattern[dpatterns].mode = mode;
    279  1.3.40.1  bouyer 	++dpatterns;
    280  1.3.40.1  bouyer }
    281  1.3.40.1  bouyer 
    282  1.3.40.1  bouyer /*
    283  1.3.40.1  bouyer  * Reads searching patterns from a file and adds them with add_pattern().
    284  1.3.40.1  bouyer  */
    285  1.3.40.1  bouyer static void
    286  1.3.40.1  bouyer read_patterns(const char *fn)
    287       1.1    cjep {
    288       1.1    cjep 	FILE *f;
    289       1.1    cjep 	char *line;
    290       1.1    cjep 	size_t len;
    291       1.1    cjep 
    292       1.1    cjep 	if ((f = fopen(fn, "r")) == NULL)
    293       1.2    cjep 		err(2, "%s", fn);
    294  1.3.40.1  bouyer 	while ((line = fgetln(f, &len)) != NULL)
    295  1.3.40.1  bouyer 		add_pattern(line, *line == '\n' ? 0 : len);
    296       1.1    cjep 	if (ferror(f))
    297       1.2    cjep 		err(2, "%s", fn);
    298       1.1    cjep 	fclose(f);
    299       1.1    cjep }
    300       1.1    cjep 
    301  1.3.40.1  bouyer static inline const char *
    302  1.3.40.1  bouyer init_color(const char *d)
    303       1.2    cjep {
    304  1.3.40.1  bouyer 	char *c;
    305       1.2    cjep 
    306  1.3.40.1  bouyer 	c = getenv("GREP_COLOR");
    307  1.3.40.1  bouyer 	return (c != NULL ? c : d);
    308       1.2    cjep }
    309       1.2    cjep 
    310       1.1    cjep int
    311       1.1    cjep main(int argc, char *argv[])
    312       1.1    cjep {
    313  1.3.40.1  bouyer 	char **aargv, **eargv, *eopts;
    314  1.3.40.1  bouyer 	char *ep;
    315  1.3.40.1  bouyer 	unsigned long long l;
    316  1.3.40.1  bouyer 	unsigned int aargc, eargc, i;
    317  1.3.40.1  bouyer 	int c, lastc, needpattern, newarg, prevoptind;
    318  1.3.40.1  bouyer 
    319  1.3.40.1  bouyer 	setlocale(LC_ALL, "");
    320  1.3.40.1  bouyer 
    321  1.3.40.1  bouyer #ifndef WITHOUT_NLS
    322  1.3.40.1  bouyer 	catalog = catopen("grep", NL_CAT_LOCALE);
    323  1.3.40.1  bouyer #endif
    324  1.3.40.1  bouyer 
    325  1.3.40.1  bouyer 	/* Check what is the program name of the binary.  In this
    326  1.3.40.1  bouyer 	   way we can have all the funcionalities in one binary
    327  1.3.40.1  bouyer 	   without the need of scripting and using ugly hacks. */
    328  1.3.40.1  bouyer 	switch (__progname[0]) {
    329       1.2    cjep 	case 'e':
    330  1.3.40.1  bouyer 		grepbehave = GREP_EXTENDED;
    331       1.2    cjep 		break;
    332       1.2    cjep 	case 'f':
    333  1.3.40.1  bouyer 		grepbehave = GREP_FIXED;
    334       1.2    cjep 		break;
    335       1.2    cjep 	case 'g':
    336  1.3.40.1  bouyer 		grepbehave = GREP_BASIC;
    337       1.2    cjep 		break;
    338       1.2    cjep 	case 'z':
    339  1.3.40.1  bouyer 		filebehave = FILE_GZIP;
    340  1.3.40.1  bouyer 		switch(__progname[1]) {
    341       1.2    cjep 		case 'e':
    342  1.3.40.1  bouyer 			grepbehave = GREP_EXTENDED;
    343       1.2    cjep 			break;
    344       1.2    cjep 		case 'f':
    345  1.3.40.1  bouyer 			grepbehave = GREP_FIXED;
    346       1.2    cjep 			break;
    347       1.2    cjep 		case 'g':
    348  1.3.40.1  bouyer 			grepbehave = GREP_BASIC;
    349       1.2    cjep 			break;
    350       1.2    cjep 		}
    351       1.2    cjep 		break;
    352       1.2    cjep 	}
    353       1.1    cjep 
    354  1.3.40.1  bouyer 	lastc = '\0';
    355  1.3.40.1  bouyer 	newarg = 1;
    356  1.3.40.1  bouyer 	prevoptind = 1;
    357  1.3.40.1  bouyer 	needpattern = 1;
    358  1.3.40.1  bouyer 
    359  1.3.40.1  bouyer 	eopts = getenv("GREP_OPTIONS");
    360  1.3.40.1  bouyer 
    361  1.3.40.1  bouyer 	/* support for extra arguments in GREP_OPTIONS */
    362  1.3.40.1  bouyer 	eargc = 0;
    363  1.3.40.1  bouyer 	if (eopts != NULL) {
    364  1.3.40.1  bouyer 		char *str;
    365  1.3.40.1  bouyer 
    366  1.3.40.1  bouyer 		/* make an estimation of how many extra arguments we have */
    367  1.3.40.1  bouyer 		for (unsigned int j = 0; j < strlen(eopts); j++)
    368  1.3.40.1  bouyer 			if (eopts[j] == ' ')
    369  1.3.40.1  bouyer 				eargc++;
    370  1.3.40.1  bouyer 
    371  1.3.40.1  bouyer 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
    372  1.3.40.1  bouyer 
    373  1.3.40.1  bouyer 		eargc = 0;
    374  1.3.40.1  bouyer 		/* parse extra arguments */
    375  1.3.40.1  bouyer 		while ((str = strsep(&eopts, " ")) != NULL)
    376  1.3.40.1  bouyer 			eargv[eargc++] = grep_strdup(str);
    377  1.3.40.1  bouyer 
    378  1.3.40.1  bouyer 		aargv = (char **)grep_calloc(eargc + argc + 1,
    379  1.3.40.1  bouyer 		    sizeof(char *));
    380  1.3.40.1  bouyer 
    381  1.3.40.1  bouyer 		aargv[0] = argv[0];
    382  1.3.40.1  bouyer 		for (i = 0; i < eargc; i++)
    383  1.3.40.1  bouyer 			aargv[i + 1] = eargv[i];
    384  1.3.40.1  bouyer 		for (int j = 1; j < argc; j++, i++)
    385  1.3.40.1  bouyer 			aargv[i + 1] = argv[j];
    386  1.3.40.1  bouyer 
    387  1.3.40.1  bouyer 		aargc = eargc + argc;
    388  1.3.40.1  bouyer 	} else {
    389  1.3.40.1  bouyer 		aargv = argv;
    390  1.3.40.1  bouyer 		aargc = argc;
    391  1.3.40.1  bouyer 	}
    392       1.2    cjep 
    393  1.3.40.1  bouyer 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
    394  1.3.40.1  bouyer 	    -1)) {
    395  1.3.40.1  bouyer 		switch (c) {
    396  1.3.40.1  bouyer 		case '0': case '1': case '2': case '3': case '4':
    397  1.3.40.1  bouyer 		case '5': case '6': case '7': case '8': case '9':
    398  1.3.40.1  bouyer 			if (newarg || !isdigit(lastc))
    399  1.3.40.1  bouyer 				Aflag = 0;
    400  1.3.40.1  bouyer 			else if (Aflag > LLONG_MAX / 10) {
    401  1.3.40.1  bouyer 				errno = ERANGE;
    402  1.3.40.1  bouyer 				err(2, NULL);
    403  1.3.40.1  bouyer 			}
    404  1.3.40.1  bouyer 			Aflag = Bflag = (Aflag * 10) + (c - '0');
    405       1.1    cjep 			break;
    406       1.1    cjep 		case 'C':
    407  1.3.40.1  bouyer 			if (optarg == NULL) {
    408       1.1    cjep 				Aflag = Bflag = 2;
    409  1.3.40.1  bouyer 				break;
    410       1.2    cjep 			}
    411  1.3.40.1  bouyer 			/* FALLTHROUGH */
    412  1.3.40.1  bouyer 		case 'A':
    413  1.3.40.1  bouyer 			/* FALLTHROUGH */
    414  1.3.40.1  bouyer 		case 'B':
    415  1.3.40.1  bouyer 			errno = 0;
    416  1.3.40.1  bouyer 			l = strtoull(optarg, &ep, 10);
    417  1.3.40.1  bouyer 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
    418  1.3.40.1  bouyer 			    ((errno == EINVAL) && (l == 0)))
    419  1.3.40.1  bouyer 				err(2, NULL);
    420  1.3.40.1  bouyer 			else if (ep[0] != '\0') {
    421  1.3.40.1  bouyer 				errno = EINVAL;
    422  1.3.40.1  bouyer 				err(2, NULL);
    423  1.3.40.1  bouyer 			}
    424  1.3.40.1  bouyer 			if (c == 'A')
    425  1.3.40.1  bouyer 				Aflag = l;
    426  1.3.40.1  bouyer 			else if (c == 'B')
    427  1.3.40.1  bouyer 				Bflag = l;
    428  1.3.40.1  bouyer 			else
    429  1.3.40.1  bouyer 				Aflag = Bflag = l;
    430       1.1    cjep 			break;
    431       1.1    cjep 		case 'a':
    432  1.3.40.1  bouyer 			binbehave = BINFILE_TEXT;
    433       1.1    cjep 			break;
    434       1.1    cjep 		case 'b':
    435  1.3.40.1  bouyer 			bflag = true;
    436       1.1    cjep 			break;
    437       1.1    cjep 		case 'c':
    438  1.3.40.1  bouyer 			cflag = true;
    439  1.3.40.1  bouyer 			break;
    440  1.3.40.1  bouyer 		case 'D':
    441  1.3.40.1  bouyer 			if (strcasecmp(optarg, "skip") == 0)
    442  1.3.40.1  bouyer 				devbehave = DEV_SKIP;
    443  1.3.40.1  bouyer 			else if (strcasecmp(optarg, "read") == 0)
    444  1.3.40.1  bouyer 				devbehave = DEV_READ;
    445  1.3.40.1  bouyer 			else
    446  1.3.40.1  bouyer 				errx(2, getstr(3), "--devices");
    447       1.1    cjep 			break;
    448       1.2    cjep 		case 'd':
    449  1.3.40.1  bouyer 			if (strcasecmp("recurse", optarg) == 0) {
    450  1.3.40.1  bouyer 				Hflag = true;
    451  1.3.40.1  bouyer 				dirbehave = DIR_RECURSE;
    452  1.3.40.1  bouyer 			} else if (strcasecmp("skip", optarg) == 0)
    453  1.3.40.1  bouyer 				dirbehave = DIR_SKIP;
    454  1.3.40.1  bouyer 			else if (strcasecmp("read", optarg) == 0)
    455  1.3.40.1  bouyer 				dirbehave = DIR_READ;
    456  1.3.40.1  bouyer 			else
    457  1.3.40.1  bouyer 				errx(2, getstr(3), "--directories");
    458  1.3.40.1  bouyer 			break;
    459  1.3.40.1  bouyer 		case 'E':
    460  1.3.40.1  bouyer 			grepbehave = GREP_EXTENDED;
    461       1.2    cjep 			break;
    462       1.1    cjep 		case 'e':
    463       1.1    cjep 			add_pattern(optarg, strlen(optarg));
    464  1.3.40.1  bouyer 			needpattern = 0;
    465  1.3.40.1  bouyer 			break;
    466  1.3.40.1  bouyer 		case 'F':
    467  1.3.40.1  bouyer 			grepbehave = GREP_FIXED;
    468       1.1    cjep 			break;
    469       1.1    cjep 		case 'f':
    470       1.1    cjep 			read_patterns(optarg);
    471  1.3.40.1  bouyer 			needpattern = 0;
    472  1.3.40.1  bouyer 			break;
    473  1.3.40.1  bouyer 		case 'G':
    474  1.3.40.1  bouyer 			grepbehave = GREP_BASIC;
    475  1.3.40.1  bouyer 			break;
    476  1.3.40.1  bouyer 		case 'H':
    477  1.3.40.1  bouyer 			Hflag = true;
    478       1.1    cjep 			break;
    479       1.1    cjep 		case 'h':
    480  1.3.40.1  bouyer 			Hflag = false;
    481  1.3.40.1  bouyer 			hflag = true;
    482  1.3.40.1  bouyer 			break;
    483  1.3.40.1  bouyer 		case 'I':
    484  1.3.40.1  bouyer 			binbehave = BINFILE_SKIP;
    485       1.1    cjep 			break;
    486       1.1    cjep 		case 'i':
    487       1.1    cjep 		case 'y':
    488  1.3.40.1  bouyer 			iflag =  true;
    489       1.1    cjep 			cflags |= REG_ICASE;
    490       1.1    cjep 			break;
    491  1.3.40.1  bouyer 		case 'J':
    492  1.3.40.1  bouyer 			filebehave = FILE_BZIP;
    493  1.3.40.1  bouyer 			break;
    494  1.3.40.1  bouyer 		case 'L':
    495  1.3.40.1  bouyer 			lflag = false;
    496  1.3.40.1  bouyer 			Lflag = true;
    497  1.3.40.1  bouyer 			break;
    498       1.1    cjep 		case 'l':
    499  1.3.40.1  bouyer 			Lflag = false;
    500  1.3.40.1  bouyer 			lflag = true;
    501       1.1    cjep 			break;
    502       1.2    cjep 		case 'm':
    503  1.3.40.1  bouyer 			mflag = true;
    504  1.3.40.1  bouyer 			errno = 0;
    505  1.3.40.1  bouyer 			mcount = strtoull(optarg, &ep, 10);
    506  1.3.40.1  bouyer 			if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
    507  1.3.40.1  bouyer 			    ((errno == EINVAL) && (mcount == 0)))
    508  1.3.40.1  bouyer 				err(2, NULL);
    509  1.3.40.1  bouyer 			else if (ep[0] != '\0') {
    510  1.3.40.1  bouyer 				errno = EINVAL;
    511  1.3.40.1  bouyer 				err(2, NULL);
    512  1.3.40.1  bouyer 			}
    513       1.2    cjep 			break;
    514       1.1    cjep 		case 'n':
    515  1.3.40.1  bouyer 			nflag = true;
    516  1.3.40.1  bouyer 			break;
    517  1.3.40.1  bouyer 		case 'O':
    518  1.3.40.1  bouyer 			linkbehave = LINK_EXPLICIT;
    519       1.1    cjep 			break;
    520       1.1    cjep 		case 'o':
    521  1.3.40.1  bouyer 			oflag = true;
    522  1.3.40.1  bouyer 			break;
    523  1.3.40.1  bouyer 		case 'p':
    524  1.3.40.1  bouyer 			linkbehave = LINK_SKIP;
    525       1.1    cjep 			break;
    526       1.1    cjep 		case 'q':
    527  1.3.40.1  bouyer 			qflag = true;
    528  1.3.40.1  bouyer 			break;
    529  1.3.40.1  bouyer 		case 'S':
    530  1.3.40.1  bouyer 			linkbehave = LINK_READ;
    531  1.3.40.1  bouyer 			break;
    532  1.3.40.1  bouyer 		case 'R':
    533  1.3.40.1  bouyer 		case 'r':
    534  1.3.40.1  bouyer 			dirbehave = DIR_RECURSE;
    535  1.3.40.1  bouyer 			Hflag = true;
    536       1.1    cjep 			break;
    537       1.1    cjep 		case 's':
    538  1.3.40.1  bouyer 			sflag = true;
    539  1.3.40.1  bouyer 			break;
    540  1.3.40.1  bouyer 		case 'U':
    541  1.3.40.1  bouyer 			binbehave = BINFILE_BIN;
    542       1.1    cjep 			break;
    543  1.3.40.1  bouyer 		case 'u':
    544  1.3.40.1  bouyer 		case MMAP_OPT:
    545  1.3.40.1  bouyer 			/* noop, compatibility */
    546  1.3.40.1  bouyer 			break;
    547  1.3.40.1  bouyer 		case 'V':
    548  1.3.40.1  bouyer 			printf(getstr(9), __progname, VERSION);
    549  1.3.40.1  bouyer 			exit(0);
    550       1.1    cjep 		case 'v':
    551  1.3.40.1  bouyer 			vflag = true;
    552       1.1    cjep 			break;
    553       1.1    cjep 		case 'w':
    554  1.3.40.1  bouyer 			wflag = true;
    555       1.1    cjep 			break;
    556       1.1    cjep 		case 'x':
    557  1.3.40.1  bouyer 			xflag = true;
    558       1.1    cjep 			break;
    559  1.3.40.1  bouyer 		case 'Z':
    560  1.3.40.1  bouyer 			filebehave = FILE_GZIP;
    561       1.2    cjep 			break;
    562       1.2    cjep 		case BIN_OPT:
    563  1.3.40.1  bouyer 			if (strcasecmp("binary", optarg) == 0)
    564  1.3.40.1  bouyer 				binbehave = BINFILE_BIN;
    565  1.3.40.1  bouyer 			else if (strcasecmp("without-match", optarg) == 0)
    566  1.3.40.1  bouyer 				binbehave = BINFILE_SKIP;
    567  1.3.40.1  bouyer 			else if (strcasecmp("text", optarg) == 0)
    568  1.3.40.1  bouyer 				binbehave = BINFILE_TEXT;
    569  1.3.40.1  bouyer 			else
    570  1.3.40.1  bouyer 				errx(2, getstr(3), "--binary-files");
    571       1.2    cjep 			break;
    572  1.3.40.1  bouyer 		case COLOR_OPT:
    573  1.3.40.1  bouyer 			color = NULL;
    574  1.3.40.1  bouyer 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
    575  1.3.40.1  bouyer 			    strcasecmp("tty", optarg) == 0 ||
    576  1.3.40.1  bouyer 			    strcasecmp("if-tty", optarg) == 0) {
    577  1.3.40.1  bouyer 				char *term;
    578  1.3.40.1  bouyer 
    579  1.3.40.1  bouyer 				term = getenv("TERM");
    580  1.3.40.1  bouyer 				if (isatty(STDOUT_FILENO) && term != NULL &&
    581  1.3.40.1  bouyer 				    strcasecmp(term, "dumb") != 0)
    582  1.3.40.1  bouyer 					color = init_color("01;31");
    583  1.3.40.1  bouyer 			} else if (strcasecmp("always", optarg) == 0 ||
    584  1.3.40.1  bouyer 			    strcasecmp("yes", optarg) == 0 ||
    585  1.3.40.1  bouyer 			    strcasecmp("force", optarg) == 0) {
    586  1.3.40.1  bouyer 				color = init_color("01;31");
    587  1.3.40.1  bouyer 			} else if (strcasecmp("never", optarg) != 0 &&
    588  1.3.40.1  bouyer 			    strcasecmp("none", optarg) != 0 &&
    589  1.3.40.1  bouyer 			    strcasecmp("no", optarg) != 0)
    590  1.3.40.1  bouyer 				errx(2, getstr(3), "--color");
    591       1.2    cjep 			break;
    592  1.3.40.1  bouyer 		case LABEL_OPT:
    593  1.3.40.1  bouyer 			label = optarg;
    594       1.2    cjep 			break;
    595       1.2    cjep 		case LINEBUF_OPT:
    596  1.3.40.1  bouyer 			lbflag = true;
    597  1.3.40.1  bouyer 			break;
    598  1.3.40.1  bouyer 		case NULL_OPT:
    599  1.3.40.1  bouyer 			nullflag = true;
    600  1.3.40.1  bouyer 			break;
    601  1.3.40.1  bouyer 		case R_INCLUDE_OPT:
    602  1.3.40.1  bouyer 			finclude = true;
    603  1.3.40.1  bouyer 			add_fpattern(optarg, INCL_PAT);
    604  1.3.40.1  bouyer 			break;
    605  1.3.40.1  bouyer 		case R_EXCLUDE_OPT:
    606  1.3.40.1  bouyer 			fexclude = true;
    607  1.3.40.1  bouyer 			add_fpattern(optarg, EXCL_PAT);
    608  1.3.40.1  bouyer 			break;
    609  1.3.40.1  bouyer 		case R_DINCLUDE_OPT:
    610  1.3.40.1  bouyer 			dinclude = true;
    611  1.3.40.1  bouyer 			add_dpattern(optarg, INCL_PAT);
    612  1.3.40.1  bouyer 			break;
    613  1.3.40.1  bouyer 		case R_DEXCLUDE_OPT:
    614  1.3.40.1  bouyer 			dexclude = true;
    615  1.3.40.1  bouyer 			add_dpattern(optarg, EXCL_PAT);
    616       1.2    cjep 			break;
    617       1.2    cjep 		case HELP_OPT:
    618       1.1    cjep 		default:
    619       1.1    cjep 			usage();
    620       1.1    cjep 		}
    621  1.3.40.1  bouyer 		lastc = c;
    622  1.3.40.1  bouyer 		newarg = optind != prevoptind;
    623  1.3.40.1  bouyer 		prevoptind = optind;
    624       1.1    cjep 	}
    625  1.3.40.1  bouyer 	aargc -= optind;
    626  1.3.40.1  bouyer 	aargv += optind;
    627       1.1    cjep 
    628  1.3.40.1  bouyer 	/* Fail if we don't have any pattern */
    629  1.3.40.1  bouyer 	if (aargc == 0 && needpattern)
    630       1.1    cjep 		usage();
    631  1.3.40.1  bouyer 
    632  1.3.40.1  bouyer 	/* Process patterns from command line */
    633  1.3.40.1  bouyer 	if (aargc != 0 && needpattern) {
    634  1.3.40.1  bouyer 		add_pattern(*aargv, strlen(*aargv));
    635  1.3.40.1  bouyer 		--aargc;
    636  1.3.40.1  bouyer 		++aargv;
    637       1.1    cjep 	}
    638       1.1    cjep 
    639  1.3.40.1  bouyer 	switch (grepbehave) {
    640  1.3.40.1  bouyer 	case GREP_FIXED:
    641  1.3.40.1  bouyer 	case GREP_BASIC:
    642  1.3.40.1  bouyer 		break;
    643  1.3.40.1  bouyer 	case GREP_EXTENDED:
    644       1.2    cjep 		cflags |= REG_EXTENDED;
    645  1.3.40.1  bouyer 		break;
    646  1.3.40.1  bouyer 	default:
    647  1.3.40.1  bouyer 		/* NOTREACHED */
    648  1.3.40.1  bouyer 		usage();
    649       1.1    cjep 	}
    650       1.1    cjep 
    651  1.3.40.1  bouyer 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
    652  1.3.40.1  bouyer 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
    653  1.3.40.1  bouyer /*
    654  1.3.40.1  bouyer  * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
    655  1.3.40.1  bouyer  * Optimizations should be done there.
    656  1.3.40.1  bouyer  */
    657  1.3.40.1  bouyer 		/* Check if cheating is allowed (always is for fgrep). */
    658  1.3.40.1  bouyer 	if (grepbehave == GREP_FIXED) {
    659  1.3.40.1  bouyer 		for (i = 0; i < patterns; ++i)
    660  1.3.40.1  bouyer 			fgrepcomp(&fg_pattern[i], pattern[i]);
    661  1.3.40.1  bouyer 	} else {
    662  1.3.40.1  bouyer 		for (i = 0; i < patterns; ++i) {
    663  1.3.40.1  bouyer 			if (fastcomp(&fg_pattern[i], pattern[i])) {
    664  1.3.40.1  bouyer 				/* Fall back to full regex library */
    665  1.3.40.1  bouyer 				c = regcomp(&r_pattern[i], pattern[i], cflags);
    666  1.3.40.1  bouyer 				if (c != 0) {
    667  1.3.40.1  bouyer 					regerror(c, &r_pattern[i], re_error,
    668  1.3.40.1  bouyer 					    RE_ERROR_BUF);
    669  1.3.40.1  bouyer 					errx(2, "%s", re_error);
    670  1.3.40.1  bouyer 				}
    671  1.3.40.1  bouyer 			}
    672  1.3.40.1  bouyer 		}
    673  1.3.40.1  bouyer 	}
    674       1.2    cjep 
    675       1.2    cjep 	if (lbflag)
    676       1.2    cjep 		setlinebuf(stdout);
    677       1.2    cjep 
    678  1.3.40.1  bouyer 	if ((aargc == 0 || aargc == 1) && !Hflag)
    679  1.3.40.1  bouyer 		hflag = true;
    680  1.3.40.1  bouyer 
    681  1.3.40.1  bouyer 	if (aargc == 0)
    682  1.3.40.1  bouyer 		exit(!procfile("-"));
    683  1.3.40.1  bouyer 
    684  1.3.40.1  bouyer 	if (dirbehave == DIR_RECURSE)
    685  1.3.40.1  bouyer 		c = grep_tree(aargv);
    686       1.1    cjep 	else
    687  1.3.40.1  bouyer 		for (c = 0; aargc--; ++aargv) {
    688  1.3.40.1  bouyer 			if ((finclude || fexclude) && !file_matching(*aargv))
    689  1.3.40.1  bouyer 				continue;
    690  1.3.40.1  bouyer 			c+= procfile(*aargv);
    691  1.3.40.1  bouyer 		}
    692       1.1    cjep 
    693  1.3.40.1  bouyer #ifndef WITHOUT_NLS
    694  1.3.40.1  bouyer 	catclose(catalog);
    695  1.3.40.1  bouyer #endif
    696  1.3.40.1  bouyer 
    697  1.3.40.1  bouyer 	/* Find out the correct return value according to the
    698  1.3.40.1  bouyer 	   results and the command line option. */
    699  1.3.40.1  bouyer 	exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
    700       1.1    cjep }
    701