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