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
22int	 globalwd = FALSE;
23static int	 compile_goto_error(int, int);
24int		 next_error(int, int);
25static int	 grep(int, int);
26static int	 gid(int, int);
27static struct buffer	*compile_mode(const char *, const char *);
28void grep_init(void);
29
30static 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 */
37struct mgwin	*compile_win;
38struct buffer	*compile_buffer;
39
40static PF compile_pf[] = {
41	compile_goto_error
42};
43
44static struct KEYMAPE (1) compilemap = {
45	1,
46	1,
47	rescan,
48	{
49		{ CCHR('M'), CCHR('M'), compile_pf, NULL }
50	}
51};
52
53void
54grep_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
64static int
65grep(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
89int
90compile(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. */
117static int
118gid(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
173struct buffer *
174compile_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
253static int
254compile_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);
308fail:
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
320int
321next_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 */
345int
346globalwdtoggle(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