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