Annotation of src/usr.bin/mg/cscope.c, Revision 1.18
1.18 ! deraadt 1: /* $OpenBSD: cscope.c,v 1.17 2017/10/12 14:12:00 florian Exp $ */
1.2 jasper 2:
1.1 lum 3: /*
1.3 lum 4: * This file is in the public domain.
1.1 lum 5: *
1.15 sunil 6: * Author: Sunil Nimmagadda <sunil@openbsd.org>
1.1 lum 7: */
8:
1.9 bcallah 9: #include <sys/queue.h>
10: #include <sys/stat.h>
1.1 lum 11: #include <sys/types.h>
12: #include <ctype.h>
1.9 bcallah 13: #include <errno.h>
1.1 lum 14: #include <fcntl.h>
15: #include <fnmatch.h>
1.8 guenther 16: #include <limits.h>
1.9 bcallah 17: #include <signal.h>
1.1 lum 18: #include <stdio.h>
19: #include <stdlib.h>
20: #include <string.h>
1.9 bcallah 21: #include <unistd.h>
1.1 lum 22:
23: #include "def.h"
24:
25: #define CSSYMBOL 0
26: #define CSDEFINITION 1
27: #define CSCALLEDFUNCS 2
28: #define CSCALLERFUNCS 3
29: #define CSTEXT 4
30: #define CSEGREP 6
31: #define CSFINDFILE 7
32: #define CSINCLUDES 8
33:
34: struct cstokens {
35: const char *fname;
36: const char *function;
37: const char *lineno;
38: const char *pattern;
39: };
40:
41: struct csmatch {
42: TAILQ_ENTRY(csmatch) entry;
43: int lineno;
44: };
45:
46: struct csrecord {
47: TAILQ_ENTRY(csrecord) entry;
48: char *filename;
49: TAILQ_HEAD(matches, csmatch) matches;
50: };
51:
52: static TAILQ_HEAD(csrecords, csrecord) csrecords = TAILQ_HEAD_INITIALIZER(csrecords);
53: static struct csrecord *addentryr;
54: static struct csrecord *currecord;
55: static struct csmatch *curmatch;
56: static const char *addentryfn;
57: static const char *csprompt[] = {
58: "Find this symbol: ",
59: "Find this global definition: ",
60: "Find functions called by this function: ",
61: "Find functions calling this function: ",
62: "Find this text string: ",
63: "Change this text string: ",
64: "Find this egrep pattern: ",
65: "Find this file: ",
66: "Find files #including this file: "
67: };
68:
69: static int addentry(struct buffer *, char *);
70: static void csflush(void);
71: static int do_cscope(int);
72: static int csexists(const char *);
73: static int getattr(char *, struct cstokens *);
74: static int jumptomatch(void);
75: static void prettyprint(struct buffer *, struct cstokens *);
76: static const char *ltrim(const char *);
77:
78: /*
79: * Find this symbol. Bound to C-c s s
80: */
81: /* ARGSUSED */
82: int
83: cssymbol(int f, int n)
84: {
85: return (do_cscope(CSSYMBOL));
86: }
87:
88: /*
89: * Find this global definition. Bound to C-c s d
90: */
91: /* ARGSUSED */int
92: csdefinition(int f, int n)
93: {
94: return (do_cscope(CSDEFINITION));
95: }
96:
97: /*
98: * Find functions called by this function. Bound to C-c s l
99: */
100: /* ARGSUSED */
101: int
102: csfuncalled(int f, int n)
103: {
104: return (do_cscope(CSCALLEDFUNCS));
105: }
106:
107: /*
108: * Find functions calling this function. Bound to C-c s c
109: */
110: /* ARGSUSED */
111: int
112: cscallerfuncs(int f, int n)
113: {
114: return (do_cscope(CSCALLERFUNCS));
115: }
116:
117: /*
118: * Find this text. Bound to C-c s t
119: */
120: /* ARGSUSED */
121: int
122: csfindtext(int f, int n)
123: {
124: return (do_cscope(CSTEXT));
125: }
126:
127: /*
128: * Find this egrep pattern. Bound to C-c s e
129: */
130: /* ARGSUSED */
131: int
132: csegrep(int f, int n)
133: {
134: return (do_cscope(CSEGREP));
135: }
136:
137: /*
138: * Find this file. Bound to C-c s f
139: */
140: /* ARGSUSED */
141: int
142: csfindfile(int f, int n)
143: {
144: return (do_cscope(CSFINDFILE));
145: }
146:
147: /*
148: * Find files #including this file. Bound to C-c s i
149: */
150: /* ARGSUSED */
151: int
152: csfindinc(int f, int n)
153: {
154: return (do_cscope(CSINCLUDES));
155: }
156:
157: /*
158: * Create list of files to index in the given directory
159: * using cscope-indexer.
160: */
161: /* ARGSUSED */
162: int
163: cscreatelist(int f, int n)
164: {
165: struct buffer *bp;
166: struct stat sb;
167: FILE *fpipe;
168: char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp;
1.17 florian 169: size_t sz;
170: ssize_t len;
1.1 lum 171: int clen;
1.10 jasper 172:
1.17 florian 173: line = NULL;
174: sz = 0;
175:
1.1 lum 176: if (getbufcwd(dir, sizeof(dir)) == FALSE)
177: dir[0] = '\0';
1.10 jasper 178:
179: bufp = eread("Index files in directory: ", dir,
1.1 lum 180: sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
1.10 jasper 181:
1.1 lum 182: if (bufp == NULL)
183: return (ABORT);
184: else if (bufp[0] == '\0')
185: return (FALSE);
1.10 jasper 186:
1.1 lum 187: if (stat(dir, &sb) == -1) {
1.5 lum 188: dobeep();
1.1 lum 189: ewprintf("stat: %s", strerror(errno));
190: return (FALSE);
191: } else if (S_ISDIR(sb.st_mode) == 0) {
1.5 lum 192: dobeep();
1.1 lum 193: ewprintf("%s: Not a directory", dir);
194: return (FALSE);
195: }
1.10 jasper 196:
1.1 lum 197: if (csexists("cscope-indexer") == FALSE) {
1.5 lum 198: dobeep();
1.1 lum 199: ewprintf("no such file or directory, cscope-indexer");
200: return (FALSE);
201: }
1.10 jasper 202:
1.1 lum 203: clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
204: if (clen < 0 || clen >= sizeof(cmd))
205: return (FALSE);
206:
207: if ((fpipe = popen(cmd, "r")) == NULL) {
1.5 lum 208: dobeep();
1.1 lum 209: ewprintf("problem opening pipe");
210: return (FALSE);
211: }
1.10 jasper 212:
1.1 lum 213: bp = bfind("*cscope*", TRUE);
1.4 jsg 214: if (bclear(bp) != TRUE) {
215: pclose(fpipe);
1.1 lum 216: return (FALSE);
1.4 jsg 217: }
1.1 lum 218: bp->b_flag |= BFREADONLY;
219:
220: clen = snprintf(title, sizeof(title), "%s%s",
221: "Creating cscope file list 'cscope.files' in: ", dir);
1.6 jsg 222: if (clen < 0 || clen >= sizeof(title)) {
223: pclose(fpipe);
1.1 lum 224: return (FALSE);
1.6 jsg 225: }
1.1 lum 226: addline(bp, title);
227: addline(bp, "");
1.17 florian 228: while ((len = getline(&line, &sz, fpipe)) != -1) {
229: if (line[len - 1] == '\n')
230: line[len - 1] = '\0';
1.1 lum 231: addline(bp, line);
232: }
1.17 florian 233: free(line);
234: if (ferror(fpipe))
235: ewprintf("Problem reading pipe");
1.1 lum 236: pclose(fpipe);
1.10 jasper 237: return (popbuftop(bp, WNONE));
1.1 lum 238: }
239:
240: /*
241: * Next Symbol. Bound to C-c s n
242: */
243: /* ARGSUSED */
244: int
245: csnextmatch(int f, int n)
246: {
247: struct csrecord *r;
248: struct csmatch *m;
1.10 jasper 249:
1.1 lum 250: if (curmatch == NULL) {
251: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 252: dobeep();
1.1 lum 253: ewprintf("The *cscope* buffer does not exist yet");
254: return (FALSE);
255: }
256: currecord = r;
257: curmatch = TAILQ_FIRST(&r->matches);
258: } else {
259: m = TAILQ_NEXT(curmatch, entry);
260: if (m == NULL) {
261: r = TAILQ_NEXT(currecord, entry);
262: if (r == NULL) {
1.5 lum 263: dobeep();
1.1 lum 264: ewprintf("The end of *cscope* buffer has been"
265: " reached");
266: return (FALSE);
267: } else {
268: currecord = r;
269: curmatch = TAILQ_FIRST(&currecord->matches);
270: }
271: } else
272: curmatch = m;
273: }
274: return (jumptomatch());
275: }
276:
277: /*
278: * Previous Symbol. Bound to C-c s p
279: */
280: /* ARGSUSED */
281: int
282: csprevmatch(int f, int n)
283: {
284: struct csmatch *m;
285: struct csrecord *r;
286:
287: if (curmatch == NULL)
288: return (FALSE);
289: else {
290: m = TAILQ_PREV(curmatch, matches, entry);
291: if (m)
292: curmatch = m;
293: else {
294: r = TAILQ_PREV(currecord, csrecords, entry);
295: if (r == NULL) {
1.5 lum 296: dobeep();
1.1 lum 297: ewprintf("The beginning of *cscope* buffer has"
298: " been reached");
299: return (FALSE);
300: } else {
301: currecord = r;
302: curmatch = TAILQ_LAST(&currecord->matches,
303: matches);
304: }
305: }
306: }
307: return (jumptomatch());
308: }
309:
310: /*
311: * Next file.
312: */
313: int
314: csnextfile(int f, int n)
315: {
316: struct csrecord *r;
1.10 jasper 317:
1.1 lum 318: if (curmatch == NULL) {
319: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 320: dobeep();
1.1 lum 321: ewprintf("The *cscope* buffer does not exist yet");
322: return (FALSE);
323: }
324:
325: } else {
326: if ((r = TAILQ_NEXT(currecord, entry)) == NULL) {
1.5 lum 327: dobeep();
1.1 lum 328: ewprintf("The end of *cscope* buffer has been reached");
329: return (FALSE);
330: }
331: }
332: currecord = r;
333: curmatch = TAILQ_FIRST(&currecord->matches);
1.10 jasper 334: return (jumptomatch());
1.1 lum 335: }
336:
337: /*
338: * Previous file.
339: */
340: int
341: csprevfile(int f, int n)
342: {
343: struct csrecord *r;
1.10 jasper 344:
1.1 lum 345: if (curmatch == NULL) {
346: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 347: dobeep();
1.1 lum 348: ewprintf("The *cscope* buffer does not exist yet");
349: return (FALSE);
350: }
351:
352: } else {
353: if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) {
1.5 lum 354: dobeep();
1.1 lum 355: ewprintf("The beginning of *cscope* buffer has been"
356: " reached");
357: return (FALSE);
358: }
359: }
360: currecord = r;
361: curmatch = TAILQ_FIRST(&currecord->matches);
1.10 jasper 362: return (jumptomatch());
1.1 lum 363: }
364:
365: /*
1.10 jasper 366: * The current symbol location is extracted from currecord->filename and
367: * curmatch->lineno. Load the file similar to filevisit and goto the
1.1 lum 368: * lineno recorded.
369: */
370: int
371: jumptomatch(void)
372: {
373: struct buffer *bp;
374: char *adjf;
1.10 jasper 375:
1.1 lum 376: if (curmatch == NULL || currecord == NULL)
377: return (FALSE);
378: adjf = adjustname(currecord->filename, TRUE);
379: if (adjf == NULL)
380: return (FALSE);
381: if ((bp = findbuffer(adjf)) == NULL)
382: return (FALSE);
383: curbp = bp;
384: if (showbuffer(bp, curwp, WFFULL) != TRUE)
385: return (FALSE);
386: if (bp->b_fname[0] == '\0') {
387: if (readin(adjf) != TRUE)
388: killbuffer(bp);
389: }
390: gotoline(FFARG, curmatch->lineno);
391: return (TRUE);
392: }
393:
394: /*
395: * Ask for the symbol, construct cscope commandline with the symbol
1.10 jasper 396: * and passed in index. Popen cscope, read the output into *cscope*
1.1 lum 397: * buffer and pop it.
398: */
399: int
400: do_cscope(int i)
401: {
402: struct buffer *bp;
403: FILE *fpipe;
404: char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
405: char *p, *buf;
406: int clen, nores = 0;
1.17 florian 407: size_t sz;
408: ssize_t len;
409:
410: buf = NULL;
411: sz = 0;
1.1 lum 412:
413: /* If current buffer isn't a source file just return */
414: if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0) {
1.5 lum 415: dobeep();
1.1 lum 416: ewprintf("C-c s not defined");
417: return (FALSE);
418: }
1.10 jasper 419:
1.1 lum 420: if (curtoken(0, 1, pattern) == FALSE)
1.10 jasper 421: return (FALSE);
1.11 guenther 422: p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
1.1 lum 423: if (p == NULL)
424: return (ABORT);
425: else if (p[0] == '\0')
426: return (FALSE);
427:
428: if (csexists("cscope") == FALSE) {
1.5 lum 429: dobeep();
1.1 lum 430: ewprintf("no such file or directory, cscope");
431: return (FALSE);
432: }
1.10 jasper 433:
1.1 lum 434: csflush();
435: clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
436: i, pattern);
437: if (clen < 0 || clen >= sizeof(cmd))
438: return (FALSE);
439:
440: if ((fpipe = popen(cmd, "r")) == NULL) {
1.5 lum 441: dobeep();
1.1 lum 442: ewprintf("problem opening pipe");
443: return (FALSE);
444: }
1.10 jasper 445:
1.1 lum 446: bp = bfind("*cscope*", TRUE);
1.4 jsg 447: if (bclear(bp) != TRUE) {
448: pclose(fpipe);
1.1 lum 449: return (FALSE);
1.4 jsg 450: }
1.1 lum 451: bp->b_flag |= BFREADONLY;
452:
453: clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
1.6 jsg 454: if (clen < 0 || clen >= sizeof(title)) {
455: pclose(fpipe);
1.1 lum 456: return (FALSE);
1.6 jsg 457: }
1.1 lum 458: addline(bp, title);
459: addline(bp, "");
460: addline(bp, "-------------------------------------------------------------------------------");
1.17 florian 461: while ((len = getline(&buf, &sz, fpipe)) != -1) {
462: if (buf[len - 1] == '\n')
463: buf[len - 1] = '\0';
464: if (addentry(bp, buf) != TRUE) {
465: free(buf);
1.1 lum 466: return (FALSE);
1.17 florian 467: }
1.1 lum 468: nores = 1;
1.17 florian 469: }
470: free(buf);
471: if (ferror(fpipe))
472: ewprintf("Problem reading pipe");
1.1 lum 473: pclose(fpipe);
474: addline(bp, "-------------------------------------------------------------------------------");
475: if (nores == 0)
476: ewprintf("No matches were found.");
477: return (popbuftop(bp, WNONE));
478: }
479:
480: /*
481: * For each line read from cscope output, extract the tokens,
482: * add them to list and pretty print a line in *cscope* buffer.
483: */
484: int
485: addentry(struct buffer *bp, char *csline)
486: {
487: struct csrecord *r;
488: struct csmatch *m;
489: struct cstokens t;
490: int lineno;
491: char buf[BUFSIZ];
492: const char *errstr;
493:
494: r = NULL;
495: if (getattr(csline, &t) == FALSE)
496: return (FALSE);
497:
498: lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
499: if (errstr)
500: return (FALSE);
1.10 jasper 501:
1.1 lum 502: if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
503: if ((r = malloc(sizeof(struct csrecord))) == NULL)
504: return (FALSE);
505: addentryr = r;
506: if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
507: goto cleanup;
508: addentryfn = r->filename;
509: TAILQ_INIT(&r->matches);
510: if ((m = malloc(sizeof(struct csmatch))) == NULL)
511: goto cleanup;
512: m->lineno = lineno;
513: TAILQ_INSERT_TAIL(&r->matches, m, entry);
514: TAILQ_INSERT_TAIL(&csrecords, r, entry);
515: addline(bp, "");
516: if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
517: goto cleanup;
518: addline(bp, buf);
519: } else {
520: if ((m = malloc(sizeof(struct csmatch))) == NULL)
521: goto cleanup;
522: m->lineno = lineno;
523: TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
524: }
525: prettyprint(bp, &t);
526: return (TRUE);
527: cleanup:
528: free(r);
529: return (FALSE);
530: }
531:
532: /*
533: * Cscope line: <filename> <function> <lineno> <pattern>
534: */
535: int
536: getattr(char *line, struct cstokens *t)
537: {
538: char *p;
539:
540: if ((p = strchr(line, ' ')) == NULL)
541: return (FALSE);
542: *p++ = '\0';
543: t->fname = line;
544: line = p;
545:
546: if ((p = strchr(line, ' ')) == NULL)
547: return (FALSE);
548: *p++ = '\0';
549: t->function = line;
550: line = p;
551:
552: if ((p = strchr(line, ' ')) == NULL)
553: return (FALSE);
554: *p++ = '\0';
555: t->lineno = line;
556:
557: if (*p == '\0')
558: return (FALSE);
559: t->pattern = p;
560:
561: return (TRUE);
562: }
563:
564: void
565: prettyprint(struct buffer *bp, struct cstokens *t)
566: {
567: char buf[BUFSIZ];
568:
569: if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
570: t->function, t->lineno, ltrim(t->pattern)) < 0)
571: return;
572: addline(bp, buf);
573: }
574:
575: const char *
576: ltrim(const char *s)
577: {
1.7 guenther 578: while (isblank((unsigned char)*s))
1.1 lum 579: s++;
580: return s;
581: }
582:
583: void
584: csflush(void)
585: {
586: struct csrecord *r;
587: struct csmatch *m;
1.10 jasper 588:
1.1 lum 589: while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
590: free(r->filename);
591: while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
592: TAILQ_REMOVE(&r->matches, m, entry);
593: free(m);
594: }
595: TAILQ_REMOVE(&csrecords, r, entry);
596: free(r);
597: }
598: addentryr = NULL;
599: addentryfn = NULL;
600: currecord = NULL;
601: curmatch = NULL;
602: }
603:
604: /*
605: * Check if the cmd exists in $PATH. Split on ":" and iterate through
606: * all paths in $PATH.
607: */
608: int
609: csexists(const char *cmd)
610: {
1.14 sunil 611: char fname[NFILEN], *dir, *path, *pathc, *tmp;
612: int len, dlen;
1.1 lum 613:
1.14 sunil 614: /* Special case if prog contains '/' */
615: if (strchr(cmd, '/')) {
616: if (access(cmd, F_OK) == -1)
617: return (FALSE);
618: else
619: return (TRUE);
620: }
621: if ((tmp = getenv("PATH")) == NULL)
622: return (FALSE);
623: if ((pathc = path = strndup(tmp, NFILEN)) == NULL) {
624: dobeep();
625: ewprintf("out of memory");
626: return (FALSE);
627: }
628: while ((dir = strsep(&path, ":")) != NULL) {
629: if (*dir == '\0')
1.12 sunil 630: continue;
1.1 lum 631:
1.14 sunil 632: dlen = strlen(dir);
1.16 sunil 633: while (dlen > 0 && dir[dlen-1] == '/')
1.14 sunil 634: dir[--dlen] = '\0'; /* strip trailing '/' */
635:
636: len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
1.18 ! deraadt 637: if (len < 0 || len >= sizeof(fname)) {
1.14 sunil 638: dobeep();
639: ewprintf("path too long");
640: goto cleanup;
641: }
642: if(access(fname, F_OK) == 0) {
643: free(pathc);
644: return (TRUE);
645: }
646: }
1.1 lum 647: cleanup:
648: free(pathc);
649: return (FALSE);
650: }