sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

grep.c (4866B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <regex.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <strings.h>
      7 
      8 #include "queue.h"
      9 #include "util.h"
     10 
     11 enum { Match = 0, NoMatch = 1, Error = 2 };
     12 
     13 static void addpattern(const char *);
     14 static void addpatternfile(FILE *);
     15 static int grep(FILE *, const char *);
     16 
     17 static int Eflag;
     18 static int Fflag;
     19 static int Hflag;
     20 static int eflag;
     21 static int fflag;
     22 static int hflag;
     23 static int iflag;
     24 static int sflag;
     25 static int vflag;
     26 static int wflag;
     27 static int xflag;
     28 static int many;
     29 static int mode;
     30 
     31 struct pattern {
     32 	regex_t preg;
     33 	SLIST_ENTRY(pattern) entry;
     34 	char pattern[];
     35 };
     36 
     37 static SLIST_HEAD(phead, pattern) phead;
     38 
     39 static void
     40 addpattern(const char *pattern)
     41 {
     42 	struct pattern *pnode;
     43 	size_t patlen;
     44 
     45 	patlen = strlen(pattern);
     46 
     47 	pnode = enmalloc(Error, sizeof(*pnode) + patlen + 9);
     48 	SLIST_INSERT_HEAD(&phead, pnode, entry);
     49 
     50 	if (Fflag || (!xflag && !wflag)) {
     51 		memcpy(pnode->pattern, pattern, patlen + 1);
     52 	} else {
     53 		sprintf(pnode->pattern, "%s%s%s%s%s",
     54 			xflag ? "^" : "\\<",
     55 			Eflag ? "(" : "\\(",
     56 			pattern,
     57 			Eflag ? ")" : "\\)",
     58 			xflag ? "$" : "\\>");
     59 	}
     60 }
     61 
     62 static void
     63 addpatternfile(FILE *fp)
     64 {
     65 	static char *buf = NULL;
     66 	static size_t size = 0;
     67 	ssize_t len = 0;
     68 
     69 	while ((len = getline(&buf, &size, fp)) > 0) {
     70 		if (buf[len - 1] == '\n')
     71 			buf[len - 1] = '\0';
     72 		addpattern(buf);
     73 	}
     74 	if (ferror(fp))
     75 		enprintf(Error, "read error:");
     76 }
     77 
     78 static int
     79 grep(FILE *fp, const char *str)
     80 {
     81 	static char *buf = NULL;
     82 	static size_t size = 0;
     83 	ssize_t len = 0;
     84 	long c = 0, n;
     85 	struct pattern *pnode;
     86 	int match, result = NoMatch;
     87 
     88 	for (n = 1; (len = getline(&buf, &size, fp)) > 0; n++) {
     89 		/* Remove the trailing newline if one is present. */
     90 		if (buf[len - 1] == '\n')
     91 			buf[len - 1] = '\0';
     92 		match = 0;
     93 		SLIST_FOREACH(pnode, &phead, entry) {
     94 			if (Fflag) {
     95 				if (xflag) {
     96 					if (!(iflag ? strcasecmp : strcmp)(buf, pnode->pattern)) {
     97 						match = 1;
     98 						break;
     99 					}
    100 				} else {
    101 					if ((iflag ? strcasestr : strstr)(buf, pnode->pattern)) {
    102 						match = 1;
    103 						break;
    104 					}
    105 				}
    106 			} else {
    107 				if (regexec(&pnode->preg, buf, 0, NULL, 0) == 0) {
    108 					match = 1;
    109 					break;
    110 				}
    111 			}
    112 		}
    113 		if (match != vflag) {
    114 			result = Match;
    115 			switch (mode) {
    116 			case 'c':
    117 				c++;
    118 				break;
    119 			case 'l':
    120 				puts(str);
    121 				goto end;
    122 			case 'q':
    123 				exit(Match);
    124 			default:
    125 				if (!hflag && (many || Hflag))
    126 					printf("%s:", str);
    127 				if (mode == 'n')
    128 					printf("%ld:", n);
    129 				puts(buf);
    130 				break;
    131 			}
    132 		}
    133 	}
    134 	if (mode == 'c')
    135 		printf("%ld\n", c);
    136 end:
    137 	if (ferror(fp)) {
    138 		weprintf("%s: read error:", str);
    139 		result = Error;
    140 	}
    141 	return result;
    142 }
    143 
    144 static void
    145 usage(void)
    146 {
    147 	enprintf(Error, "usage: %s [-EFHchilnqsvwx] [-e pattern] [-f file] "
    148 	         "[pattern] [file ...]\n", argv0);
    149 }
    150 
    151 int
    152 main(int argc, char *argv[])
    153 {
    154 	struct pattern *pnode;
    155 	int m, flags = REG_NOSUB, match = NoMatch;
    156 	FILE *fp;
    157 	char *arg;
    158 
    159 	SLIST_INIT(&phead);
    160 
    161 	ARGBEGIN {
    162 	case 'E':
    163 		Eflag = 1;
    164 		Fflag = 0;
    165 		flags |= REG_EXTENDED;
    166 		break;
    167 	case 'F':
    168 		Fflag = 1;
    169 		Eflag = 0;
    170 		flags &= ~REG_EXTENDED;
    171 		break;
    172 	case 'H':
    173 		Hflag = 1;
    174 		hflag = 0;
    175 		break;
    176 	case 'e':
    177 		arg = EARGF(usage());
    178 		if (!(fp = fmemopen(arg, strlen(arg) + 1, "r")))
    179 			eprintf("fmemopen:");
    180 		addpatternfile(fp);
    181 		efshut(fp, arg);
    182 		eflag = 1;
    183 		break;
    184 	case 'f':
    185 		arg = EARGF(usage());
    186 		fp = fopen(arg, "r");
    187 		if (!fp)
    188 			enprintf(Error, "fopen %s:", arg);
    189 		addpatternfile(fp);
    190 		efshut(fp, arg);
    191 		fflag = 1;
    192 		break;
    193 	case 'h':
    194 		hflag = 1;
    195 		Hflag = 0;
    196 		break;
    197 	case 'c':
    198 	case 'l':
    199 	case 'n':
    200 	case 'q':
    201 		mode = ARGC();
    202 		break;
    203 	case 'i':
    204 		flags |= REG_ICASE;
    205 		iflag = 1;
    206 		break;
    207 	case 's':
    208 		sflag = 1;
    209 		break;
    210 	case 'v':
    211 		vflag = 1;
    212 		break;
    213 	case 'w':
    214 		wflag = 1;
    215 		break;
    216 	case 'x':
    217 		xflag = 1;
    218 		break;
    219 	default:
    220 		usage();
    221 	} ARGEND
    222 
    223 	if (argc == 0 && !eflag && !fflag)
    224 		usage(); /* no pattern */
    225 
    226 	/* just add literal pattern to list */
    227 	if (!eflag && !fflag) {
    228 		if (!(fp = fmemopen(argv[0], strlen(argv[0]) + 1, "r")))
    229 			eprintf("fmemopen:");
    230 		addpatternfile(fp);
    231 		efshut(fp, argv[0]);
    232 		argc--;
    233 		argv++;
    234 	}
    235 
    236 	if (!Fflag)
    237 		/* Compile regex for all search patterns */
    238 		SLIST_FOREACH(pnode, &phead, entry)
    239 			enregcomp(Error, &pnode->preg, pnode->pattern, flags);
    240 	many = (argc > 1);
    241 	if (argc == 0) {
    242 		match = grep(stdin, "<stdin>");
    243 	} else {
    244 		for (; *argv; argc--, argv++) {
    245 			if (!strcmp(*argv, "-")) {
    246 				*argv = "<stdin>";
    247 				fp = stdin;
    248 			} else if (!(fp = fopen(*argv, "r"))) {
    249 				if (!sflag)
    250 					weprintf("fopen %s:", *argv);
    251 				match = Error;
    252 				continue;
    253 			}
    254 			m = grep(fp, *argv);
    255 			if (m == Error || (match != Error && m == Match))
    256 				match = m;
    257 			if (fp != stdin && fshut(fp, *argv))
    258 				match = Error;
    259 		}
    260 	}
    261 
    262 	if (fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>"))
    263 		match = Error;
    264 
    265 	return match;
    266 }