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