1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
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 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /* Copyright (c) 1987, 1988 Microsoft Corporation */
31 /* All Rights Reserved */
32
33 /* Copyright 2012 Nexenta Systems, Inc. All rights reserved. */
34
35 /*
36 * Copyright 2013 Damian Bogel. All rights reserved.
37 * Copyright (c) 2013 Andrew Stormont. All rights reserved.
38 */
39
40 /*
41 * grep -- print lines matching (or not matching) a pattern
42 *
43 * status returns:
44 * 0 - ok, and some matches
45 * 1 - ok, but no matches
46 * 2 - some error
47 */
48
49 #include <sys/types.h>
50
51 #include <ctype.h>
52 #include <fcntl.h>
53 #include <locale.h>
54 #include <memory.h>
55 #include <regexpr.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <ftw.h>
61 #include <limits.h>
62 #include <sys/param.h>
63
64 static const char *errstr[] = {
65 "Range endpoint too large.",
66 "Bad number.",
67 "``\\digit'' out of range.",
68 "No remembered search string.",
69 "\\( \\) imbalance.",
70 "Too many \\(.",
71 "More than 2 numbers given in \\{ \\}.",
72 "} expected after \\.",
73 "First number exceeds second in \\{ \\}.",
74 "[ ] imbalance.",
75 "Regular expression overflow.",
76 "Illegal byte sequence.",
77 "Unknown regexp error code!!",
78 NULL
79 };
80
81 #define STDIN_FILENAME gettext("(standard input)")
82
83 #define errmsg(msg, arg) (void) fprintf(stderr, gettext(msg), arg)
84 #define BLKSIZE 512
85 #define GBUFSIZ 8192
86 #define MAX_DEPTH 1000
87
88 static int temp;
89 static long long lnum;
90 static char *linebuf;
91 static char *prntbuf = NULL;
92 static long fw_lPrntBufLen = 0;
93 static int nflag;
94 static int bflag;
95 static int lflag;
96 static int cflag;
97 static int rflag;
98 static int Rflag;
99 static int vflag;
100 static int sflag;
101 static int iflag;
102 static int wflag;
103 static int hflag;
104 static int Hflag;
105 static int qflag;
106 static int oflag;
107 static int errflg;
108 static int nfile;
109 static long long tln;
110 static int nsucc;
111 static int outfn = 0;
112 static int nlflag;
113 static char *ptr, *ptrend;
114 static char *expbuf;
115
116 static void execute(const char *, int);
117 static void regerr(int);
118 static void prepare(const char *);
119 static int recursive(const char *, const struct stat *, int, struct FTW *);
120 static int succeed(const char *);
121
122 int
123 main(int argc, char **argv)
124 {
125 int c;
126 char *arg;
127 extern int optind;
128
129 (void) setlocale(LC_ALL, "");
130 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
131 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
132 #endif
133 (void) textdomain(TEXT_DOMAIN);
134
135 while ((c = getopt(argc, argv, "hHqblcnoRrsviyw")) != -1)
136 switch (c) {
137 /* based on options order h or H is set as in GNU grep */
138 case 'h':
139 hflag++;
140 Hflag = 0; /* h excludes H */
141 break;
142 case 'H':
143 if (!lflag) /* H is excluded by l */
144 Hflag++;
145 hflag = 0; /* H excludes h */
146 break;
147 case 'q': /* POSIX: quiet: status only */
148 qflag++;
149 break;
150 case 'v':
151 vflag++;
152 break;
153 case 'c':
154 cflag++;
155 break;
156 case 'n':
157 nflag++;
158 break;
159 case 'o':
160 oflag++;
161 break;
162 case 'R':
163 Rflag++;
164 /* FALLTHROUGH */
165 case 'r':
166 rflag++;
167 break;
168 case 'b':
169 bflag++;
170 break;
171 case 's':
172 sflag++;
173 break;
174 case 'l':
175 lflag++;
176 Hflag = 0; /* l excludes H */
177 break;
178 case 'y':
179 case 'i':
180 iflag++;
181 break;
182 case 'w':
183 wflag++;
184 break;
185 case '?':
186 errflg++;
187 }
188
189 if (errflg || (optind >= argc)) {
190 errmsg("Usage: grep [-c|-l|-q|-o] [-r|-R] -hHbnsviw "
191 "pattern file . . .\n",
192 (char *)NULL);
193 exit(2);
194 }
195
196 argv = &argv[optind];
197 argc -= optind;
198 nfile = argc - 1;
199
200 if (strrchr(*argv, '\n') != NULL)
201 regerr(41);
202
203 if (iflag) {
204 for (arg = *argv; *arg != NULL; ++arg)
205 *arg = (char)tolower((int)((unsigned char)*arg));
206 }
207
208 if (wflag) {
209 unsigned int wordlen;
210 char *wordbuf;
211
212 wordlen = strlen(*argv) + 5; /* '\\' '<' *argv '\\' '>' '\0' */
213 if ((wordbuf = malloc(wordlen)) == NULL) {
214 errmsg("grep: Out of memory for word\n", (char *)NULL);
215 exit(2);
216 }
217
218 (void) strcpy(wordbuf, "\\<");
219 (void) strcat(wordbuf, *argv);
220 (void) strcat(wordbuf, "\\>");
221 *argv = wordbuf;
222 }
223
224 expbuf = compile(*argv, (char *)0, (char *)0);
225 if (regerrno)
226 regerr(regerrno);
227
228 if (--argc == 0)
229 execute(NULL, 0);
230 else
231 while (argc-- > 0)
232 prepare(*++argv);
233
234 return (nsucc == 2 ? 2 : (nsucc == 0 ? 1 : 0));
235 }
236
237 static void
238 prepare(const char *path)
239 {
240 struct stat st;
241 int walkflags = FTW_CHDIR;
242 char *buf = NULL;
243
244 if (rflag) {
245 if (stat(path, &st) != -1 &&
246 (st.st_mode & S_IFMT) == S_IFDIR) {
247 outfn = 1;
248
249 /*
250 * Add trailing slash if arg
251 * is directory, to resolve symlinks.
252 */
253 if (path[strlen(path) - 1] != '/') {
254 (void) asprintf(&buf, "%s/", path);
255 if (buf != NULL)
256 path = buf;
257 }
258
259 /*
260 * Search through subdirs if path is directory.
261 * Don't follow symlinks if Rflag is not set.
262 */
263 if (!Rflag)
264 walkflags |= FTW_PHYS;
265
266 if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
267 if (!sflag)
268 errmsg("grep: can't open %s\n", path);
269 nsucc = 2;
270 }
271 return;
272 }
273 }
274 execute(path, 0);
275 }
276
277 static int
278 recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
279 {
280 /*
281 * process files and follow symlinks if Rflag set.
282 */
283 if (info != FTW_F) {
284 if (!sflag &&
285 (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
286 /* report broken symlinks and unreadable files */
287 errmsg("grep: can't open %s\n", name);
288 }
289 return (0);
290 }
291
292 /* skip devices and pipes if Rflag is not set */
293 if (!Rflag && !S_ISREG(statp->st_mode))
294 return (0);
295
296 /* pass offset to relative name from FTW_CHDIR */
297 execute(name, ftw->base);
298 return (0);
299 }
300
301 static void
302 execute(const char *file, int base)
303 {
304 char *lbuf, *p;
305 long count;
306 long offset = 0;
307 char *next_ptr = NULL;
308 long next_count = 0;
309
310 tln = 0;
311
312 if (prntbuf == NULL) {
313 fw_lPrntBufLen = GBUFSIZ + 1;
314 if ((prntbuf = malloc(fw_lPrntBufLen)) == NULL) {
315 exit(2); /* out of memory - BAIL */
316 }
317 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL) {
318 exit(2); /* out of memory - BAIL */
319 }
320 }
321
322 if (file == NULL) {
323 temp = 0;
324 file = STDIN_FILENAME;
325 } else if ((temp = open(file + base, O_RDONLY)) == -1) {
326 if (!sflag)
327 errmsg("grep: can't open %s\n", file);
328 nsucc = 2;
329 return;
330 }
331
332 /* read in first block of bytes */
333 if ((count = read(temp, prntbuf, GBUFSIZ)) <= 0) {
334 (void) close(temp);
335
336 if (cflag && !qflag) {
337 if (Hflag || (nfile > 1 && !hflag))
338 (void) fprintf(stdout, "%s:", file);
339 if (!rflag)
340 (void) fprintf(stdout, "%lld\n", tln);
341 }
342 return;
343 }
344
345 lnum = 0;
346 ptr = prntbuf;
347 for (;;) {
348 /* look for next newline */
349 if ((ptrend = memchr(ptr + offset, '\n', count)) == NULL) {
350 offset += count;
351
352 /*
353 * shift unused data to the beginning of the buffer
354 */
355 if (ptr > prntbuf) {
356 (void) memmove(prntbuf, ptr, offset);
357 ptr = prntbuf;
358 }
359
360 /*
361 * re-allocate a larger buffer if this one is full
362 */
363 if (offset + GBUFSIZ > fw_lPrntBufLen) {
364 /*
365 * allocate a new buffer and preserve the
366 * contents...
367 */
368 fw_lPrntBufLen += GBUFSIZ;
369 if ((prntbuf = realloc(prntbuf,
370 fw_lPrntBufLen)) == NULL)
371 exit(2);
372
373 /*
374 * set up a bigger linebuffer (this is only used
375 * for case insensitive operations). Contents do
376 * not have to be preserved.
377 */
378 free(linebuf);
379 if ((linebuf = malloc(fw_lPrntBufLen)) == NULL)
380 exit(2);
381
382 ptr = prntbuf;
383 }
384
385 p = prntbuf + offset;
386 if ((count = read(temp, p, GBUFSIZ)) > 0)
387 continue;
388
389 if (offset == 0)
390 /* end of file already reached */
391 break;
392
393 /* last line of file has no newline */
394 ptrend = ptr + offset;
395 nlflag = 0;
396 } else {
397 next_ptr = ptrend + 1;
398 next_count = offset + count - (next_ptr - ptr);
399 nlflag = 1;
400 }
401 lnum++;
402 *ptrend = '\0';
403
404 if (iflag) {
405 /*
406 * Make a lower case copy of the record
407 */
408 p = ptr;
409 for (lbuf = linebuf; p < ptrend; )
410 *lbuf++ = (char)tolower((int)
411 (unsigned char)*p++);
412 *lbuf = '\0';
413 lbuf = linebuf;
414 } else
415 /*
416 * Use record as is
417 */
418 lbuf = ptr;
419
420 /* lflag only once */
421 if (step(lbuf, expbuf) ^ vflag) {
422 if (oflag) {
423 /*
424 * Only store the matching bits
425 */
426 ptr = loc1;
427 ptrend = loc2;
428 }
429 if (succeed(file) == 1)
430 break;
431 }
432
433 if (!nlflag)
434 break;
435
436 ptr = next_ptr;
437 count = next_count;
438 offset = 0;
439 }
440 (void) close(temp);
441
442 if (cflag && !qflag) {
443 if (Hflag || (!hflag && ((nfile > 1) ||
444 (rflag && outfn))))
445 (void) fprintf(stdout, "%s:", file);
446 (void) fprintf(stdout, "%lld\n", tln);
447 }
448 }
449
450 static int
451 succeed(const char *f)
452 {
453 int nchars;
454 nsucc = (nsucc == 2) ? 2 : 1;
455
456 if (qflag) {
457 /* no need to continue */
458 return (1);
459 }
460
461 if (cflag) {
462 tln++;
463 return (0);
464 }
465
466 if (lflag) {
467 (void) fprintf(stdout, "%s\n", f);
468 return (1);
469 }
470
471 if (Hflag || (!hflag && (nfile > 1 || (rflag && outfn)))) {
472 /* print filename */
473 (void) fprintf(stdout, "%s:", f);
474 }
475
476 if (bflag)
477 /* print block number */
478 (void) fprintf(stdout, "%lld:", (offset_t)
479 ((lseek(temp, (off_t)0, SEEK_CUR) - 1) / BLKSIZE));
480
481 if (nflag)
482 /* print line number */
483 (void) fprintf(stdout, "%lld:", lnum);
484
485 if (nlflag) {
486 /* newline at end of line */
487 *ptrend = '\n';
488 nchars = ptrend - ptr + 1;
489 } else {
490 /* don't write sentinel \0 */
491 nchars = ptrend - ptr;
492 }
493
494 (void) fwrite(ptr, 1, nchars, stdout);
495 return (0);
496 }
497
498 static void
499 regerr(int err)
500 {
501 errmsg("grep: RE error %d: ", err);
502 switch (err) {
503 case 11:
504 err = 0;
505 break;
506 case 16:
507 err = 1;
508 break;
509 case 25:
510 err = 2;
511 break;
512 case 41:
513 err = 3;
514 break;
515 case 42:
516 err = 4;
517 break;
518 case 43:
519 err = 5;
520 break;
521 case 44:
522 err = 6;
523 break;
524 case 45:
525 err = 7;
526 break;
527 case 46:
528 err = 8;
529 break;
530 case 49:
531 err = 9;
532 break;
533 case 50:
534 err = 10;
535 break;
536 case 67:
537 err = 11;
538 break;
539 default:
540 err = 12;
541 break;
542 }
543
544 errmsg("%s\n", gettext(errstr[err]));
545 exit(2);
546 }