xref: /titanic_52/usr/src/cmd/fgrep/fgrep.c (revision 4a6ec905b96eb96a398c346f59e034a90ce8ad37)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 /*
36  * fgrep -- print all lines containing any of a set of keywords
37  *
38  *	status returns:
39  *		0 - ok, and some matches
40  *		1 - ok, but no matches
41  *		2 - some error
42  */
43 
44 #include <stdio.h>
45 #include <ctype.h>
46 #include <sys/types.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <locale.h>
50 #include <libintl.h>
51 #include <euc.h>
52 #include <sys/stat.h>
53 #include <fcntl.h>
54 
55 #include <getwidth.h>
56 
57 eucwidth_t WW;
58 #define	WIDTH1	WW._eucw1
59 #define	WIDTH2	WW._eucw2
60 #define	WIDTH3	WW._eucw3
61 #define	MULTI_BYTE	WW._multibyte
62 #define	GETONE(lc, p) \
63 	cw = ISASCII(lc = (unsigned char)*p++) ? 1 :     \
64 		(ISSET2(lc) ? WIDTH2 :                       \
65 		(ISSET3(lc) ? WIDTH3 : WIDTH1));             \
66 	if (--cw > --ccount) {                           \
67 		cw -= ccount;                                \
68 		while (ccount--)                             \
69 			lc = (lc << 7) | ((*p++) & 0177);        \
70 			if (p >= &buf[fw_lBufsiz + BUFSIZ]) {    \
71 			if (nlp == buf) {                        \
72 				/* Increase the buffer size */       \
73 				fw_lBufsiz += BUFSIZ;                \
74 				if ((buf = realloc(buf,              \
75 					fw_lBufsiz + BUFSIZ)) == NULL) { \
76 					exit(2); /* out of memory */     \
77 				}                                    \
78 				nlp = buf;                           \
79 				p = &buf[fw_lBufsiz];                \
80 			} else {                                 \
81 				/* shift the buffer contents down */ \
82 				(void) memmove(buf, nlp,             \
83 					&buf[fw_lBufsiz + BUFSIZ] - nlp);\
84 				p -= nlp - buf;                      \
85 				nlp = buf;                           \
86 			}                                        \
87 		}                                            \
88 		if (p > &buf[fw_lBufsiz]) {                  \
89 			if ((ccount = fread(p, sizeof (char),    \
90 			    &buf[fw_lBufsiz + BUFSIZ] - p, fptr))\
91 				<= 0) break;                         \
92 		} else if ((ccount = fread(p,                \
93 			sizeof (char),  BUFSIZ, fptr)) <= 0)     \
94 			break;                                   \
95 		blkno += (long long)ccount;                  \
96 	}                                                \
97 	ccount -= cw;                                    \
98 	while (cw--)                                     \
99 		lc = (lc << 7) | ((*p++) & 0177)
100 
101 /*
102  * The same() macro and letter() function were inserted to allow for
103  * the -i option work for the multi-byte environment.
104  */
105 wchar_t letter();
106 #define	same(a, b) \
107 	(a == b || iflag && (!MULTI_BYTE || ISASCII(a)) && (a ^ b) == ' ' && \
108 	letter(a) == letter(b))
109 
110 
111 #define	QSIZE 400
112 struct words {
113 	wchar_t inp;
114 	char	out;
115 	struct	words *nst;
116 	struct	words *link;
117 	struct	words *fail;
118 } *w = NULL, *smax, *q;
119 
120 FILE *fptr;
121 long long lnum;
122 int	bflag, cflag, lflag, fflag, nflag, vflag, xflag, eflag, sflag;
123 int	hflag, iflag;
124 int	retcode = 0;
125 int	nfile;
126 long long blkno;
127 int	nsucc;
128 long long tln;
129 FILE	*wordf;
130 char	*argptr;
131 off_t input_size = 0;
132 
133 void	execute(char *);
134 void	cgotofn(void);
135 void	overflo(void);
136 void	cfail(void);
137 
138 static long fw_lBufsiz = 0;
139 
140 int
141 main(int argc, char **argv)
142 {
143 	int c;
144 	int errflg = 0;
145 	struct stat file_stat;
146 
147 	(void) setlocale(LC_ALL, "");
148 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
149 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
150 #endif
151 	(void) textdomain(TEXT_DOMAIN);
152 
153 	while ((c = getopt(argc, argv, "hybcie:f:lnvxs")) != EOF)
154 		switch (c) {
155 
156 		case 's':
157 			sflag++;
158 			continue;
159 		case 'h':
160 			hflag++;
161 			continue;
162 		case 'b':
163 			bflag++;
164 			continue;
165 
166 		case 'i':
167 		case 'y':
168 			iflag++;
169 			continue;
170 
171 		case 'c':
172 			cflag++;
173 			continue;
174 
175 		case 'e':
176 			eflag++;
177 			argptr = optarg;
178 			input_size = strlen(argptr);
179 			continue;
180 
181 		case 'f':
182 			fflag++;
183 			wordf = fopen(optarg, "r");
184 			if (wordf == NULL) {
185 				(void) fprintf(stderr,
186 					gettext("fgrep: can't open %s\n"),
187 					optarg);
188 				exit(2);
189 			}
190 
191 			if (fstat(fileno(wordf), &file_stat) == 0) {
192 			    input_size = file_stat.st_size;
193 			} else {
194 				(void) fprintf(stderr,
195 					gettext("fgrep: can't fstat %s\n"),
196 					optarg);
197 				exit(2);
198 			}
199 
200 			continue;
201 
202 		case 'l':
203 			lflag++;
204 			continue;
205 
206 		case 'n':
207 			nflag++;
208 			continue;
209 
210 		case 'v':
211 			vflag++;
212 			continue;
213 
214 		case 'x':
215 			xflag++;
216 			continue;
217 
218 		case '?':
219 			errflg++;
220 	}
221 
222 	argc -= optind;
223 	if (errflg || ((argc <= 0) && !fflag && !eflag)) {
224 		(void) printf(gettext("usage: fgrep [ -bchilnsvx ] "
225 			"[ -e exp ] [ -f file ] [ strings ] [ file ] ...\n"));
226 		exit(2);
227 	}
228 	if (!eflag && !fflag) {
229 		argptr = argv[optind];
230 		input_size = strlen(argptr);
231 		input_size++;
232 		optind++;
233 		argc--;
234 	}
235 
236 /*
237  * Normally we need one struct words for each letter in the pattern
238  * plus one terminating struct words with outp = 1, but when -x option
239  * is specified we require one more struct words for `\n` character so we
240  * calculate the input_size as below. We add extra 1 because
241  * (input_size/2) rounds off odd numbers
242  */
243 
244 	if (xflag) {
245 		input_size = input_size + (input_size/2) + 1;
246 	}
247 
248 	input_size++;
249 
250 	w = (struct words *)calloc(input_size, sizeof (struct words));
251 	if (w == NULL) {
252 		(void) fprintf(stderr,
253 			gettext("fgrep: could not allocate "
254 				"memory for wordlist\n"));
255 		exit(2);
256 	}
257 
258 	getwidth(&WW);
259 	if ((WIDTH1 == 0) && (WIDTH2 == 0) &&
260 		(WIDTH3 == 0)) {
261 		/*
262 		 * If non EUC-based locale,
263 		 * assume WIDTH1 is 1.
264 		 */
265 		WIDTH1 = 1;
266 	}
267 	WIDTH2++;
268 	WIDTH3++;
269 
270 	cgotofn();
271 	cfail();
272 	nfile = argc;
273 	argv = &argv[optind];
274 	if (argc <= 0) {
275 		execute((char *)NULL);
276 	} else
277 		while (--argc >= 0) {
278 			execute(*argv);
279 			argv++;
280 		}
281 
282 	if (w != NULL) {
283 		free(w);
284 	}
285 
286 	return (retcode != 0 ? retcode : nsucc == 0);
287 }
288 
289 void
290 execute(char *file)
291 {
292 	char *p;
293 	struct words *c;
294 	int ccount;
295 	static char *buf = NULL;
296 	int failed;
297 	char *nlp;
298 	wchar_t lc;
299 	int cw;
300 
301 	if (buf == NULL) {
302 		fw_lBufsiz = BUFSIZ;
303 		if ((buf = malloc(fw_lBufsiz + BUFSIZ)) == NULL) {
304 			exit(2); /* out of memory */
305 		}
306 	}
307 
308 	if (file) {
309 		if ((fptr = fopen(file, "r")) == NULL) {
310 			(void) fprintf(stderr,
311 				gettext("fgrep: can't open %s\n"), file);
312 			retcode = 2;
313 			return;
314 		}
315 	} else {
316 		file = "<stdin>";
317 		fptr = stdin;
318 	}
319 	ccount = 0;
320 	failed = 0;
321 	lnum = 1;
322 	tln = 0;
323 	blkno = 0;
324 	p = buf;
325 	nlp = p;
326 	c = w;
327 	for (;;) {
328 		if (c == 0)
329 			break;
330 		if (ccount <= 0) {
331 			if (p >= &buf[fw_lBufsiz + BUFSIZ]) {
332 				if (nlp == buf) {
333 					/* increase the buffer size */
334 					fw_lBufsiz += BUFSIZ;
335 					if ((buf = realloc(buf,
336 						fw_lBufsiz + BUFSIZ)) == NULL) {
337 						exit(2); /* out of memory */
338 					}
339 					nlp = buf;
340 					p = &buf[fw_lBufsiz];
341 				} else {
342 					/* shift the buffer down */
343 					(void) memmove(buf, nlp,
344 						&buf[fw_lBufsiz + BUFSIZ]
345 						- nlp);
346 					p -= nlp - buf;
347 					nlp = buf;
348 				}
349 
350 			}
351 			if (p > &buf[fw_lBufsiz]) {
352 				if ((ccount = fread(p, sizeof (char),
353 					&buf[fw_lBufsiz + BUFSIZ] - p, fptr))
354 					<= 0)
355 					break;
356 			} else if ((ccount = fread(p, sizeof (char),
357 				BUFSIZ, fptr)) <= 0)
358 				break;
359 			blkno += (long long)ccount;
360 		}
361 		GETONE(lc, p);
362 nstate:
363 		if (same(c->inp, lc)) {
364 			c = c->nst;
365 		} else if (c->link != 0) {
366 			c = c->link;
367 			goto nstate;
368 		} else {
369 			c = c->fail;
370 			failed = 1;
371 			if (c == 0) {
372 				c = w;
373 istate:
374 				if (same(c->inp, lc)) {
375 					c = c->nst;
376 				} else if (c->link != 0) {
377 					c = c->link;
378 					goto istate;
379 				}
380 			} else
381 				goto nstate;
382 		}
383 
384 		if (c == 0)
385 			break;
386 
387 		if (c->out) {
388 			while (lc != '\n') {
389 				if (ccount <= 0) {
390 if (p == &buf[fw_lBufsiz + BUFSIZ]) {
391 	if (nlp == buf) {
392 		/* increase buffer size */
393 		fw_lBufsiz += BUFSIZ;
394 		if ((buf = realloc(buf, fw_lBufsiz + BUFSIZ)) == NULL) {
395 			exit(2); /* out of memory */
396 		}
397 		nlp = buf;
398 		p = &buf[fw_lBufsiz];
399 	} else {
400 		/* shift buffer down */
401 		(void) memmove(buf, nlp, &buf[fw_lBufsiz + BUFSIZ] - nlp);
402 		p -= nlp - buf;
403 		nlp = buf;
404 	}
405 }
406 if (p > &buf[fw_lBufsiz]) {
407 	if ((ccount = fread(p, sizeof (char),
408 		&buf[fw_lBufsiz + BUFSIZ] - p, fptr)) <= 0) break;
409 	} else if ((ccount = fread(p, sizeof (char), BUFSIZ,
410 		fptr)) <= 0) break;
411 		blkno += (long long)ccount;
412 	}
413 	GETONE(lc, p);
414 }
415 			if ((vflag && (failed == 0 || xflag == 0)) ||
416 				(vflag == 0 && xflag && failed))
417 				goto nomatch;
418 succeed:
419 			nsucc = 1;
420 			if (cflag)
421 				tln++;
422 			else if (lflag && !sflag) {
423 				(void) printf("%s\n", file);
424 				(void) fclose(fptr);
425 				return;
426 			} else if (!sflag) {
427 				if (nfile > 1 && !hflag)
428 					(void) printf("%s:", file);
429 				if (bflag)
430 					(void) printf("%lld:",
431 						(blkno - (long long)(ccount-1))
432 						/ BUFSIZ);
433 				if (nflag)
434 					(void) printf("%lld:", lnum);
435 				if (p <= nlp) {
436 					while (nlp < &buf[fw_lBufsiz + BUFSIZ])
437 						(void) putchar(*nlp++);
438 					nlp = buf;
439 				}
440 				while (nlp < p)
441 					(void) putchar(*nlp++);
442 			}
443 nomatch:
444 			lnum++;
445 			nlp = p;
446 			c = w;
447 			failed = 0;
448 			continue;
449 		}
450 		if (lc == '\n')
451 			if (vflag)
452 				goto succeed;
453 			else {
454 				lnum++;
455 				nlp = p;
456 				c = w;
457 				failed = 0;
458 			}
459 	}
460 	(void) fclose(fptr);
461 	if (cflag) {
462 		if ((nfile > 1) && !hflag)
463 			(void) printf("%s:", file);
464 		(void) printf("%lld\n", tln);
465 	}
466 }
467 
468 
469 wchar_t
470 getargc(void)
471 {
472 	/* appends a newline to shell quoted argument list so */
473 	/* the list looks like it came from an ed style file  */
474 	wchar_t c;
475 	int cw;
476 	int b;
477 	static int endflg;
478 
479 
480 	if (wordf) {
481 		if ((b = getc(wordf)) == EOF)
482 			return (EOF);
483 		cw = ISASCII(c = (wchar_t)b) ? 1 :
484 			(ISSET2(c) ? WIDTH2 : (ISSET3(c) ? WIDTH3 : WIDTH1));
485 		while (--cw) {
486 			if ((b = getc(wordf)) == EOF)
487 				return (EOF);
488 			c = (c << 7) | (b & 0177);
489 		}
490 		return (iflag ? letter(c) : c);
491 	}
492 
493 	if (endflg)
494 		return (EOF);
495 
496 	{
497 		cw = ISASCII(c = (unsigned char)*argptr++) ? 1 :
498 			(ISSET2(c) ? WIDTH2 : (ISSET3(c) ? WIDTH3 : WIDTH1));
499 
500 		while (--cw)
501 			c = (c << 7) | ((*argptr++) & 0177);
502 		if (c == '\0') {
503 			endflg++;
504 			return ('\n');
505 		}
506 	}
507 	return (iflag ? letter(c) : c);
508 
509 
510 }
511 
512 void
513 cgotofn(void)
514 {
515 	int c;
516 	struct words *s;
517 
518 	s = smax = w;
519 nword:
520 	for (;;) {
521 		c = getargc();
522 		if (c == EOF)
523 			return;
524 		if (c == 0)
525 			goto enter;
526 		if (c == '\n') {
527 			if (xflag) {
528 				for (;;) {
529 					if (s->inp == c) {
530 						s = s->nst;
531 						break;
532 					}
533 					if (s->inp == 0)
534 						goto nenter;
535 					if (s->link == 0) {
536 						if (smax >= &w[input_size -1])
537 							overflo();
538 						s->link = ++smax;
539 						s = smax;
540 						goto nenter;
541 					}
542 					s = s->link;
543 				}
544 			}
545 			s->out = 1;
546 			s = w;
547 		} else {
548 loop:
549 			if (s->inp == c) {
550 				s = s->nst;
551 				continue;
552 			}
553 			if (s->inp == 0)
554 				goto enter;
555 			if (s->link == 0) {
556 				if (smax >= &w[input_size -1])
557 					overflo();
558 				s->link = ++smax;
559 				s = smax;
560 				goto enter;
561 			}
562 			s = s->link;
563 			goto loop;
564 		}
565 	}
566 
567 enter:
568 	do {
569 		s->inp = c;
570 		if (smax >= &w[input_size -1])
571 			overflo();
572 		s->nst = ++smax;
573 		s = smax;
574 	} while ((c = getargc()) != '\n' && c != EOF);
575 	if (xflag) {
576 nenter:
577 		s->inp = '\n';
578 		if (smax >= &w[input_size -1])
579 			overflo();
580 		s->nst = ++smax;
581 	}
582 	smax->out = 1;
583 	s = w;
584 	if (c != EOF)
585 		goto nword;
586 }
587 
588 /*
589  * This function is an unexpected condition, since input_size should have been
590  * calculated correctly before hand.
591  */
592 
593 void
594 overflo(void)
595 {
596 	(void) fprintf(stderr, gettext("fgrep: wordlist too large\n"));
597 	exit(2);
598 }
599 
600 void
601 cfail(void)
602 {
603 	int qsize = QSIZE;
604 	struct words **queue = NULL;
605 
606 	/*
607 	 * front and rear are pointers used to traverse the global words
608 	 * structure "w" which contains the data of input pattern file
609 	 */
610 	struct words **front, **rear;
611 	struct words *state;
612 	unsigned long frontoffset = 0, rearoffset = 0;
613 	char c;
614 	struct words *s;
615 	s = w;
616 	if ((queue = (struct words **)calloc(qsize, sizeof (struct words *)))
617 				== NULL) {
618 		perror("fgrep");
619 		exit(2);
620 	}
621 	front = rear = queue;
622 init:
623 	if ((s->inp) != 0) {
624 		*rear++ = s->nst;
625 	/*
626 	 * Reallocates the queue if the number of distinct starting
627 	 * character of patterns exceeds the qsize value
628 	 */
629 		if (rear >= &queue[qsize - 1]) {
630 			frontoffset = front - queue;
631 			rearoffset = rear - queue;
632 			qsize += QSIZE;
633 			if ((queue = (struct words **)realloc(queue,
634 				qsize * sizeof (struct words *))) == NULL) {
635 				perror("fgrep");
636 				exit(2);
637 			}
638 			front = queue + frontoffset;
639 			rear = queue + rearoffset;
640 		}
641 	}
642 	if ((s = s->link) != 0) {
643 		goto init;
644 	}
645 
646 	while (rear != front) {
647 		s = *front++;
648 cloop:
649 		if ((c = s->inp) != 0) {
650 			*rear++ = (q = s->nst);
651 		/*
652 		 * Reallocate the queue if the rear pointer reaches the end
653 		 * queue
654 		 */
655 			if (rear >= &queue[qsize - 1]) {
656 				frontoffset = front - queue;
657 				rearoffset = rear - queue;
658 				qsize += QSIZE;
659 				if ((queue = (struct words **)realloc(queue,
660 				    qsize * sizeof (struct words *))) == NULL) {
661 					perror("fgrep");
662 					exit(2);
663 				}
664 				front = queue + frontoffset;
665 				rear = queue + rearoffset;
666 			}
667 			state = s->fail;
668 floop:
669 			if (state == 0)
670 				state = w;
671 			if (state->inp == c) {
672 qloop:
673 				q->fail = state->nst;
674 				if ((state->nst)->out == 1)
675 					q->out = 1;
676 				if ((q = q->link) != 0)
677 					goto qloop;
678 			} else if ((state = state->link) != 0)
679 				goto floop;
680 		}
681 		if ((s = s->link) != 0)
682 			goto cloop;
683 	}
684 }
685 
686 wchar_t
687 letter(wchar_t c)
688 {
689 	if (c >= 'a' && c <= 'z')
690 		return (c);
691 	if (c >= 'A' && c <= 'Z')
692 		return (c + 'a' - 'A');
693 	return (c);
694 }
695