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