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