sbase

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

grep.c (5455B)


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