1 /* grep - search a file for a pattern Author: Norbert Schlenker */
3 /* Norbert Schlenker (nfs@princeton.edu) 1990-02-08
4 * Released into the public domain.
6 * Grep searches files for lines containing a pattern, as specified by
7 * a regular expression, and prints those lines. It is invoked by:
8 * grep [flags] [pattern] [file ...]
11 * -e pattern useful when pattern begins with '-'
12 * -c print a count of lines matched
14 * -l prints just file names, no lines (quietly overrides -n)
15 * -n printed lines are preceded by relative line numbers
16 * -s prints errors only (quietly overrides -l and -n)
17 * -v prints lines which don't contain the pattern
20 * If both -l and -v are specified, grep prints the names of those
21 * files which do not contain the pattern *anywhere*.
24 * Grep sets an exit status which can be tested by the caller.
25 * Note that these settings are not necessarily compatible with
26 * any other version of grep, especially when -v is specified.
27 * Possible status values are:
28 * 0 if any matches are found
29 * 1 if no matches are found
30 * 2 if syntax errors are detected or any file cannot be opened
34 /* External interfaces */
35 #include <sys/types.h>
36 #include <regexp.h> /* Thanks to Henry Spencer */
42 /* Internal constants */
43 #define MATCH 0 /* exit code: some match somewhere */
44 #define NO_MATCH 1 /* exit code: no match on any line */
45 #define FAILURE 2 /* exit code: syntax error or bad file name */
48 #define SET_FLAG(c) (flags[(c)-'a'] = 1)
49 #define FLAG(c) (flags[(c)-'a'] != 0)
51 #define uppercase(c) (((unsigned) ((c) - 'A')) <= ('Z' - 'A'))
52 #define downcase(c) ((c) - 'A' + 'a')
55 static char *program
; /* program name */
56 static char flags
[26]; /* invocation flags */
57 static regexp
*expression
; /* compiled search pattern */
58 static const char *rerr
; /* error message */
60 /* External variables. */
64 /* Internal interfaces */
65 _PROTOTYPE(int main
, (int argc
, char **argv
));
66 _PROTOTYPE(static int match
, (FILE *input
, char *label
, char *filename
));
67 _PROTOTYPE(static char *get_line
, (FILE *input
));
68 _PROTOTYPE(static char *map_nocase
, (char *line
));
69 _PROTOTYPE(void regerror
, (const char *s
) );
70 _PROTOTYPE(static void tov8
, (char *v8pattern
, char *pattern
));
76 int opt
; /* option letter from getopt() */
77 int egrep
=0; /* using extended regexp operators */
78 char *pattern
; /* search pattern */
79 char *v8pattern
; /* v8 regexp search pattern */
80 int exit_status
= NO_MATCH
; /* exit status for our caller */
81 int file_status
; /* status of search in one file */
82 FILE *input
; /* input file (if not stdin) */
85 if (strlen(program
)>=5 && strcmp(program
+strlen(program
)-5,"egrep")==0) egrep
=1;
86 memset(flags
, 0, sizeof(flags
));
89 /* Process any command line flags. */
90 while ((opt
= getopt(argc
, argv
, "e:cilnsv")) != EOF
) {
92 exit_status
= FAILURE
;
100 /* Detect a few problems. */
101 if ((exit_status
== FAILURE
) || (optind
== argc
&& pattern
== NULL
)) {
102 fprintf(stderr
,"Usage: %s [-cilnsv] [-e] expression [file ...]\n",program
);
106 /* Ensure we have a usable pattern. */
108 pattern
= argv
[optind
++];
110 /* Map pattern to lowercase if -i given. */
113 for (p
= pattern
; *p
!= '\0'; p
++) {
120 if ((v8pattern
=malloc(2*strlen(pattern
)))==(char*)0) {
121 fprintf(stderr
,"%s: out of memory\n");
124 tov8(v8pattern
,pattern
);
125 } else v8pattern
=pattern
;
128 if ((expression
= regcomp(v8pattern
)) == NULL
) {
129 fprintf(stderr
,"%s: bad regular expression",program
);
130 if (rerr
) fprintf(stderr
," (%s)",rerr
);
131 fprintf(stderr
,"\n");
135 /* Process the files appropriately. */
136 if (optind
== argc
) { /* no file names - find pattern in stdin */
137 exit_status
= match(stdin
, (char *) NULL
, "<stdin>");
140 if (optind
+ 1 == argc
) { /* one file name - find pattern in it */
141 if (strcmp(argv
[optind
], "-") == 0) {
142 exit_status
= match(stdin
, (char *) NULL
, "-");
144 if ((input
= fopen(argv
[optind
], "r")) == NULL
) {
145 fprintf(stderr
, "%s: couldn't open %s\n",
146 program
, argv
[optind
]);
147 exit_status
= FAILURE
;
150 exit_status
= match(input
, (char *) NULL
, argv
[optind
]);
155 while (optind
< argc
) { /* lots of file names - find pattern in all */
156 if (strcmp(argv
[optind
], "-") == 0) {
157 file_status
= match(stdin
, "-", "-");
159 if ((input
= fopen(argv
[optind
], "r")) == NULL
) {
160 fprintf(stderr
, "%s: couldn't open %s\n",
161 program
, argv
[optind
]);
162 exit_status
= FAILURE
;
164 file_status
= match(input
, argv
[optind
], argv
[optind
]);
168 if (exit_status
!= FAILURE
)
169 exit_status
&= file_status
;
176 /* match - matches the lines of a file with the regular expression.
177 * To improve performance when either -s or -l is specified, this
178 * function handles those cases specially.
181 static int match(input
, label
, filename
)
186 char *line
, *testline
; /* pointers to input line */
187 long int lineno
= 0; /* line number */
188 long int matchcount
= 0; /* lines matched */
189 int status
= NO_MATCH
; /* summary of what was found in this file */
191 if (FLAG('s') || FLAG('l')) {
192 while ((line
= get_line(input
)) != NULL
) {
193 testline
= FLAG('i') ? map_nocase(line
) : line
;
194 if (regexec(expression
, testline
, 1)) {
200 if ((!FLAG('v') && status
== MATCH
) ||
201 ( FLAG('v') && status
== NO_MATCH
))
206 while ((line
= get_line(input
)) != NULL
) {
208 testline
= FLAG('i') ? map_nocase(line
) : line
;
209 if (regexec(expression
, testline
, 1)) {
213 printf("%s:", label
);
215 printf("%ld:", lineno
);
216 if (!FLAG('c')) puts(line
);
222 printf("%s:", label
);
224 printf("%ld:", lineno
);
225 if (!FLAG('c')) puts(line
);
230 if (FLAG('c')) printf("%ld\n", matchcount
);
235 /* get_line - fetch a line from the input file
236 * This function reads a line from the input file into a dynamically
237 * allocated buffer. If the line is too long for the current buffer,
238 * attempts will be made to increase its size to accomodate the line.
239 * The trailing newline is stripped before returning to the caller.
242 #define FIRST_BUFFER (size_t)256 /* first buffer size */
244 static char *buf
= NULL
; /* input buffer */
245 static size_t buf_size
= 0; /* input buffer size */
247 static char *get_line(input
)
257 if ((buf
= (char *) malloc(FIRST_BUFFER
)) == NULL
) {
258 fprintf(stderr
,"%s: not enough memory\n",program
);
261 buf_size
= FIRST_BUFFER
;
267 while (--n
> 0 && (c
= getc(input
)) != EOF
) {
275 return (ferror(input
) || bp
== buf
) ? NULL
: buf
;
276 new_size
= buf_size
<< 1;
277 if ((new_buf
= (char *) realloc(buf
, new_size
)) == NULL
) {
278 fprintf(stderr
, "%s: line too long - truncated\n", program
);
279 while ((c
= getc(input
)) != EOF
&& c
!= '\n') ;
283 bp
= new_buf
+ (buf_size
- 1);
292 /* map_nocase - map a line down to lowercase letters only.
293 * bad points: assumes line gotten from get_line.
294 * there is more than A-Z you say?
297 static char *map_nocase(line
)
300 static char *mapped
=(char*)0;
301 static size_t map_size
= 0;
304 if (map_size
< buf_size
) {
305 if ((mapped
=realloc(mapped
,map_size
=buf_size
)) == NULL
) {
306 fprintf(stderr
,"%s: not enough memory\n",program
);
313 *mp
++ = uppercase(*line
) ? downcase(*line
) : *line
;
314 } while (*line
++ != '\0');
319 /* In basic regular expressions, the characters ?, +, |, (, and )
320 are taken literally; use the backslashed versions for RE operators.
321 In v8 regular expressions, things are the other way round, so
322 we have to swap those characters and their backslashed versions.
324 static void tov8(char *v8
, char *basic
)
326 while (*basic
) switch (*basic
)
373 /* Regular expression code calls this routine to print errors. */