Annotation of src/usr.bin/mg/grep.c, Revision 1.26
1.26 ! kjell 1: /* $OpenBSD: grep.c,v 1.25 2005/12/13 02:08:06 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.13 db 186: return (TRUE);
1.1 art 187: }
188:
189: /* id-utils foo. */
1.20 kjell 190: /* ARGSUSED */
1.1 art 191: static int
192: gid(int f, int n)
193: {
1.13 db 194: char command[NFILEN + 20];
1.25 kjell 195: char cprompt[NFILEN], c, *bufp;
1.24 deraadt 196: struct buffer *bp;
197: struct mgwin *wp;
1.13 db 198: int i, j;
1.18 kjell 199: char path[NFILEN];
200:
201: /* get buffer cwd */
202: if (getbufcwd(path, sizeof(path)) == FALSE) {
203: ewprintf("Failed. "
204: "Can't get working directory of current buffer.");
205: return (FALSE);
206: }
1.1 art 207:
1.11 vincent 208: /* catch ([^\s(){}]+)[\s(){}]* */
209:
210: i = curwp->w_doto;
1.15 cloder 211: /* Skip backwards over delimiters we are currently on */
212: while (i > 0) {
213: c = lgetc(curwp->w_dotp, i);
214: if (isalnum(c) || c == '_')
215: break;
216:
1.11 vincent 217: i--;
1.15 cloder 218: }
219:
1.11 vincent 220: /* Skip the symbol itself */
221: for (; i > 0; i--) {
222: c = lgetc(curwp->w_dotp, i - 1);
1.15 cloder 223: if (!isalnum(c) && c != '_')
1.11 vincent 224: break;
225: }
1.25 kjell 226: /* Fill the symbol in cprompt[] */
227: for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp);
1.11 vincent 228: j++, i++) {
229: c = lgetc(curwp->w_dotp, i);
1.15 cloder 230: if (!isalnum(c) && c != '_')
1.11 vincent 231: break;
1.25 kjell 232: cprompt[j] = c;
1.11 vincent 233: }
1.25 kjell 234: cprompt[j] = '\0';
1.11 vincent 235:
1.25 kjell 236: if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN,
1.16 kjell 237: (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL)
1.13 db 238: return (ABORT);
1.16 kjell 239: else if (bufp[0] == '\0')
240: return (FALSE);
1.25 kjell 241: (void)snprintf(command, sizeof(command), "gid %s", cprompt);
1.1 art 242:
1.18 kjell 243: if ((bp = compile_mode("*gid*", command, path)) == NULL)
1.13 db 244: return (FALSE);
1.1 art 245: if ((wp = popbuf(bp)) == NULL)
1.13 db 246: return (FALSE);
1.1 art 247: curbp = bp;
248: compile_win = curwp = wp;
1.13 db 249: return (TRUE);
1.1 art 250: }
251:
1.24 deraadt 252: struct buffer *
1.21 kjell 253: compile_mode(const char *name, const char *command, const char *path)
1.1 art 254: {
1.24 deraadt 255: struct buffer *bp;
1.26 ! kjell 256: FILE *fpipe;
1.13 db 257: char *buf;
258: size_t len;
259: int ret;
1.23 deraadt 260: char cwd[NFILEN];
1.18 kjell 261: char timestr[NTIME];
262: time_t t;
1.1 art 263:
264: bp = bfind(name, TRUE);
265: if (bclear(bp) != TRUE)
1.13 db 266: return (NULL);
1.1 art 267:
1.18 kjell 268: addlinef(bp, "cd %s", path);
269: addline(bp, command);
1.1 art 270: addline(bp, "");
271:
1.23 deraadt 272: if (getcwd(cwd, sizeof(cwd)) == NULL)
1.18 kjell 273: panic("Can't get current directory!");
274: if (chdir(path) == -1) {
275: ewprintf("Can't change dir to %s", path);
276: return (NULL);
1.19 deraadt 277: }
1.26 ! kjell 278: if ((fpipe = popen(command, "r")) == NULL) {
1.1 art 279: ewprintf("Problem opening pipe");
1.13 db 280: return (NULL);
1.1 art 281: }
282: /*
283: * We know that our commands are nice and the last line will end with
284: * a \n, so we don't need to try to deal with the last line problem
285: * in fgetln.
286: */
1.26 ! kjell 287: while ((buf = fgetln(fpipe, &len)) != NULL) {
1.1 art 288: buf[len - 1] = '\0';
289: addline(bp, buf);
290: }
1.26 ! kjell 291: ret = pclose(fpipe);
1.18 kjell 292: t = time(NULL);
293: strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t));
1.1 art 294: addline(bp, "");
1.18 kjell 295: if (ret != 0)
296: addlinef(bp, "Command exited abnormally with code %d"
297: " at %s", ret, timestr);
298: else
299: addlinef(bp, "Command finished at %s", timestr);
300:
1.1 art 301: bp->b_dotp = lforw(bp->b_linep); /* go to first line */
302: bp->b_modes[0] = name_mode("fundamental");
303: bp->b_modes[1] = name_mode("compile");
304: bp->b_nmodes = 1;
305:
306: compile_buffer = bp;
307:
1.18 kjell 308: if (chdir(cwd) == -1) {
309: ewprintf("Can't change dir back to %s", cwd);
310: return (NULL);
1.19 deraadt 311: }
1.13 db 312: return (bp);
1.1 art 313: }
314:
1.20 kjell 315: /* ARGSUSED */
1.1 art 316: static int
317: compile_goto_error(int f, int n)
318: {
1.24 deraadt 319: struct buffer *bp;
320: struct mgwin *wp;
1.21 kjell 321: char *fname, *line, *lp, *ln;
1.13 db 322: int lineno, len;
323: char *adjf;
1.21 kjell 324: const char *errstr;
1.24 deraadt 325: struct line *last;
1.1 art 326:
327: compile_win = curwp;
328: compile_buffer = curbp;
1.21 kjell 329: last = lback(compile_buffer->b_linep);
1.22 deraadt 330:
1.21 kjell 331: retry:
332: /* last line is compilation result */
333: if (curwp->w_dotp == last)
334: return (FALSE);
1.1 art 335:
336: len = llength(curwp->w_dotp);
337:
338: if ((line = malloc(len + 1)) == NULL)
1.13 db 339: return (FALSE);
1.1 art 340:
1.6 vincent 341: (void)memcpy(line, curwp->w_dotp->l_text, len);
1.1 art 342: line[len] = '\0';
343:
344: lp = line;
1.21 kjell 345: if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0')
1.1 art 346: goto fail;
1.21 kjell 347: if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0')
1.1 art 348: goto fail;
1.24 deraadt 349: lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr);
1.21 kjell 350: if (errstr)
1.1 art 351: goto fail;
1.10 vincent 352:
353: adjf = adjustname(fname);
1.1 art 354: free(line);
355:
1.7 vincent 356: if (adjf == NULL)
357: return (FALSE);
1.10 vincent 358:
1.1 art 359: if ((bp = findbuffer(adjf)) == NULL)
1.13 db 360: return (FALSE);
1.1 art 361: if ((wp = popbuf(bp)) == NULL)
1.13 db 362: return (FALSE);
1.1 art 363: curbp = bp;
364: curwp = wp;
365: if (bp->b_fname[0] == 0)
366: readin(adjf);
367: gotoline(FFARG, lineno);
1.13 db 368: return (TRUE);
1.1 art 369: fail:
1.3 deraadt 370: free(line);
1.1 art 371: if (curwp->w_dotp != lback(curbp->b_linep)) {
372: curwp->w_dotp = lforw(curwp->w_dotp);
373: curwp->w_flag |= WFMOVE;
374: goto retry;
375: }
376: ewprintf("No more hits");
1.13 db 377: return (FALSE);
1.1 art 378: }
379:
1.20 kjell 380: /* ARGSUSED */
1.17 kjell 381: int
1.1 art 382: next_error(int f, int n)
383: {
384: if (compile_win == NULL || compile_buffer == NULL) {
385: ewprintf("No compilation active");
1.13 db 386: return (FALSE);
1.1 art 387: }
388: curwp = compile_win;
389: curbp = compile_buffer;
390: if (curwp->w_dotp == lback(curbp->b_linep)) {
391: ewprintf("No more hits");
1.13 db 392: return (FALSE);
1.1 art 393: }
394: curwp->w_dotp = lforw(curwp->w_dotp);
395: curwp->w_flag |= WFMOVE;
396:
1.13 db 397: return (compile_goto_error(f, n));
1.18 kjell 398: }
399:
400: /*
401: * Return the working directory for the current buffer, terminated
402: * with a '/'. First, try to extract it from the current buffer's
403: * filename. If that fails, use global cwd.
404: */
405: static int
406: getbufcwd(char *path, size_t plen)
407: {
408: char *dname, cwd[NFILEN];
409: if (plen == 0)
410: goto error;
411:
412: if (curbp->b_fname && curbp->b_fname[0] != '\0' &&
413: (dname = dirname(curbp->b_fname)) != NULL) {
414: if (strlcpy(path, dname, plen) >= plen)
415: goto error;
416: if (strlcat(path, "/", plen) >= plen)
417: goto error;
418: } else {
419: if ((dname = getcwd(cwd, sizeof(cwd))) == NULL)
420: goto error;
421: if (strlcpy(path, dname, plen) >= plen)
422: goto error;
423: }
424: return (TRUE);
425: error:
426: path = NULL;
427: return (FALSE);
1.1 art 428: }