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