1 /* $OpenBSD: grep.c,v 1.50 2023/03/08 04:43:11 guenther Exp $ */
2
3 /* This file is in the public domain */
4
5 #include <sys/queue.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8
9 #include <ctype.h>
10 #include <limits.h>
11 #include <signal.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <time.h>
16 #include <unistd.h>
17
18 #include "def.h"
19 #include "kbd.h"
20 #include "funmap.h"
21
22 int globalwd = FALSE;
23 static int compile_goto_error(int, int);
24 int next_error(int, int);
25 static int grep(int, int);
26 static int gid(int, int);
27 static struct buffer *compile_mode(const char *, const char *);
28 void grep_init(void);
29
30 static char compile_last_command[NFILEN] = "make ";
31
32 /*
33 * Hints for next-error
34 *
35 * XXX - need some kind of callback to find out when those get killed.
36 */
37 struct mgwin *compile_win;
38 struct buffer *compile_buffer;
39
40 static PF compile_pf[] = {
41 compile_goto_error
42 };
43
44 static struct KEYMAPE (1) compilemap = {
45 1,
46 1,
47 rescan,
48 {
49 { CCHR('M'), CCHR('M'), compile_pf, NULL }
50 }
51 };
52
53 void
grep_init(void)54 grep_init(void)
55 {
56 funmap_add(compile_goto_error, "compile-goto-error", 0);
57 funmap_add(next_error, "next-error", 0);
58 funmap_add(grep, "grep", 1);
59 funmap_add(compile, "compile", 0);
60 funmap_add(gid, "gid", 1);
61 maps_add((KEYMAP *)&compilemap, "compile");
62 }
63
64 static int
grep(int f,int n)65 grep(int f, int n)
66 {
67 char cprompt[NFILEN], *bufp;
68 struct buffer *bp;
69 struct mgwin *wp;
70
71 (void)strlcpy(cprompt, "grep -n ", sizeof(cprompt));
72 if ((bufp = eread("Run grep: ", cprompt, NFILEN,
73 EFDEF | EFNEW | EFCR)) == NULL)
74 return (ABORT);
75 else if (bufp[0] == '\0')
76 return (FALSE);
77 if (strlcat(cprompt, " /dev/null", sizeof(cprompt)) >= sizeof(cprompt))
78 return (FALSE);
79
80 if ((bp = compile_mode("*grep*", cprompt)) == NULL)
81 return (FALSE);
82 if ((wp = popbuf(bp, WNONE)) == NULL)
83 return (FALSE);
84 curbp = bp;
85 compile_win = curwp = wp;
86 return (TRUE);
87 }
88
89 int
compile(int f,int n)90 compile(int f, int n)
91 {
92 char cprompt[NFILEN], *bufp;
93 struct buffer *bp;
94 struct mgwin *wp;
95
96 (void)strlcpy(cprompt, compile_last_command, sizeof(cprompt));
97 if ((bufp = eread("Compile command: ", cprompt, NFILEN,
98 EFDEF | EFNEW | EFCR)) == NULL)
99 return (ABORT);
100 else if (bufp[0] == '\0')
101 return (FALSE);
102 if (savebuffers(f, n) == ABORT)
103 return (ABORT);
104 (void)strlcpy(compile_last_command, bufp, sizeof(compile_last_command));
105
106 if ((bp = compile_mode("*compile*", cprompt)) == NULL)
107 return (FALSE);
108 if ((wp = popbuf(bp, WNONE)) == NULL)
109 return (FALSE);
110 curbp = bp;
111 compile_win = curwp = wp;
112 gotoline(FFARG, 0);
113 return (TRUE);
114 }
115
116 /* id-utils foo. */
117 static int
gid(int f,int n)118 gid(int f, int n)
119 {
120 char command[NFILEN];
121 char cprompt[NFILEN], *bufp;
122 int c;
123 struct buffer *bp;
124 struct mgwin *wp;
125 int i, j, len;
126
127 /* catch ([^\s(){}]+)[\s(){}]* */
128
129 i = curwp->w_doto;
130 /* Skip backwards over delimiters we are currently on */
131 while (i > 0) {
132 c = lgetc(curwp->w_dotp, i);
133 if (isalnum(c) || c == '_')
134 break;
135
136 i--;
137 }
138
139 /* Skip the symbol itself */
140 for (; i > 0; i--) {
141 c = lgetc(curwp->w_dotp, i - 1);
142 if (!isalnum(c) && c != '_')
143 break;
144 }
145 /* Fill the symbol in cprompt[] */
146 for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp);
147 j++, i++) {
148 c = lgetc(curwp->w_dotp, i);
149 if (!isalnum(c) && c != '_')
150 break;
151 cprompt[j] = c;
152 }
153 cprompt[j] = '\0';
154
155 if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN,
156 (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL)
157 return (ABORT);
158 else if (bufp[0] == '\0')
159 return (FALSE);
160 len = snprintf(command, sizeof(command), "gid %s", cprompt);
161 if (len < 0 || len >= sizeof(command))
162 return (FALSE);
163
164 if ((bp = compile_mode("*gid*", command)) == NULL)
165 return (FALSE);
166 if ((wp = popbuf(bp, WNONE)) == NULL)
167 return (FALSE);
168 curbp = bp;
169 compile_win = curwp = wp;
170 return (TRUE);
171 }
172
173 struct buffer *
compile_mode(const char * name,const char * command)174 compile_mode(const char *name, const char *command)
175 {
176 struct buffer *bp;
177 FILE *fpipe;
178 char *buf;
179 size_t sz;
180 ssize_t len;
181 int ret, n, status;
182 char cwd[NFILEN], qcmd[NFILEN];
183 char timestr[NTIME];
184 time_t t;
185
186 buf = NULL;
187 sz = 0;
188
189 n = snprintf(qcmd, sizeof(qcmd), "%s 2>&1", command);
190 if (n < 0 || n >= sizeof(qcmd))
191 return (NULL);
192
193 bp = bfind(name, TRUE);
194 if (bclear(bp) != TRUE)
195 return (NULL);
196
197 if (getbufcwd(bp->b_cwd, sizeof(bp->b_cwd)) != TRUE)
198 return (NULL);
199 addlinef(bp, "cd %s", bp->b_cwd);
200 addline(bp, qcmd);
201 addline(bp, "");
202
203 if (getcwd(cwd, sizeof(cwd)) == NULL)
204 panic("Can't get current directory!");
205 if (chdir(bp->b_cwd) == -1) {
206 dobeep();
207 ewprintf("Can't change dir to %s", bp->b_cwd);
208 return (NULL);
209 }
210 if ((fpipe = popen(qcmd, "r")) == NULL) {
211 dobeep();
212 ewprintf("Problem opening pipe");
213 return (NULL);
214 }
215 while ((len = getline(&buf, &sz, fpipe)) != -1) {
216 if (buf[len - 1] == *bp->b_nlchr)
217 buf[len - 1] = '\0';
218 addline(bp, buf);
219 }
220 free(buf);
221 if (ferror(fpipe))
222 ewprintf("Problem reading pipe");
223 ret = pclose(fpipe);
224 t = time(NULL);
225 strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t));
226 addline(bp, "");
227 if (WIFEXITED(ret)) {
228 status = WEXITSTATUS(ret);
229 if (status == 0)
230 addlinef(bp, "Command finished at %s", timestr);
231 else
232 addlinef(bp, "Command exited abnormally with code %d "
233 "at %s", status, timestr);
234 } else
235 addlinef(bp, "Subshell killed by signal %d at %s",
236 WTERMSIG(ret), timestr);
237
238 bp->b_dotp = bfirstlp(bp);
239 bp->b_modes[0] = name_mode("fundamental");
240 bp->b_modes[1] = name_mode("compile");
241 bp->b_nmodes = 1;
242
243 compile_buffer = bp;
244
245 if (chdir(cwd) == -1) {
246 dobeep();
247 ewprintf("Can't change dir back to %s", cwd);
248 return (NULL);
249 }
250 return (bp);
251 }
252
253 static int
compile_goto_error(int f,int n)254 compile_goto_error(int f, int n)
255 {
256 struct buffer *bp;
257 struct mgwin *wp;
258 char *fname, *line, *lp, *ln;
259 int lineno;
260 char *adjf, path[NFILEN];
261 const char *errstr;
262 struct line *last;
263
264 compile_win = curwp;
265 compile_buffer = curbp;
266 last = blastlp(compile_buffer);
267
268 retry:
269 /* last line is compilation result */
270 if (curwp->w_dotp == last)
271 return (FALSE);
272
273 if ((line = linetostr(curwp->w_dotp)) == NULL)
274 return (FALSE);
275 lp = line;
276 if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0')
277 goto fail;
278 if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0')
279 goto fail;
280 lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr);
281 if (errstr)
282 goto fail;
283
284 if (fname && fname[0] != '/') {
285 if (getbufcwd(path, sizeof(path)) == FALSE)
286 goto fail;
287 if (strlcat(path, fname, sizeof(path)) >= sizeof(path))
288 goto fail;
289 adjf = path;
290 } else {
291 adjf = adjustname(fname, TRUE);
292 }
293 free(line);
294
295 if (adjf == NULL)
296 return (FALSE);
297
298 if ((bp = findbuffer(adjf)) == NULL)
299 return (FALSE);
300 if ((wp = popbuf(bp, WNONE)) == NULL)
301 return (FALSE);
302 curbp = bp;
303 curwp = wp;
304 if (bp->b_fname[0] == '\0')
305 readin(adjf);
306 gotoline(FFARG, lineno);
307 return (TRUE);
308 fail:
309 free(line);
310 if (curwp->w_dotp != blastlp(curbp)) {
311 curwp->w_dotp = lforw(curwp->w_dotp);
312 curwp->w_rflag |= WFMOVE;
313 goto retry;
314 }
315 dobeep();
316 ewprintf("No more hits");
317 return (FALSE);
318 }
319
320 int
next_error(int f,int n)321 next_error(int f, int n)
322 {
323 if (compile_win == NULL || compile_buffer == NULL) {
324 dobeep();
325 ewprintf("No compilation active");
326 return (FALSE);
327 }
328 curwp = compile_win;
329 curbp = compile_buffer;
330 if (curwp->w_dotp == blastlp(curbp)) {
331 dobeep();
332 ewprintf("No more hits");
333 return (FALSE);
334 }
335 curwp->w_dotp = lforw(curwp->w_dotp);
336 curwp->w_rflag |= WFMOVE;
337
338 return (compile_goto_error(f, n));
339 }
340
341 /*
342 * Since we don't have variables (we probably should) these are command
343 * processors for changing the values of mode flags.
344 */
345 int
globalwdtoggle(int f,int n)346 globalwdtoggle(int f, int n)
347 {
348 if (f & FFARG)
349 globalwd = n > 0;
350 else
351 globalwd = !globalwd;
352
353 sgarbf = TRUE;
354
355 return (TRUE);
356 }
357