1 /* fgrep - fast grep Author: Bert Gijsbers */
3 /* Copyright (c) 1991 by Bert Gijsbers. All rights reserved.
4 * Permission to use and redistribute this software is hereby granted provided
5 * that this copyright notice remains intact and that any modifications are
6 * clearly marked as such.
9 * fgrep -chlnsv <[-e string] ... [-f file] ... | string> [file] ...
11 * -c : print the number of matching lines
12 * -h : don't print file name headers if more than one file
13 * -l : print only the file names of the files containing a match
14 * -n : print line numbers
15 * -s : don't print, return status only
16 * -v : reverse, lines not containing one of the strings match
17 * -e string : search for this string
18 * -f file : file contains strings to search for
20 * Options are processed by getopt(3).
21 * Multiple strings per command line are supported, eg.
22 * fgrep -e str1 -e str2 *.c
23 * Instead of a filename - is allowed, meaning standard input.
26 /* #include <ansi.h> */
27 #include <sys/types.h>
34 #define MAX_STR_LEN 256 /* maximum length of strings to search for */
35 #define BYTE 0xFF /* convert from char to int */
36 #define READ_SIZE 4096 /* read() request size */
37 #define BUF_SIZE (2*READ_SIZE) /* size of buffer */
39 typedef struct test_str
{
40 struct test_str
*next
; /* linked list */
41 char *str
; /* string to be found */
42 char *str_end
; /* points to last character */
43 int len
; /* string length */
44 char *bufp
; /* pointer into input buffer */
45 unsigned char table
[256]; /* table for Boyer-Moore algorithm */
50 int cflag
, hflag
, lflag
, nflag
, sflag
, vflag
;
51 unsigned line_num
; /* line number in current file */
53 int fd_in
, eof_seen
; /* file descriptor for input and eof status */
54 char input_buffer
[BUF_SIZE
+ 2];/* buffer + sentinel margin */
55 #define buffer (&input_buffer[2])
57 /* Pointers into the input buffer */
58 char *input
; /* points to current input char */
59 char *max_input
; /* points to first invalid char */
60 char *buf_end
; /* points to first char not read */
63 char no_mem
[] = "not enough memory";
64 char no_arg
[] = "argument missing";
69 _PROTOTYPE(int main
, (int argc
, char **argv
));
70 _PROTOTYPE(char *search_str
, (test_str
* ts
));
71 _PROTOTYPE(int fill_buffer
, (void));
72 _PROTOTYPE(void failure
, (char *mesg
));
73 _PROTOTYPE(void file_open
, (void));
74 _PROTOTYPE(void usage
, (void));
75 _PROTOTYPE(char *get_line
, (void));
76 _PROTOTYPE(void string_file
, (void));
77 _PROTOTYPE(void add_string
, (char *str
));
85 unsigned count
; /* number of matching lines in current file */
86 unsigned found_one
= 0; /* was there any match in any file at all ? */
91 static char outbuf
[BUFSIZ
];
93 setvbuf(stdout
, outbuf
, _IOFBF
, sizeof outbuf
);
97 if (argc
== 1) usage();
98 while ((c
= getopt(argc
, argv
, "ce:f:hlnsv")) != EOF
) {
100 case 'c': cflag
++; break;
101 case 'e': add_string(optarg
); break;
102 case 'f': string_file(); break;
103 case 'h': hflag
++; break;
104 case 'l': lflag
++; break;
105 case 'n': nflag
++; break;
106 case 's': sflag
++; break;
107 case 'v': vflag
++; break;
108 default: usage(); break;
112 /* If no -e or -f option is used take a string from the command line. */
113 if (strings
== (test_str
*) NULL
) {
114 if (optind
== argc
) failure(no_arg
);
115 add_string(argv
[optind
++]);
117 if (argc
- optind
< 2)
118 hflag
++; /* don't print filenames if less than two
121 /* Handle every matching line according to the flags. */
123 optarg
= argv
[optind
];
126 while ((line
= get_line()) != (char *) NULL
) {
130 printf("%s\n", optarg
);
135 if (hflag
== 0) printf("%s:", optarg
);
136 if (nflag
) printf("%u:", line_num
);
139 } while (++line
< input
);
144 if (hflag
== 0) printf("%s: ", optarg
);
145 printf("%u\n", count
);
149 } while (++optind
< argc
);
151 /* Exit nonzero if no match is found. */
152 return found_one
? 0 : 1;
158 "Usage: %s -chlnsv <[-e string] ... [-f file] ... | string> [file] ...\n",
166 fprintf(stderr
, "%s: %s\n", prog_name
, mesg
);
170 /* Add a string to search for to the global linked list `strings'. */
177 if (str
== (char *) NULL
|| (len
= strlen(str
)) == 0) return;
178 if (len
> MAX_STR_LEN
) failure("string too long");
179 if ((ts
= (test_str
*) malloc(sizeof(*ts
))) == (test_str
*) NULL
)
182 /* Initialize Boyer-Moore table. */
183 memset(ts
->table
, len
, sizeof(ts
->table
));
186 ts
->str_end
= str
+ len
- 1;
187 for (; --len
>= 0; str
++) ts
->table
[*str
& BYTE
] = len
;
189 /* Put it on the list */
194 /* Open a file for reading. Initialize input buffer pointers. */
197 /* Use stdin if no file arguments are given on the command line. */
198 if (optarg
== (char *) NULL
|| strcmp(optarg
, "-") == 0) {
201 } else if ((fd_in
= open(optarg
, O_RDONLY
)) == -1) {
202 fprintf(stderr
, "%s: can't open %s\n", prog_name
, optarg
);
205 input
= max_input
= buf_end
= buffer
;
206 buffer
[-1] = '\n'; /* sentinel */
211 /* Move any leftover characters to the head of the buffer.
212 * Read characters into the rest of the buffer.
213 * Round off the available input to whole lines.
214 * Return the number of valid input characters.
221 if (eof_seen
) return 0;
223 size
= buf_end
- max_input
;
224 memmove(buffer
, max_input
, size
);
225 bufp
= &buffer
[size
];
228 if ((size
= read(fd_in
, bufp
, READ_SIZE
)) <= 0) {
229 if (size
!= 0) failure("read error");
231 if (bufp
== buffer
) /* no input left */
233 /* Make sure the last char of a file is '\n'. */
238 } while (bufp
- buffer
< READ_SIZE
&& bufp
[-1] != '\n');
241 while (*--bufp
!= '\n');
242 if (++bufp
== buffer
) {
250 return max_input
- buffer
;
253 /* Read strings from a file. Give duplicates to add_string(). */
259 while (input
< max_input
|| fill_buffer() > 0) {
260 p
= (char *) memchr(input
, '\n', BUF_SIZE
);
262 if ((str
= (char *) malloc(p
- input
)) == (char *) NULL
)
264 memcpy(str
, input
, p
- input
);
271 /* Scan the rest of the available input for a string using Boyer-Moore.
272 * Return a pointer to the match or a pointer beyond end of input if no match.
273 * Record how far the input is scanned.
278 char *bufp
, *prevbufp
, *s
;
280 bufp
= input
+ ts
->len
- 1;
281 while (bufp
< max_input
) {
283 bufp
+= ts
->table
[*bufp
& BYTE
];
284 if (bufp
> prevbufp
) continue;
287 if (s
== ts
->str
) { /* match found */
291 } while (*--bufp
== *--s
);
299 /* Return the next line in which one of the strings occurs.
300 * Or, if the -v option is used, the next line without a match.
308 /* Loop until a line is found. */
310 if (input
>= max_input
&& fill_buffer() == 0) { /* EOF */
311 line
= (char *) NULL
;
315 /* If match is still equal to max_input after the next loop
316 * then no match is found. */
320 if (input
== buffer
) {
321 if (search_str(ts
) < match
) match
= ts
->bufp
;
322 } else if (ts
->bufp
< match
) {
323 if (ts
->bufp
>= input
|| search_str(ts
) < match
)
326 } while ((ts
= ts
->next
) != (test_str
*) NULL
);
328 /* Determine if and in what line a match is found. Only do
329 * line number counting if it is necessary or very easy. */
333 input
= 1 + (char *) memchr(line
, '\n', BUF_SIZE
);
334 if (input
<= match
) break; /* no match in current line */
339 input
= 1 + (char *) memchr(line
, '\n', BUF_SIZE
);
340 } while (input
< match
||
341 (input
== match
&& match
< max_input
));
342 if (match
< max_input
) break; /* match found */
343 } else if (match
< max_input
) {
345 for (line
= match
; *--line
!= '\n';);
347 input
= 1 + (char *) memchr(match
, '\n', BUF_SIZE
);