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