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