grep.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) 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 }