Annotation of src/usr.bin/mg/cscope.c, Revision 1.12
1.12 ! sunil 1: /* $OpenBSD: cscope.c,v 1.11 2015/09/29 02:07:49 guenther 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.3 lum 6: * Author: Sunil Nimmagadda <sunil@sunilnimmagadda.com>
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;
169: size_t len;
170: int clen;
1.10 jasper 171:
1.1 lum 172: if (getbufcwd(dir, sizeof(dir)) == FALSE)
173: dir[0] = '\0';
1.10 jasper 174:
175: bufp = eread("Index files in directory: ", dir,
1.1 lum 176: sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
1.10 jasper 177:
1.1 lum 178: if (bufp == NULL)
179: return (ABORT);
180: else if (bufp[0] == '\0')
181: return (FALSE);
1.10 jasper 182:
1.1 lum 183: if (stat(dir, &sb) == -1) {
1.5 lum 184: dobeep();
1.1 lum 185: ewprintf("stat: %s", strerror(errno));
186: return (FALSE);
187: } else if (S_ISDIR(sb.st_mode) == 0) {
1.5 lum 188: dobeep();
1.1 lum 189: ewprintf("%s: Not a directory", dir);
190: return (FALSE);
191: }
1.10 jasper 192:
1.1 lum 193: if (csexists("cscope-indexer") == FALSE) {
1.5 lum 194: dobeep();
1.1 lum 195: ewprintf("no such file or directory, cscope-indexer");
196: return (FALSE);
197: }
1.10 jasper 198:
1.1 lum 199: clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
200: if (clen < 0 || clen >= sizeof(cmd))
201: return (FALSE);
202:
203: if ((fpipe = popen(cmd, "r")) == NULL) {
1.5 lum 204: dobeep();
1.1 lum 205: ewprintf("problem opening pipe");
206: return (FALSE);
207: }
1.10 jasper 208:
1.1 lum 209: bp = bfind("*cscope*", TRUE);
1.4 jsg 210: if (bclear(bp) != TRUE) {
211: pclose(fpipe);
1.1 lum 212: return (FALSE);
1.4 jsg 213: }
1.1 lum 214: bp->b_flag |= BFREADONLY;
215:
216: clen = snprintf(title, sizeof(title), "%s%s",
217: "Creating cscope file list 'cscope.files' in: ", dir);
1.6 jsg 218: if (clen < 0 || clen >= sizeof(title)) {
219: pclose(fpipe);
1.1 lum 220: return (FALSE);
1.6 jsg 221: }
1.1 lum 222: addline(bp, title);
223: addline(bp, "");
224: /* All lines are NUL terminated */
225: while ((line = fgetln(fpipe, &len)) != NULL) {
226: line[len - 1] = '\0';
227: addline(bp, line);
228: }
229: pclose(fpipe);
1.10 jasper 230: return (popbuftop(bp, WNONE));
1.1 lum 231: }
232:
233: /*
234: * Next Symbol. Bound to C-c s n
235: */
236: /* ARGSUSED */
237: int
238: csnextmatch(int f, int n)
239: {
240: struct csrecord *r;
241: struct csmatch *m;
1.10 jasper 242:
1.1 lum 243: if (curmatch == NULL) {
244: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 245: dobeep();
1.1 lum 246: ewprintf("The *cscope* buffer does not exist yet");
247: return (FALSE);
248: }
249: currecord = r;
250: curmatch = TAILQ_FIRST(&r->matches);
251: } else {
252: m = TAILQ_NEXT(curmatch, entry);
253: if (m == NULL) {
254: r = TAILQ_NEXT(currecord, entry);
255: if (r == NULL) {
1.5 lum 256: dobeep();
1.1 lum 257: ewprintf("The end of *cscope* buffer has been"
258: " reached");
259: return (FALSE);
260: } else {
261: currecord = r;
262: curmatch = TAILQ_FIRST(&currecord->matches);
263: }
264: } else
265: curmatch = m;
266: }
267: return (jumptomatch());
268: }
269:
270: /*
271: * Previous Symbol. Bound to C-c s p
272: */
273: /* ARGSUSED */
274: int
275: csprevmatch(int f, int n)
276: {
277: struct csmatch *m;
278: struct csrecord *r;
279:
280: if (curmatch == NULL)
281: return (FALSE);
282: else {
283: m = TAILQ_PREV(curmatch, matches, entry);
284: if (m)
285: curmatch = m;
286: else {
287: r = TAILQ_PREV(currecord, csrecords, entry);
288: if (r == NULL) {
1.5 lum 289: dobeep();
1.1 lum 290: ewprintf("The beginning of *cscope* buffer has"
291: " been reached");
292: return (FALSE);
293: } else {
294: currecord = r;
295: curmatch = TAILQ_LAST(&currecord->matches,
296: matches);
297: }
298: }
299: }
300: return (jumptomatch());
301: }
302:
303: /*
304: * Next file.
305: */
306: int
307: csnextfile(int f, int n)
308: {
309: struct csrecord *r;
1.10 jasper 310:
1.1 lum 311: if (curmatch == NULL) {
312: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 313: dobeep();
1.1 lum 314: ewprintf("The *cscope* buffer does not exist yet");
315: return (FALSE);
316: }
317:
318: } else {
319: if ((r = TAILQ_NEXT(currecord, entry)) == NULL) {
1.5 lum 320: dobeep();
1.1 lum 321: ewprintf("The end of *cscope* buffer has been reached");
322: return (FALSE);
323: }
324: }
325: currecord = r;
326: curmatch = TAILQ_FIRST(&currecord->matches);
1.10 jasper 327: return (jumptomatch());
1.1 lum 328: }
329:
330: /*
331: * Previous file.
332: */
333: int
334: csprevfile(int f, int n)
335: {
336: struct csrecord *r;
1.10 jasper 337:
1.1 lum 338: if (curmatch == NULL) {
339: if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
1.5 lum 340: dobeep();
1.1 lum 341: ewprintf("The *cscope* buffer does not exist yet");
342: return (FALSE);
343: }
344:
345: } else {
346: if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) {
1.5 lum 347: dobeep();
1.1 lum 348: ewprintf("The beginning of *cscope* buffer has been"
349: " reached");
350: return (FALSE);
351: }
352: }
353: currecord = r;
354: curmatch = TAILQ_FIRST(&currecord->matches);
1.10 jasper 355: return (jumptomatch());
1.1 lum 356: }
357:
358: /*
1.10 jasper 359: * The current symbol location is extracted from currecord->filename and
360: * curmatch->lineno. Load the file similar to filevisit and goto the
1.1 lum 361: * lineno recorded.
362: */
363: int
364: jumptomatch(void)
365: {
366: struct buffer *bp;
367: char *adjf;
1.10 jasper 368:
1.1 lum 369: if (curmatch == NULL || currecord == NULL)
370: return (FALSE);
371: adjf = adjustname(currecord->filename, TRUE);
372: if (adjf == NULL)
373: return (FALSE);
374: if ((bp = findbuffer(adjf)) == NULL)
375: return (FALSE);
376: curbp = bp;
377: if (showbuffer(bp, curwp, WFFULL) != TRUE)
378: return (FALSE);
379: if (bp->b_fname[0] == '\0') {
380: if (readin(adjf) != TRUE)
381: killbuffer(bp);
382: }
383: gotoline(FFARG, curmatch->lineno);
384: return (TRUE);
385: }
386:
387: /*
388: * Ask for the symbol, construct cscope commandline with the symbol
1.10 jasper 389: * and passed in index. Popen cscope, read the output into *cscope*
1.1 lum 390: * buffer and pop it.
391: */
392: int
393: do_cscope(int i)
394: {
395: struct buffer *bp;
396: FILE *fpipe;
397: char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
398: char *p, *buf;
399: int clen, nores = 0;
400: size_t len;
401:
402: /* If current buffer isn't a source file just return */
403: if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0) {
1.5 lum 404: dobeep();
1.1 lum 405: ewprintf("C-c s not defined");
406: return (FALSE);
407: }
1.10 jasper 408:
1.1 lum 409: if (curtoken(0, 1, pattern) == FALSE)
1.10 jasper 410: return (FALSE);
1.11 guenther 411: p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
1.1 lum 412: if (p == NULL)
413: return (ABORT);
414: else if (p[0] == '\0')
415: return (FALSE);
416:
417: if (csexists("cscope") == FALSE) {
1.5 lum 418: dobeep();
1.1 lum 419: ewprintf("no such file or directory, cscope");
420: return (FALSE);
421: }
1.10 jasper 422:
1.1 lum 423: csflush();
424: clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
425: i, pattern);
426: if (clen < 0 || clen >= sizeof(cmd))
427: return (FALSE);
428:
429: if ((fpipe = popen(cmd, "r")) == NULL) {
1.5 lum 430: dobeep();
1.1 lum 431: ewprintf("problem opening pipe");
432: return (FALSE);
433: }
1.10 jasper 434:
1.1 lum 435: bp = bfind("*cscope*", TRUE);
1.4 jsg 436: if (bclear(bp) != TRUE) {
437: pclose(fpipe);
1.1 lum 438: return (FALSE);
1.4 jsg 439: }
1.1 lum 440: bp->b_flag |= BFREADONLY;
441:
442: clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
1.6 jsg 443: if (clen < 0 || clen >= sizeof(title)) {
444: pclose(fpipe);
1.1 lum 445: return (FALSE);
1.6 jsg 446: }
1.1 lum 447: addline(bp, title);
448: addline(bp, "");
449: addline(bp, "-------------------------------------------------------------------------------");
450: /* All lines are NUL terminated */
451: while ((buf = fgetln(fpipe, &len)) != NULL) {
452: buf[len - 1] = '\0';
453: if (addentry(bp, buf) != TRUE)
454: return (FALSE);
455: nores = 1;
1.10 jasper 456: };
1.1 lum 457: pclose(fpipe);
458: addline(bp, "-------------------------------------------------------------------------------");
459: if (nores == 0)
460: ewprintf("No matches were found.");
461: return (popbuftop(bp, WNONE));
462: }
463:
464: /*
465: * For each line read from cscope output, extract the tokens,
466: * add them to list and pretty print a line in *cscope* buffer.
467: */
468: int
469: addentry(struct buffer *bp, char *csline)
470: {
471: struct csrecord *r;
472: struct csmatch *m;
473: struct cstokens t;
474: int lineno;
475: char buf[BUFSIZ];
476: const char *errstr;
477:
478: r = NULL;
479: if (getattr(csline, &t) == FALSE)
480: return (FALSE);
481:
482: lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
483: if (errstr)
484: return (FALSE);
1.10 jasper 485:
1.1 lum 486: if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
487: if ((r = malloc(sizeof(struct csrecord))) == NULL)
488: return (FALSE);
489: addentryr = r;
490: if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
491: goto cleanup;
492: addentryfn = r->filename;
493: TAILQ_INIT(&r->matches);
494: if ((m = malloc(sizeof(struct csmatch))) == NULL)
495: goto cleanup;
496: m->lineno = lineno;
497: TAILQ_INSERT_TAIL(&r->matches, m, entry);
498: TAILQ_INSERT_TAIL(&csrecords, r, entry);
499: addline(bp, "");
500: if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
501: goto cleanup;
502: addline(bp, buf);
503: } else {
504: if ((m = malloc(sizeof(struct csmatch))) == NULL)
505: goto cleanup;
506: m->lineno = lineno;
507: TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
508: }
509: prettyprint(bp, &t);
510: return (TRUE);
511: cleanup:
512: free(r);
513: return (FALSE);
514: }
515:
516: /*
517: * Cscope line: <filename> <function> <lineno> <pattern>
518: */
519: int
520: getattr(char *line, struct cstokens *t)
521: {
522: char *p;
523:
524: if ((p = strchr(line, ' ')) == NULL)
525: return (FALSE);
526: *p++ = '\0';
527: t->fname = line;
528: line = p;
529:
530: if ((p = strchr(line, ' ')) == NULL)
531: return (FALSE);
532: *p++ = '\0';
533: t->function = line;
534: line = p;
535:
536: if ((p = strchr(line, ' ')) == NULL)
537: return (FALSE);
538: *p++ = '\0';
539: t->lineno = line;
540:
541: if (*p == '\0')
542: return (FALSE);
543: t->pattern = p;
544:
545: return (TRUE);
546: }
547:
548: void
549: prettyprint(struct buffer *bp, struct cstokens *t)
550: {
551: char buf[BUFSIZ];
552:
553: if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
554: t->function, t->lineno, ltrim(t->pattern)) < 0)
555: return;
556: addline(bp, buf);
557: }
558:
559: const char *
560: ltrim(const char *s)
561: {
1.7 guenther 562: while (isblank((unsigned char)*s))
1.1 lum 563: s++;
564: return s;
565: }
566:
567: void
568: csflush(void)
569: {
570: struct csrecord *r;
571: struct csmatch *m;
1.10 jasper 572:
1.1 lum 573: while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
574: free(r->filename);
575: while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
576: TAILQ_REMOVE(&r->matches, m, entry);
577: free(m);
578: }
579: TAILQ_REMOVE(&csrecords, r, entry);
580: free(r);
581: }
582: addentryr = NULL;
583: addentryfn = NULL;
584: currecord = NULL;
585: curmatch = NULL;
586: }
587:
588: /*
589: * Check if the cmd exists in $PATH. Split on ":" and iterate through
590: * all paths in $PATH.
591: */
592: int
593: csexists(const char *cmd)
594: {
595: char fname[NFILEN], *dir, *path, *pathc, *tmp;
596: int cmdlen, dlen;
597:
598: /* Special case if prog contains '/' */
599: if (strchr(cmd, '/')) {
600: if (access(cmd, F_OK) == -1)
601: return (FALSE);
602: else
603: return (TRUE);
604: }
605: if ((tmp = getenv("PATH")) == NULL)
606: return (FALSE);
607: if ((pathc = path = strndup(tmp, NFILEN)) == NULL) {
1.5 lum 608: dobeep();
1.1 lum 609: ewprintf("out of memory");
610: return (FALSE);
611: }
612: cmdlen = strlen(cmd);
613: while ((dir = strsep(&path, ":")) != NULL) {
614: if (*dir == '\0')
1.12 ! sunil 615: continue;
1.1 lum 616:
617: dlen = strlen(dir);
618: while (dir[dlen-1] == '/')
619: dir[--dlen] = '\0'; /* strip trailing '/' */
620:
621: if (dlen + 1 + cmdlen >= sizeof(fname)) {
1.5 lum 622: dobeep();
1.1 lum 623: ewprintf("path too long");
624: goto cleanup;
625: }
626: snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
627: if(access(fname, F_OK) == 0) {
628: free(pathc);
629: return (TRUE);
630: }
631: }
632: cleanup:
633: free(pathc);
634: return (FALSE);
635: }