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