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";
86 unsigned count
; /* number of matching lines in current file */
87 unsigned found_one
= 0; /* was there any match in any file at all ? */
92 static char outbuf
[BUFSIZ
];
94 setvbuf(stdout
, outbuf
, _IOFBF
, sizeof outbuf
);
98 if (argc
== 1) usage();
99 while ((c
= getopt(argc
, argv
, "ce:f:hlnsv")) != EOF
) {
101 case 'c': cflag
++; break;
102 case 'e': add_string(optarg
); break;
103 case 'f': string_file(); break;
104 case 'h': hflag
++; break;
105 case 'l': lflag
++; break;
106 case 'n': nflag
++; break;
107 case 's': sflag
++; break;
108 case 'v': vflag
++; break;
109 default: usage(); break;
113 /* If no -e or -f option is used take a string from the command line. */
114 if (strings
== (test_str
*) NULL
) {
115 if (optind
== argc
) failure(no_arg
);
116 add_string(argv
[optind
++]);
118 if (argc
- optind
< 2)
119 hflag
++; /* don't print filenames if less than two
122 /* Handle every matching line according to the flags. */
124 optarg
= argv
[optind
];
127 while ((line
= get_line()) != (char *) NULL
) {
131 printf("%s\n", optarg
);
136 if (hflag
== 0) printf("%s:", optarg
);
137 if (nflag
) printf("%u:", line_num
);
140 } while (++line
< input
);
145 if (hflag
== 0) printf("%s: ", optarg
);
146 printf("%u\n", count
);
150 } while (++optind
< argc
);
152 /* Exit nonzero if no match is found. */
153 return found_one
? 0 : 1;
159 "Usage: %s -chlnsv <[-e string] ... [-f file] ... | string> [file] ...\n",
167 fprintf(stderr
, "%s: %s\n", prog_name
, mesg
);
171 /* Add a string to search for to the global linked list `strings'. */
178 if (str
== (char *) NULL
|| (len
= strlen(str
)) == 0) return;
179 if (len
> MAX_STR_LEN
) failure("string too long");
180 if ((ts
= (test_str
*) malloc(sizeof(*ts
))) == (test_str
*) NULL
)
183 /* Initialize Boyer-Moore table. */
184 memset(ts
->table
, len
, sizeof(ts
->table
));
187 ts
->str_end
= str
+ len
- 1;
188 for (; --len
>= 0; str
++) ts
->table
[*str
& BYTE
] = len
;
190 /* Put it on the list */
195 /* Open a file for reading. Initialize input buffer pointers. */
198 /* Use stdin if no file arguments are given on the command line. */
199 if (optarg
== (char *) NULL
|| strcmp(optarg
, "-") == 0) {
202 } else if ((fd_in
= open(optarg
, O_RDONLY
)) == -1) {
203 fprintf(stderr
, "%s: can't open %s\n", prog_name
, optarg
);
206 input
= max_input
= buf_end
= buffer
;
207 buffer
[-1] = '\n'; /* sentinel */
212 /* Move any leftover characters to the head of the buffer.
213 * Read characters into the rest of the buffer.
214 * Round off the available input to whole lines.
215 * Return the number of valid input characters.
222 if (eof_seen
) return 0;
224 size
= buf_end
- max_input
;
225 memmove(buffer
, max_input
, size
);
226 bufp
= &buffer
[size
];
229 if ((size
= read(fd_in
, bufp
, READ_SIZE
)) <= 0) {
230 if (size
!= 0) failure("read error");
232 if (bufp
== buffer
) /* no input left */
234 /* Make sure the last char of a file is '\n'. */
239 } while (bufp
- buffer
< READ_SIZE
&& bufp
[-1] != '\n');
242 while (*--bufp
!= '\n');
243 if (++bufp
== buffer
) {
251 return max_input
- buffer
;
254 /* Read strings from a file. Give duplicates to add_string(). */
260 while (input
< max_input
|| fill_buffer() > 0) {
261 p
= (char *) memchr(input
, '\n', BUF_SIZE
);
263 if ((str
= (char *) malloc(p
- input
)) == (char *) NULL
)
265 memcpy(str
, input
, p
- input
);
272 /* Scan the rest of the available input for a string using Boyer-Moore.
273 * Return a pointer to the match or a pointer beyond end of input if no match.
274 * Record how far the input is scanned.
279 char *bufp
, *prevbufp
, *s
;
281 bufp
= input
+ ts
->len
- 1;
282 while (bufp
< max_input
) {
284 bufp
+= ts
->table
[*bufp
& BYTE
];
285 if (bufp
> prevbufp
) continue;
288 if (s
== ts
->str
) { /* match found */
292 } while (*--bufp
== *--s
);
300 /* Return the next line in which one of the strings occurs.
301 * Or, if the -v option is used, the next line without a match.
309 /* Loop until a line is found. */
311 if (input
>= max_input
&& fill_buffer() == 0) { /* EOF */
312 line
= (char *) NULL
;
316 /* If match is still equal to max_input after the next loop
317 * then no match is found. */
321 if (input
== buffer
) {
322 if (search_str(ts
) < match
) match
= ts
->bufp
;
323 } else if (ts
->bufp
< match
) {
324 if (ts
->bufp
>= input
|| search_str(ts
) < match
)
327 } while ((ts
= ts
->next
) != (test_str
*) NULL
);
329 /* Determine if and in what line a match is found. Only do
330 * line number counting if it is necessary or very easy. */
334 input
= 1 + (char *) memchr(line
, '\n', BUF_SIZE
);
335 if (input
<= match
) break; /* no match in current line */
340 input
= 1 + (char *) memchr(line
, '\n', BUF_SIZE
);
341 } while (input
< match
||
342 (input
== match
&& match
< max_input
));
343 if (match
< max_input
) break; /* match found */
344 } else if (match
< max_input
) {
346 for (line
= match
; *--line
!= '\n';);
348 input
= 1 + (char *) memchr(match
, '\n', BUF_SIZE
);