grep.c - 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 : strcm… | |
121 match = 1; | |
122 break; | |
123 } | |
124 } else { | |
125 if ((iflag ? strcasestr : strstr… | |
126 match = 1; | |
127 break; | |
128 } | |
129 } | |
130 } else { | |
131 if (regexec(&pnode->preg, buf, 0, NULL, … | |
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 fil… | |
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, f… | |
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 } |