Annotation of src/usr.bin/mg/grep.c, Revision 1.27
1.27 ! kjell 1: /* $OpenBSD: grep.c,v 1.26 2005/12/13 07:20:13 kjell Exp $ */
1.1 art 2: /*
1.18 kjell 3: * Copyright (c) 2001 Artur Grabowski <art@openbsd.org>.
4: * Copyright (c) 2005 Kjell Wooding <kjell@openbsd.org>.
5: * All rights reserved.
1.1 art 6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26: */
27:
28: #include "def.h"
29: #include "kbd.h"
30: #include "funmap.h"
31:
1.18 kjell 32: #include <sys/types.h>
1.11 vincent 33: #include <ctype.h>
1.18 kjell 34: #include <libgen.h>
35: #include <time.h>
1.11 vincent 36:
1.13 db 37: static int compile_goto_error(int, int);
1.17 kjell 38: int next_error(int, int);
1.13 db 39: static int grep(int, int);
40: static int compile(int, int);
41: static int gid(int, int);
1.24 deraadt 42: static struct buffer *compile_mode(const char *, const char *, const char *);
1.18 kjell 43: static int getbufcwd(char *, size_t);
1.25 kjell 44: static int xlint(int, int);
1.1 art 45:
46: void grep_init(void);
47:
1.8 vincent 48: static char compile_last_command[NFILEN] = "make ";
49:
1.1 art 50: /*
51: * Hints for next-error
52: *
53: * XXX - need some kind of callback to find out when those get killed.
54: */
1.24 deraadt 55: struct mgwin *compile_win;
56: struct buffer *compile_buffer;
1.1 art 57:
58: static PF compile_pf[] = {
1.13 db 59: compile_goto_error
1.1 art 60: };
61:
62: static struct KEYMAPE (1 + IMAPEXT) compilemap = {
63: 1,
64: 1 + IMAPEXT,
65: rescan,
66: {
1.13 db 67: { CCHR('M'), CCHR('M'), compile_pf, NULL }
1.1 art 68: }
69: };
70:
71: void
72: grep_init(void)
73: {
74: funmap_add(compile_goto_error, "compile-goto-error");
75: funmap_add(next_error, "next-error");
76: funmap_add(grep, "grep");
1.25 kjell 77: funmap_add(xlint, "lint");
1.1 art 78: funmap_add(compile, "compile");
79: funmap_add(gid, "gid");
80: maps_add((KEYMAP *)&compilemap, "compile");
81: }
82:
1.20 kjell 83: /* ARGSUSED */
1.1 art 84: static int
85: grep(int f, int n)
86: {
1.25 kjell 87: char command[NFILEN + 21];
88: char cprompt[NFILEN], *bufp;
1.24 deraadt 89: struct buffer *bp;
90: struct mgwin *wp;
1.18 kjell 91: char path[NFILEN];
92:
93: /* get buffer cwd */
94: if (getbufcwd(path, sizeof(path)) == FALSE) {
95: ewprintf("Failed. "
96: "Can't get working directory of current buffer.");
97: return (FALSE);
98: }
1.1 art 99:
1.25 kjell 100: (void)strlcpy(cprompt, "grep -n ", sizeof(cprompt));
101: if ((bufp = eread("Run grep: ", cprompt, NFILEN,
1.16 kjell 102: EFDEF | EFNEW | EFCR)) == NULL)
1.13 db 103: return (ABORT);
1.16 kjell 104: else if (bufp[0] == '\0')
105: return (FALSE);
1.13 db 106: (void)snprintf(command, sizeof(command), "%s /dev/null", bufp);
1.1 art 107:
1.18 kjell 108: if ((bp = compile_mode("*grep*", command, path)) == NULL)
1.13 db 109: return (FALSE);
1.1 art 110: if ((wp = popbuf(bp)) == NULL)
1.13 db 111: return (FALSE);
1.1 art 112: curbp = bp;
113: compile_win = curwp = wp;
1.13 db 114: return (TRUE);
1.1 art 115: }
116:
1.20 kjell 117: /* ARGSUSED */
1.1 art 118: static int
1.25 kjell 119: xlint(int f, int n)
120: {
121: char command[NFILEN + 16];
122: char cprompt[NFILEN], *bufp;
123: struct buffer *bp;
124: struct mgwin *wp;
125: char path[NFILEN];
126:
127: /* get buffer cwd */
128: if (getbufcwd(path, sizeof(path)) == FALSE) {
129: ewprintf("Failed. "
130: "Can't get working directory of current buffer.");
131: return (FALSE);
132: }
133:
134: (void)strlcpy(cprompt, "make lint ", sizeof(cprompt));
135: if ((bufp = eread("Run lint: ", cprompt, NFILEN,
136: EFDEF | EFNEW | EFCR)) == NULL)
137: return (ABORT);
138: else if (bufp[0] == '\0')
139: return (FALSE);
140: (void)snprintf(command, sizeof(command), "%s 2>&1", bufp);
141:
142: if ((bp = compile_mode("*lint*", command, path)) == NULL)
143: return (FALSE);
144: if ((wp = popbuf(bp)) == NULL)
145: return (FALSE);
146: curbp = bp;
147: compile_win = curwp = wp;
148: return (TRUE);
149: }
150:
151: /* ARGSUSED */
152: static int
1.1 art 153: compile(int f, int n)
154: {
1.13 db 155: char command[NFILEN + 20];
1.25 kjell 156: char cprompt[NFILEN], *bufp;
1.24 deraadt 157: struct buffer *bp;
158: struct mgwin *wp;
1.18 kjell 159: char path[NFILEN];
160:
161: /* get buffer cwd */
162: if (getbufcwd(path, sizeof(path)) == FALSE) {
163: ewprintf("Failed. "
164: "Can't get working directory of current buffer.");
165: return (FALSE);
166: }
1.1 art 167:
1.25 kjell 168: (void)strlcpy(cprompt, compile_last_command, sizeof(cprompt));
169: if ((bufp = eread("Compile command: ", cprompt, NFILEN,
1.16 kjell 170: EFDEF | EFNEW | EFCR)) == NULL)
1.14 jason 171: return (ABORT);
1.16 kjell 172: else if (bufp[0] == '\0')
173: return (FALSE);
1.14 jason 174: if (savebuffers(f, n) == ABORT)
1.13 db 175: return (ABORT);
176: (void)strlcpy(compile_last_command, bufp, sizeof(compile_last_command));
1.1 art 177:
1.13 db 178: (void)snprintf(command, sizeof(command), "%s 2>&1", bufp);
1.1 art 179:
1.18 kjell 180: if ((bp = compile_mode("*compile*", command, path)) == NULL)
1.13 db 181: return (FALSE);
1.1 art 182: if ((wp = popbuf(bp)) == NULL)
1.13 db 183: return (FALSE);
1.1 art 184: curbp = bp;
185: compile_win = curwp = wp;
1.27 ! kjell 186: gotoline(FFARG, 0);
1.13 db 187: return (TRUE);
1.1 art 188: }
189:
190: /* id-utils foo. */
1.20 kjell 191: /* ARGSUSED */
1.1 art 192: static int
193: gid(int f, int n)
194: {
1.13 db 195: char command[NFILEN + 20];
1.25 kjell 196: char cprompt[NFILEN], c, *bufp;
1.24 deraadt 197: struct buffer *bp;
198: struct mgwin *wp;
1.13 db 199: int i, j;
1.18 kjell 200: char path[NFILEN];
201:
202: /* get buffer cwd */
203: if (getbufcwd(path, sizeof(path)) == FALSE) {
204: ewprintf("Failed. "
205: "Can't get working directory of current buffer.");
206: return (FALSE);
207: }
1.1 art 208:
1.11 vincent 209: /* catch ([^\s(){}]+)[\s(){}]* */
210:
211: i = curwp->w_doto;
1.15 cloder 212: /* Skip backwards over delimiters we are currently on */
213: while (i > 0) {
214: c = lgetc(curwp->w_dotp, i);
215: if (isalnum(c) || c == '_')
216: break;
217:
1.11 vincent 218: i--;
1.15 cloder 219: }
220:
1.11 vincent 221: /* Skip the symbol itself */
222: for (; i > 0; i--) {
223: c = lgetc(curwp->w_dotp, i - 1);
1.15 cloder 224: if (!isalnum(c) && c != '_')
1.11 vincent 225: break;
226: }
1.25 kjell 227: /* Fill the symbol in cprompt[] */
228: for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp);
1.11 vincent 229: j++, i++) {
230: c = lgetc(curwp->w_dotp, i);
1.15 cloder 231: if (!isalnum(c) && c != '_')
1.11 vincent 232: break;
1.25 kjell 233: cprompt[j] = c;
1.11 vincent 234: }
1.25 kjell 235: cprompt[j] = '\0';
1.11 vincent 236:
1.25 kjell 237: if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN,
1.16 kjell 238: (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL)
1.13 db 239: return (ABORT);
1.16 kjell 240: else if (bufp[0] == '\0')
241: return (FALSE);
1.25 kjell 242: (void)snprintf(command, sizeof(command), "gid %s", cprompt);
1.1 art 243:
1.18 kjell 244: if ((bp = compile_mode("*gid*", command, path)) == NULL)
1.13 db 245: return (FALSE);
1.1 art 246: if ((wp = popbuf(bp)) == NULL)
1.13 db 247: return (FALSE);
1.1 art 248: curbp = bp;
249: compile_win = curwp = wp;
1.13 db 250: return (TRUE);
1.1 art 251: }
252:
1.24 deraadt 253: struct buffer *
1.21 kjell 254: compile_mode(const char *name, const char *command, const char *path)
1.1 art 255: {
1.24 deraadt 256: struct buffer *bp;
1.26 kjell 257: FILE *fpipe;
1.13 db 258: char *buf;
259: size_t len;
260: int ret;
1.23 deraadt 261: char cwd[NFILEN];
1.18 kjell 262: char timestr[NTIME];
263: time_t t;
1.1 art 264:
265: bp = bfind(name, TRUE);
266: if (bclear(bp) != TRUE)
1.13 db 267: return (NULL);
1.1 art 268:
1.18 kjell 269: addlinef(bp, "cd %s", path);
270: addline(bp, command);
1.1 art 271: addline(bp, "");
272:
1.23 deraadt 273: if (getcwd(cwd, sizeof(cwd)) == NULL)
1.18 kjell 274: panic("Can't get current directory!");
275: if (chdir(path) == -1) {
276: ewprintf("Can't change dir to %s", path);
277: return (NULL);
1.19 deraadt 278: }
1.26 kjell 279: if ((fpipe = popen(command, "r")) == NULL) {
1.1 art 280: ewprintf("Problem opening pipe");
1.13 db 281: return (NULL);
1.1 art 282: }
283: /*
284: * We know that our commands are nice and the last line will end with
285: * a \n, so we don't need to try to deal with the last line problem
286: * in fgetln.
287: */
1.26 kjell 288: while ((buf = fgetln(fpipe, &len)) != NULL) {
1.1 art 289: buf[len - 1] = '\0';
290: addline(bp, buf);
291: }
1.26 kjell 292: ret = pclose(fpipe);
1.18 kjell 293: t = time(NULL);
294: strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t));
1.1 art 295: addline(bp, "");
1.18 kjell 296: if (ret != 0)
297: addlinef(bp, "Command exited abnormally with code %d"
298: " at %s", ret, timestr);
299: else
300: addlinef(bp, "Command finished at %s", timestr);
301:
1.1 art 302: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
303: bp->b_modes[0] = name_mode("fundamental");
304: bp->b_modes[1] = name_mode("compile");
305: bp->b_nmodes = 1;
306:
307: compile_buffer = bp;
308:
1.18 kjell 309: if (chdir(cwd) == -1) {
310: ewprintf("Can't change dir back to %s", cwd);
311: return (NULL);
1.19 deraadt 312: }
1.13 db 313: return (bp);
1.1 art 314: }
315:
1.20 kjell 316: /* ARGSUSED */
1.1 art 317: static int
318: compile_goto_error(int f, int n)
319: {
1.24 deraadt 320: struct buffer *bp;
321: struct mgwin *wp;
1.21 kjell 322: char *fname, *line, *lp, *ln;
1.13 db 323: int lineno, len;
324: char *adjf;
1.21 kjell 325: const char *errstr;
1.24 deraadt 326: struct line *last;
1.1 art 327:
328: compile_win = curwp;
329: compile_buffer = curbp;
1.21 kjell 330: last = lback(compile_buffer->b_linep);
1.22 deraadt 331:
1.21 kjell 332: retry:
333: /* last line is compilation result */
334: if (curwp->w_dotp == last)
335: return (FALSE);
1.1 art 336:
337: len = llength(curwp->w_dotp);
338:
339: if ((line = malloc(len + 1)) == NULL)
1.13 db 340: return (FALSE);
1.1 art 341:
1.6 vincent 342: (void)memcpy(line, curwp->w_dotp->l_text, len);
1.1 art 343: line[len] = '\0';
344:
345: lp = line;
1.21 kjell 346: if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0')
1.1 art 347: goto fail;
1.21 kjell 348: if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0')
1.1 art 349: goto fail;
1.24 deraadt 350: lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr);
1.21 kjell 351: if (errstr)
1.1 art 352: goto fail;
1.10 vincent 353:
354: adjf = adjustname(fname);
1.1 art 355: free(line);
356:
1.7 vincent 357: if (adjf == NULL)
358: return (FALSE);
1.10 vincent 359:
1.1 art 360: if ((bp = findbuffer(adjf)) == NULL)
1.13 db 361: return (FALSE);
1.1 art 362: if ((wp = popbuf(bp)) == NULL)
1.13 db 363: return (FALSE);
1.1 art 364: curbp = bp;
365: curwp = wp;
1.27 ! kjell 366: if (bp->b_fname[0] == '\0')
1.1 art 367: readin(adjf);
368: gotoline(FFARG, lineno);
1.13 db 369: return (TRUE);
1.1 art 370: fail:
1.3 deraadt 371: free(line);
1.1 art 372: if (curwp->w_dotp != lback(curbp->b_linep)) {
373: curwp->w_dotp = lforw(curwp->w_dotp);
374: curwp->w_flag |= WFMOVE;
375: goto retry;
376: }
377: ewprintf("No more hits");
1.13 db 378: return (FALSE);
1.1 art 379: }
380:
1.20 kjell 381: /* ARGSUSED */
1.17 kjell 382: int
1.1 art 383: next_error(int f, int n)
384: {
385: if (compile_win == NULL || compile_buffer == NULL) {
386: ewprintf("No compilation active");
1.13 db 387: return (FALSE);
1.1 art 388: }
389: curwp = compile_win;
390: curbp = compile_buffer;
391: if (curwp->w_dotp == lback(curbp->b_linep)) {
392: ewprintf("No more hits");
1.13 db 393: return (FALSE);
1.1 art 394: }
395: curwp->w_dotp = lforw(curwp->w_dotp);
396: curwp->w_flag |= WFMOVE;
397:
1.13 db 398: return (compile_goto_error(f, n));
1.18 kjell 399: }
400:
401: /*
402: * Return the working directory for the current buffer, terminated
403: * with a '/'. First, try to extract it from the current buffer's
404: * filename. If that fails, use global cwd.
405: */
406: static int
407: getbufcwd(char *path, size_t plen)
408: {
409: char *dname, cwd[NFILEN];
410: if (plen == 0)
411: goto error;
412:
413: if (curbp->b_fname && curbp->b_fname[0] != '\0' &&
414: (dname = dirname(curbp->b_fname)) != NULL) {
415: if (strlcpy(path, dname, plen) >= plen)
416: goto error;
417: if (strlcat(path, "/", plen) >= plen)
418: goto error;
419: } else {
420: if ((dname = getcwd(cwd, sizeof(cwd))) == NULL)
421: goto error;
422: if (strlcpy(path, dname, plen) >= plen)
423: goto error;
424: }
425: return (TRUE);
426: error:
427: path = NULL;
428: return (FALSE);
1.1 art 429: }