xref: /openbsd/usr.bin/mg/grep.c (revision 5b133f3f)
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