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