7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * grep - pattern matching program - combined grep, egrep, and fgrep.
31 * Based on MKS grep command, with XCU & Solaris mods.
32 */
33
34 /*
35 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved.
36 *
37 */
38
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <stdarg.h>
43 #include <regex.h>
44 #include <limits.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <locale.h>
50 #include <wchar.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <wctype.h>
54
55 #define BSIZE 512 /* Size of block for -b */
56 #define BUFSIZE 8192 /* Input buffer size */
57
58 #define M_CSETSIZE 256 /* singlebyte chars */
59 static int bmglen; /* length of BMG pattern */
60 static char *bmgpat; /* BMG pattern */
61 static int bmgtab[M_CSETSIZE]; /* BMG delta1 table */
62
63 typedef struct _PATTERN {
64 char *pattern; /* original pattern */
65 wchar_t *wpattern; /* wide, lowercased pattern */
66 struct _PATTERN *next;
67 regex_t re; /* compiled pattern */
68 } PATTERN;
69
70 static PATTERN *patterns;
71 static char errstr[128]; /* regerror string buffer */
72 static int regflags = 0; /* regcomp options */
73 static uchar_t fgrep = 0; /* Invoked as fgrep */
74 static uchar_t egrep = 0; /* Invoked as egrep */
75 static uchar_t nvflag = 1; /* Print matching lines */
76 static uchar_t cflag; /* Count of matches */
77 static uchar_t iflag; /* Case insensitve matching */
78 static uchar_t hflag; /* Supress printing of filename */
79 static uchar_t lflag; /* Print file names of matches */
80 static uchar_t nflag; /* Precede lines by line number */
81 static uchar_t bflag; /* Preccede matches by block number */
82 static uchar_t sflag; /* Suppress file error messages */
83 static uchar_t qflag; /* Suppress standard output */
84 static uchar_t wflag; /* Search for expression as a word */
85 static uchar_t xflag; /* Anchoring */
86 static uchar_t Eflag; /* Egrep or -E flag */
87 static uchar_t Fflag; /* Fgrep or -F flag */
88 static uchar_t outfn; /* Put out file name */
89 static char *cmdname;
90
91 static int use_wchar, use_bmg, mblocale;
92
93 static size_t outbuflen, prntbuflen;
94 static char *prntbuf;
95 static wchar_t *outline;
96
97 static void addfile(char *fn);
98 static void addpattern(char *s);
99 static void fixpatterns(void);
100 static void usage(void);
101 static int grep(int, char *);
102 static void bmgcomp(char *, int);
103 static char *bmgexec(char *, char *);
104
105 /*
106 * mainline for grep
107 */
108 int
109 main(int argc, char **argv)
110 {
111 char *ap;
112 int matched = 0;
113 int c;
114 int fflag = 0;
115 int errors = 0;
116 int i, n_pattern = 0, n_file = 0;
117 char **pattern_list = NULL;
118 char **file_list = NULL;
119
120 (void) setlocale(LC_ALL, "");
121 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
122 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
123 #endif
124 (void) textdomain(TEXT_DOMAIN);
125
126 /*
127 * true if this is running on the multibyte locale
128 */
129 mblocale = (MB_CUR_MAX > 1);
130 /*
131 * Skip leading slashes
132 */
133 cmdname = argv[0];
134 if (ap = strrchr(cmdname, '/'))
135 cmdname = ap + 1;
136
137 ap = cmdname;
138 /*
139 * Detect egrep/fgrep via command name, map to -E and -F options.
140 */
141 if (*ap == 'e' || *ap == 'E') {
142 regflags |= REG_EXTENDED;
143 egrep++;
144 } else {
145 if (*ap == 'f' || *ap == 'F') {
146 fgrep++;
147 }
148 }
149
150 while ((c = getopt(argc, argv, "vwchilnbse:f:qxEFI")) != EOF) {
151 switch (c) {
152 case 'v': /* POSIX: negate matches */
153 nvflag = 0;
154 break;
155
156 case 'c': /* POSIX: write count */
157 cflag++;
158 break;
159
160 case 'i': /* POSIX: ignore case */
161 iflag++;
162 regflags |= REG_ICASE;
163 break;
164
165 case 'l': /* POSIX: Write filenames only */
166 lflag++;
167 break;
168
169 case 'n': /* POSIX: Write line numbers */
170 nflag++;
171 break;
172
173 case 'b': /* Solaris: Write file block numbers */
174 bflag++;
175 break;
176
177 case 's': /* POSIX: No error msgs for files */
178 sflag++;
179 break;
180
181 case 'e': /* POSIX: pattern list */
182 n_pattern++;
183 pattern_list = realloc(pattern_list,
184 sizeof (char *) * n_pattern);
185 if (pattern_list == NULL) {
186 (void) fprintf(stderr,
187 gettext("%s: out of memory\n"),
188 cmdname);
189 exit(2);
190 }
191 *(pattern_list + n_pattern - 1) = optarg;
192 break;
213 break;
214
215 case 'w': /* Solaris: treat pattern as word */
216 wflag++;
217 break;
218
219 case 'x': /* POSIX: full line matches */
220 xflag++;
221 regflags |= REG_ANCHOR;
222 break;
223
224 case 'E': /* POSIX: Extended RE's */
225 regflags |= REG_EXTENDED;
226 Eflag++;
227 break;
228
229 case 'F': /* POSIX: strings, not RE's */
230 Fflag++;
231 break;
232
233 default:
234 usage();
235 }
236 }
237 /*
238 * If we're invoked as egrep or fgrep we need to do some checks
239 */
240
241 if (egrep || fgrep) {
242 /*
243 * Use of -E or -F with egrep or fgrep is illegal
244 */
245 if (Eflag || Fflag)
246 usage();
247 /*
248 * Don't allow use of wflag with egrep / fgrep
249 */
250 if (wflag)
251 usage();
252 /*
317 /*
318 * If -x flag is not specified or -i flag is specified
319 * with fgrep in a multibyte locale, need to use
320 * the wide character APIs. Otherwise, byte-oriented
321 * process will be done.
322 */
323 use_wchar = Fflag && mblocale && (!xflag || iflag);
324
325 /*
326 * Compile Patterns and also decide if BMG can be used
327 */
328 fixpatterns();
329
330 /* Process all files: stdin, or rest of arg list */
331 if (argc < 2) {
332 matched = grep(0, gettext("(standard input)"));
333 } else {
334 if (argc > 2 && hflag == 0)
335 outfn = 1; /* Print filename on match line */
336 for (argv++; *argv != NULL; argv++) {
337 int fd;
338
339 if ((fd = open(*argv, O_RDONLY)) == -1) {
340 errors = 1;
341 if (sflag)
342 continue;
343 (void) fprintf(stderr, gettext(
344 "%s: can't open \"%s\"\n"),
345 cmdname, *argv);
346 continue;
347 }
348 matched |= grep(fd, *argv);
349 (void) close(fd);
350 if (ferror(stdout))
351 break;
352 }
353 }
354 /*
355 * Return() here is used instead of exit
356 */
357
358 (void) fflush(stdout);
359
360 if (errors)
361 return (2);
362 return (matched ? 0 : 1);
363 }
364
365 /*
366 * Add a file of strings to the pattern list.
367 */
368 static void
369 addfile(char *fn)
370 {
371 FILE *fp;
372 char *inbuf;
373 char *bufp;
374 size_t bufsiz, buflen, bufused;
375
376 /*
377 * Open the pattern file
378 */
379 if ((fp = fopen(fn, "r")) == NULL) {
380 (void) fprintf(stderr, gettext("%s: can't open \"%s\"\n"),
381 cmdname, fn);
382 exit(2);
383 }
384 bufsiz = BUFSIZE;
385 if ((inbuf = malloc(bufsiz)) == NULL) {
386 (void) fprintf(stderr,
387 gettext("%s: out of memory\n"), cmdname);
388 exit(2);
389 }
658 p = ibuf;
659 do {
660 *p++ = tolower(*s1);
661 } while (*s1++ != '\0');
662 return (ibuf);
663 }
664
665 /*
666 * Do grep on a single file.
667 * Return true in any lines matched.
668 *
669 * We have two strategies:
670 * The fast one is used when we have a single pattern with
671 * a string known to occur in the pattern. We can then
672 * do a BMG match on the whole buffer.
673 * This is an order of magnitude faster.
674 * Otherwise we split the buffer into lines,
675 * and check for a match on each line.
676 */
677 static int
678 grep(int fd, char *fn)
679 {
680 PATTERN *pp;
681 off_t data_len; /* length of the data chunk */
682 off_t line_len; /* length of the current line */
683 off_t line_offset; /* current line's offset from the beginning */
684 long long lineno;
685 long long matches = 0; /* Number of matching lines */
686 int newlinep; /* 0 if the last line of file has no newline */
687 char *ptr, *ptrend;
688
689
690 if (patterns == NULL)
691 return (0); /* no patterns to match -- just return */
692
693 pp = patterns;
694
695 if (use_bmg) {
696 bmgcomp(pp->pattern, strlen(pp->pattern));
697 }
698
723 long count;
724 off_t offset = 0;
725
726 if (data_len == 0) {
727 /*
728 * If no data in the buffer, reset ptr
729 */
730 ptr = prntbuf;
731 }
732 if (ptr == prntbuf) {
733 /*
734 * The current data chunk starts from prntbuf.
735 * This means either the buffer has no data
736 * or the buffer has no newline.
737 * So, read more data from input.
738 */
739 count = read(fd, ptr + data_len, prntbuflen - data_len);
740 if (count < 0) {
741 /* read error */
742 if (cflag) {
743 if (outfn) {
744 (void) fprintf(stdout,
745 "%s:", fn);
746 }
747 if (!qflag) {
748 (void) fprintf(stdout, "%lld\n",
749 matches);
750 }
751 }
752 return (0);
753 } else if (count == 0) {
754 /* no new data */
755 if (data_len == 0) {
756 /* end of file already reached */
757 break;
758 }
759 /* last line of file has no newline */
760 ptrend = ptr + data_len;
761 newlinep = 0;
762 goto L_start_process;
763 }
764 offset = data_len;
765 data_len += count;
766 }
767
1039 if (cflag) {
1040 if (outfn) {
1041 (void) printf("%s:", fn);
1042 }
1043 if (!qflag) {
1044 (void) printf("%lld\n", matches);
1045 }
1046 }
1047 return (matches != 0);
1048 }
1049
1050 /*
1051 * usage message for grep
1052 */
1053 static void
1054 usage(void)
1055 {
1056 if (egrep || fgrep) {
1057 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1058 (void) fprintf(stderr,
1059 gettext(" [-c|-l|-q] [-bhinsvx] "
1060 "pattern_list [file ...]\n"));
1061
1062 (void) fprintf(stderr, "\t%s", cmdname);
1063 (void) fprintf(stderr,
1064 gettext(" [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1065 "[-f pattern_file]... [file...]\n"));
1066 } else {
1067 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1068 (void) fprintf(stderr,
1069 gettext(" [-c|-l|-q] [-bhinsvwx] "
1070 "pattern_list [file ...]\n"));
1071
1072 (void) fprintf(stderr, "\t%s", cmdname);
1073 (void) fprintf(stderr,
1074 gettext(" [-c|-l|-q] [-bhinsvwx] [-e pattern_list]... "
1075 "[-f pattern_file]... [file...]\n"));
1076
1077 (void) fprintf(stderr, "\t%s", cmdname);
1078 (void) fprintf(stderr,
1079 gettext(" -E [-c|-l|-q] [-bhinsvx] "
1080 "pattern_list [file ...]\n"));
1081
1082 (void) fprintf(stderr, "\t%s", cmdname);
1083 (void) fprintf(stderr,
1084 gettext(" -E [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1085 "[-f pattern_file]... [file...]\n"));
1086
1087 (void) fprintf(stderr, "\t%s", cmdname);
1088 (void) fprintf(stderr,
1089 gettext(" -F [-c|-l|-q] [-bhinsvx] "
1090 "pattern_list [file ...]\n"));
1091
1092 (void) fprintf(stderr, "\t%s", cmdname);
1093 (void) fprintf(stderr,
1094 gettext(" -F [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1095 "[-f pattern_file]... [file...]\n"));
1096 }
1097 exit(2);
1098 /* NOTREACHED */
1099 }
1100
1101 /*
1102 * Compile literal pattern into BMG tables
1103 */
1104 static void
1105 bmgcomp(char *pat, int len)
1106 {
1107 int i;
1108 int tlen;
1109 unsigned char *uc = (unsigned char *)pat;
|
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * grep - pattern matching program - combined grep, egrep, and fgrep.
29 * Based on MKS grep command, with XCU & Solaris mods.
30 */
31
32 /*
33 * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved.
34 *
35 */
36
37 /* Copyright 2012 Nexenta Systems, Inc. All rights reserved. */
38
39 #include <string.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <stdarg.h>
43 #include <regex.h>
44 #include <limits.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <locale.h>
50 #include <wchar.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <wctype.h>
54 #include <ftw.h>
55 #include <sys/param.h>
56
57 #define BSIZE 512 /* Size of block for -b */
58 #define BUFSIZE 8192 /* Input buffer size */
59 #define MAX_DEPTH 1000 /* how deep to recurse */
60
61 #define M_CSETSIZE 256 /* singlebyte chars */
62 static int bmglen; /* length of BMG pattern */
63 static char *bmgpat; /* BMG pattern */
64 static int bmgtab[M_CSETSIZE]; /* BMG delta1 table */
65
66 typedef struct _PATTERN {
67 char *pattern; /* original pattern */
68 wchar_t *wpattern; /* wide, lowercased pattern */
69 struct _PATTERN *next;
70 regex_t re; /* compiled pattern */
71 } PATTERN;
72
73 static PATTERN *patterns;
74 static char errstr[128]; /* regerror string buffer */
75 static int regflags = 0; /* regcomp options */
76 static int matched = 0; /* return of the grep() */
77 static int errors = 0; /* count of errors */
78 static uchar_t fgrep = 0; /* Invoked as fgrep */
79 static uchar_t egrep = 0; /* Invoked as egrep */
80 static uchar_t nvflag = 1; /* Print matching lines */
81 static uchar_t cflag; /* Count of matches */
82 static uchar_t iflag; /* Case insensitve matching */
83 static uchar_t hflag; /* Supress printing of filename */
84 static uchar_t lflag; /* Print file names of matches */
85 static uchar_t nflag; /* Precede lines by line number */
86 static uchar_t rflag; /* Search directories recursively */
87 static uchar_t bflag; /* Preccede matches by block number */
88 static uchar_t sflag; /* Suppress file error messages */
89 static uchar_t qflag; /* Suppress standard output */
90 static uchar_t wflag; /* Search for expression as a word */
91 static uchar_t xflag; /* Anchoring */
92 static uchar_t Eflag; /* Egrep or -E flag */
93 static uchar_t Fflag; /* Fgrep or -F flag */
94 static uchar_t Rflag; /* Like rflag, but follow symlinks */
95 static uchar_t outfn; /* Put out file name */
96 static char *cmdname;
97
98 static int use_wchar, use_bmg, mblocale;
99
100 static size_t outbuflen, prntbuflen;
101 static char *prntbuf;
102 static wchar_t *outline;
103
104 static void addfile(const char *fn);
105 static void addpattern(char *s);
106 static void fixpatterns(void);
107 static void usage(void);
108 static int grep(int, const char *);
109 static void bmgcomp(char *, int);
110 static char *bmgexec(char *, char *);
111 static int recursive(const char *, const struct stat *, int, struct FTW *);
112 static void process_path(const char *);
113 static void process_file(const char *, int);
114
115 /*
116 * mainline for grep
117 */
118 int
119 main(int argc, char **argv)
120 {
121 char *ap;
122 int c;
123 int fflag = 0;
124 int i, n_pattern = 0, n_file = 0;
125 char **pattern_list = NULL;
126 char **file_list = NULL;
127
128 (void) setlocale(LC_ALL, "");
129 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
130 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
131 #endif
132 (void) textdomain(TEXT_DOMAIN);
133
134 /*
135 * true if this is running on the multibyte locale
136 */
137 mblocale = (MB_CUR_MAX > 1);
138 /*
139 * Skip leading slashes
140 */
141 cmdname = argv[0];
142 if (ap = strrchr(cmdname, '/'))
143 cmdname = ap + 1;
144
145 ap = cmdname;
146 /*
147 * Detect egrep/fgrep via command name, map to -E and -F options.
148 */
149 if (*ap == 'e' || *ap == 'E') {
150 regflags |= REG_EXTENDED;
151 egrep++;
152 } else {
153 if (*ap == 'f' || *ap == 'F') {
154 fgrep++;
155 }
156 }
157
158 while ((c = getopt(argc, argv, "vwchilnrbse:f:qxEFIR")) != EOF) {
159 switch (c) {
160 case 'v': /* POSIX: negate matches */
161 nvflag = 0;
162 break;
163
164 case 'c': /* POSIX: write count */
165 cflag++;
166 break;
167
168 case 'i': /* POSIX: ignore case */
169 iflag++;
170 regflags |= REG_ICASE;
171 break;
172
173 case 'l': /* POSIX: Write filenames only */
174 lflag++;
175 break;
176
177 case 'n': /* POSIX: Write line numbers */
178 nflag++;
179 break;
180
181 case 'r': /* Solaris: search recursively */
182 rflag++;
183 break;
184
185 case 'b': /* Solaris: Write file block numbers */
186 bflag++;
187 break;
188
189 case 's': /* POSIX: No error msgs for files */
190 sflag++;
191 break;
192
193 case 'e': /* POSIX: pattern list */
194 n_pattern++;
195 pattern_list = realloc(pattern_list,
196 sizeof (char *) * n_pattern);
197 if (pattern_list == NULL) {
198 (void) fprintf(stderr,
199 gettext("%s: out of memory\n"),
200 cmdname);
201 exit(2);
202 }
203 *(pattern_list + n_pattern - 1) = optarg;
204 break;
225 break;
226
227 case 'w': /* Solaris: treat pattern as word */
228 wflag++;
229 break;
230
231 case 'x': /* POSIX: full line matches */
232 xflag++;
233 regflags |= REG_ANCHOR;
234 break;
235
236 case 'E': /* POSIX: Extended RE's */
237 regflags |= REG_EXTENDED;
238 Eflag++;
239 break;
240
241 case 'F': /* POSIX: strings, not RE's */
242 Fflag++;
243 break;
244
245 case 'R': /* Solaris: like rflag, but follow symlinks */
246 Rflag++;
247 rflag++;
248 break;
249
250 default:
251 usage();
252 }
253 }
254 /*
255 * If we're invoked as egrep or fgrep we need to do some checks
256 */
257
258 if (egrep || fgrep) {
259 /*
260 * Use of -E or -F with egrep or fgrep is illegal
261 */
262 if (Eflag || Fflag)
263 usage();
264 /*
265 * Don't allow use of wflag with egrep / fgrep
266 */
267 if (wflag)
268 usage();
269 /*
334 /*
335 * If -x flag is not specified or -i flag is specified
336 * with fgrep in a multibyte locale, need to use
337 * the wide character APIs. Otherwise, byte-oriented
338 * process will be done.
339 */
340 use_wchar = Fflag && mblocale && (!xflag || iflag);
341
342 /*
343 * Compile Patterns and also decide if BMG can be used
344 */
345 fixpatterns();
346
347 /* Process all files: stdin, or rest of arg list */
348 if (argc < 2) {
349 matched = grep(0, gettext("(standard input)"));
350 } else {
351 if (argc > 2 && hflag == 0)
352 outfn = 1; /* Print filename on match line */
353 for (argv++; *argv != NULL; argv++) {
354 process_path(*argv);
355 }
356 }
357 /*
358 * Return() here is used instead of exit
359 */
360
361 (void) fflush(stdout);
362
363 if (errors)
364 return (2);
365 return (matched ? 0 : 1);
366 }
367
368 static void
369 process_path(const char *path)
370 {
371 struct stat st;
372 int walkflags = FTW_CHDIR;
373 char *buf = NULL;
374
375 if (rflag) {
376 if (stat(path, &st) != -1 &&
377 (st.st_mode & S_IFMT) == S_IFDIR) {
378 outfn = 1; /* Print filename */
379
380 /*
381 * Add trailing slash if arg
382 * is directory, to resolve symlinks.
383 */
384 if (path[strlen(path) - 1] != '/') {
385 (void) asprintf(&buf, "%s/", path);
386 if (buf != NULL)
387 path = buf;
388 }
389
390 /*
391 * Search through subdirs if path is directory.
392 * Don't follow symlinks if Rflag is not set.
393 */
394 if (!Rflag)
395 walkflags |= FTW_PHYS;
396
397 if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
398 if (!sflag)
399 (void) fprintf(stderr,
400 gettext("%s: can't open \"%s\"\n"),
401 cmdname, path);
402 errors = 1;
403 }
404 return;
405 }
406 }
407 process_file(path, 0);
408 }
409
410 /*
411 * Read and process all files in directory recursively.
412 */
413 static int
414 recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
415 {
416 /*
417 * Process files and follow symlinks if Rflag set.
418 */
419 if (info != FTW_F) {
420 /* Report broken symlinks and unreadable files */
421 if (!sflag &&
422 (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
423 (void) fprintf(stderr,
424 gettext("%s: can't open \"%s\"\n"), cmdname, name);
425 }
426 return (0);
427 }
428
429
430 /* Skip devices and pipes if Rflag is not set */
431 if (!Rflag && !S_ISREG(statp->st_mode))
432 return (0);
433 /* Pass offset to relative name from FTW_CHDIR */
434 process_file(name, ftw->base);
435 return (0);
436 }
437
438 /*
439 * Opens file and call grep function.
440 */
441 static void
442 process_file(const char *name, int base)
443 {
444 int fd;
445
446 if ((fd = open(name + base, O_RDONLY)) == -1) {
447 errors = 1;
448 if (!sflag) /* Silent mode */
449 (void) fprintf(stderr, gettext(
450 "%s: can't open \"%s\"\n"),
451 cmdname, name);
452 return;
453 }
454 matched |= grep(fd, name);
455 (void) close(fd);
456
457 if (ferror(stdout)) {
458 (void) fprintf(stderr, gettext(
459 "%s: error writing to stdout\n"),
460 cmdname);
461 (void) fflush(stdout);
462 exit(2);
463 }
464
465 }
466
467 /*
468 * Add a file of strings to the pattern list.
469 */
470 static void
471 addfile(const char *fn)
472 {
473 FILE *fp;
474 char *inbuf;
475 char *bufp;
476 size_t bufsiz, buflen, bufused;
477
478 /*
479 * Open the pattern file
480 */
481 if ((fp = fopen(fn, "r")) == NULL) {
482 (void) fprintf(stderr, gettext("%s: can't open \"%s\"\n"),
483 cmdname, fn);
484 exit(2);
485 }
486 bufsiz = BUFSIZE;
487 if ((inbuf = malloc(bufsiz)) == NULL) {
488 (void) fprintf(stderr,
489 gettext("%s: out of memory\n"), cmdname);
490 exit(2);
491 }
760 p = ibuf;
761 do {
762 *p++ = tolower(*s1);
763 } while (*s1++ != '\0');
764 return (ibuf);
765 }
766
767 /*
768 * Do grep on a single file.
769 * Return true in any lines matched.
770 *
771 * We have two strategies:
772 * The fast one is used when we have a single pattern with
773 * a string known to occur in the pattern. We can then
774 * do a BMG match on the whole buffer.
775 * This is an order of magnitude faster.
776 * Otherwise we split the buffer into lines,
777 * and check for a match on each line.
778 */
779 static int
780 grep(int fd, const char *fn)
781 {
782 PATTERN *pp;
783 off_t data_len; /* length of the data chunk */
784 off_t line_len; /* length of the current line */
785 off_t line_offset; /* current line's offset from the beginning */
786 long long lineno;
787 long long matches = 0; /* Number of matching lines */
788 int newlinep; /* 0 if the last line of file has no newline */
789 char *ptr, *ptrend;
790
791
792 if (patterns == NULL)
793 return (0); /* no patterns to match -- just return */
794
795 pp = patterns;
796
797 if (use_bmg) {
798 bmgcomp(pp->pattern, strlen(pp->pattern));
799 }
800
825 long count;
826 off_t offset = 0;
827
828 if (data_len == 0) {
829 /*
830 * If no data in the buffer, reset ptr
831 */
832 ptr = prntbuf;
833 }
834 if (ptr == prntbuf) {
835 /*
836 * The current data chunk starts from prntbuf.
837 * This means either the buffer has no data
838 * or the buffer has no newline.
839 * So, read more data from input.
840 */
841 count = read(fd, ptr + data_len, prntbuflen - data_len);
842 if (count < 0) {
843 /* read error */
844 if (cflag) {
845 if (outfn && !rflag) {
846 (void) fprintf(stdout,
847 "%s:", fn);
848 }
849 if (!qflag && !rflag) {
850 (void) fprintf(stdout, "%lld\n",
851 matches);
852 }
853 }
854 return (0);
855 } else if (count == 0) {
856 /* no new data */
857 if (data_len == 0) {
858 /* end of file already reached */
859 break;
860 }
861 /* last line of file has no newline */
862 ptrend = ptr + data_len;
863 newlinep = 0;
864 goto L_start_process;
865 }
866 offset = data_len;
867 data_len += count;
868 }
869
1141 if (cflag) {
1142 if (outfn) {
1143 (void) printf("%s:", fn);
1144 }
1145 if (!qflag) {
1146 (void) printf("%lld\n", matches);
1147 }
1148 }
1149 return (matches != 0);
1150 }
1151
1152 /*
1153 * usage message for grep
1154 */
1155 static void
1156 usage(void)
1157 {
1158 if (egrep || fgrep) {
1159 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1160 (void) fprintf(stderr,
1161 gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
1162 "pattern_list [file ...]\n"));
1163
1164 (void) fprintf(stderr, "\t%s", cmdname);
1165 (void) fprintf(stderr,
1166 gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
1167 "[-e pattern_list]... "
1168 "[-f pattern_file]... [file...]\n"));
1169 } else {
1170 (void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1171 (void) fprintf(stderr,
1172 gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
1173 "pattern_list [file ...]\n"));
1174
1175 (void) fprintf(stderr, "\t%s", cmdname);
1176 (void) fprintf(stderr,
1177 gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
1178 "[-e pattern_list]... "
1179 "[-f pattern_file]... [file...]\n"));
1180
1181 (void) fprintf(stderr, "\t%s", cmdname);
1182 (void) fprintf(stderr,
1183 gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
1184 "pattern_list [file ...]\n"));
1185
1186 (void) fprintf(stderr, "\t%s", cmdname);
1187 (void) fprintf(stderr,
1188 gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
1189 "[-e pattern_list]... "
1190 "[-f pattern_file]... [file...]\n"));
1191
1192 (void) fprintf(stderr, "\t%s", cmdname);
1193 (void) fprintf(stderr,
1194 gettext(" -F [-c|-l|-q] [-r|-R] [-bhinsvx] "
1195 "pattern_list [file ...]\n"));
1196
1197 (void) fprintf(stderr, "\t%s", cmdname);
1198 (void) fprintf(stderr,
1199 gettext(" -F [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
1200 "[-f pattern_file]... [file...]\n"));
1201 }
1202 exit(2);
1203 /* NOTREACHED */
1204 }
1205
1206 /*
1207 * Compile literal pattern into BMG tables
1208 */
1209 static void
1210 bmgcomp(char *pat, int len)
1211 {
1212 int i;
1213 int tlen;
1214 unsigned char *uc = (unsigned char *)pat;
|