1 /* $OpenBSD: cscope.c,v 1.3 2012/07/02 08:08:31 lum Exp $ */
4 * This file is in the public domain.
6 * Author: Sunil Nimmagadda <sunil@sunilnimmagadda.com>
11 #include <sys/queue.h>
23 #define CSDEFINITION 1
24 #define CSCALLEDFUNCS 2
25 #define CSCALLERFUNCS 3
39 TAILQ_ENTRY(csmatch) entry;
44 TAILQ_ENTRY(csrecord) entry;
46 TAILQ_HEAD(matches, csmatch) matches;
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[] = {
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: ",
63 "Find files #including this file: "
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 *);
76 * Find this symbol. Bound to C-c s s
80 cssymbol(int f, int n)
82 return (do_cscope(CSSYMBOL));
86 * Find this global definition. Bound to C-c s d
89 csdefinition(int f, int n)
91 return (do_cscope(CSDEFINITION));
95 * Find functions called by this function. Bound to C-c s l
99 csfuncalled(int f, int n)
101 return (do_cscope(CSCALLEDFUNCS));
105 * Find functions calling this function. Bound to C-c s c
109 cscallerfuncs(int f, int n)
111 return (do_cscope(CSCALLERFUNCS));
115 * Find this text. Bound to C-c s t
119 csfindtext(int f, int n)
121 return (do_cscope(CSTEXT));
125 * Find this egrep pattern. Bound to C-c s e
129 csegrep(int f, int n)
131 return (do_cscope(CSEGREP));
135 * Find this file. Bound to C-c s f
139 csfindfile(int f, int n)
141 return (do_cscope(CSFINDFILE));
145 * Find files #including this file. Bound to C-c s i
149 csfindinc(int f, int n)
151 return (do_cscope(CSINCLUDES));
155 * Create list of files to index in the given directory
156 * using cscope-indexer.
160 cscreatelist(int f, int n)
165 char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp;
169 if (getbufcwd(dir, sizeof(dir)) == FALSE)
172 bufp = eread("Index files in directory: ", dir,
173 sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
177 else if (bufp[0] == '\0')
180 if (stat(dir, &sb) == -1) {
181 ewprintf("stat: %s", strerror(errno));
183 } else if (S_ISDIR(sb.st_mode) == 0) {
184 ewprintf("%s: Not a directory", dir);
188 if (csexists("cscope-indexer") == FALSE) {
189 ewprintf("no such file or directory, cscope-indexer");
193 clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
194 if (clen < 0 || clen >= sizeof(cmd))
197 if ((fpipe = popen(cmd, "r")) == NULL) {
198 ewprintf("problem opening pipe");
202 bp = bfind("*cscope*", TRUE);
203 if (bclear(bp) != TRUE)
205 bp->b_flag |= BFREADONLY;
207 clen = snprintf(title, sizeof(title), "%s%s",
208 "Creating cscope file list 'cscope.files' in: ", dir);
209 if (clen < 0 || clen >= sizeof(title))
213 /* All lines are NUL terminated */
214 while ((line = fgetln(fpipe, &len)) != NULL) {
215 line[len - 1] = '\0';
219 return (popbuftop(bp, WNONE));
223 * Next Symbol. Bound to C-c s n
227 csnextmatch(int f, int n)
232 if (curmatch == NULL) {
233 if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
234 ewprintf("The *cscope* buffer does not exist yet");
238 curmatch = TAILQ_FIRST(&r->matches);
240 m = TAILQ_NEXT(curmatch, entry);
242 r = TAILQ_NEXT(currecord, entry);
244 ewprintf("The end of *cscope* buffer has been"
249 curmatch = TAILQ_FIRST(&currecord->matches);
254 return (jumptomatch());
258 * Previous Symbol. Bound to C-c s p
262 csprevmatch(int f, int n)
267 if (curmatch == NULL)
270 m = TAILQ_PREV(curmatch, matches, entry);
274 r = TAILQ_PREV(currecord, csrecords, entry);
276 ewprintf("The beginning of *cscope* buffer has"
281 curmatch = TAILQ_LAST(&currecord->matches,
286 return (jumptomatch());
293 csnextfile(int f, int n)
297 if (curmatch == NULL) {
298 if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
299 ewprintf("The *cscope* buffer does not exist yet");
304 if ((r = TAILQ_NEXT(currecord, entry)) == NULL) {
305 ewprintf("The end of *cscope* buffer has been reached");
310 curmatch = TAILQ_FIRST(&currecord->matches);
311 return (jumptomatch());
318 csprevfile(int f, int n)
322 if (curmatch == NULL) {
323 if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
324 ewprintf("The *cscope* buffer does not exist yet");
329 if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) {
330 ewprintf("The beginning of *cscope* buffer has been"
336 curmatch = TAILQ_FIRST(&currecord->matches);
337 return (jumptomatch());
341 * The current symbol location is extracted from currecord->filename and
342 * curmatch->lineno. Load the file similar to filevisit and goto the
351 if (curmatch == NULL || currecord == NULL)
353 adjf = adjustname(currecord->filename, TRUE);
356 if ((bp = findbuffer(adjf)) == NULL)
359 if (showbuffer(bp, curwp, WFFULL) != TRUE)
361 if (bp->b_fname[0] == '\0') {
362 if (readin(adjf) != TRUE)
365 gotoline(FFARG, curmatch->lineno);
371 * Ask for the symbol, construct cscope commandline with the symbol
372 * and passed in index. Popen cscope, read the output into *cscope*
380 char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
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");
391 if (curtoken(0, 1, pattern) == FALSE)
393 p = eread(csprompt[i], pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF);
396 else if (p[0] == '\0')
399 if (csexists("cscope") == FALSE) {
400 ewprintf("no such file or directory, cscope");
405 clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
407 if (clen < 0 || clen >= sizeof(cmd))
410 if ((fpipe = popen(cmd, "r")) == NULL) {
411 ewprintf("problem opening pipe");
415 bp = bfind("*cscope*", TRUE);
416 if (bclear(bp) != TRUE)
418 bp->b_flag |= BFREADONLY;
420 clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
421 if (clen < 0 || clen >= sizeof(title))
425 addline(bp, "-------------------------------------------------------------------------------");
426 /* All lines are NUL terminated */
427 while ((buf = fgetln(fpipe, &len)) != NULL) {
429 if (addentry(bp, buf) != TRUE)
434 addline(bp, "-------------------------------------------------------------------------------");
436 ewprintf("No matches were found.");
437 return (popbuftop(bp, WNONE));
441 * For each line read from cscope output, extract the tokens,
442 * add them to list and pretty print a line in *cscope* buffer.
445 addentry(struct buffer *bp, char *csline)
455 if (getattr(csline, &t) == FALSE)
458 lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
462 if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
463 if ((r = malloc(sizeof(struct csrecord))) == NULL)
466 if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
468 addentryfn = r->filename;
469 TAILQ_INIT(&r->matches);
470 if ((m = malloc(sizeof(struct csmatch))) == NULL)
473 TAILQ_INSERT_TAIL(&r->matches, m, entry);
474 TAILQ_INSERT_TAIL(&csrecords, r, entry);
476 if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
480 if ((m = malloc(sizeof(struct csmatch))) == NULL)
483 TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
493 * Cscope line: <filename> <function> <lineno> <pattern>
496 getattr(char *line, struct cstokens *t)
500 if ((p = strchr(line, ' ')) == NULL)
506 if ((p = strchr(line, ' ')) == NULL)
512 if ((p = strchr(line, ' ')) == NULL)
525 prettyprint(struct buffer *bp, struct cstokens *t)
529 if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
530 t->function, t->lineno, ltrim(t->pattern)) < 0)
549 while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
551 while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
552 TAILQ_REMOVE(&r->matches, m, entry);
555 TAILQ_REMOVE(&csrecords, r, entry);
565 * Check if the cmd exists in $PATH. Split on ":" and iterate through
566 * all paths in $PATH.
569 csexists(const char *cmd)
571 char fname[NFILEN], *dir, *path, *pathc, *tmp;
574 /* Special case if prog contains '/' */
575 if (strchr(cmd, '/')) {
576 if (access(cmd, F_OK) == -1)
581 if ((tmp = getenv("PATH")) == NULL)
583 if ((pathc = path = strndup(tmp, NFILEN)) == NULL) {
584 ewprintf("out of memory");
587 cmdlen = strlen(cmd);
588 while ((dir = strsep(&path, ":")) != NULL) {
593 while (dir[dlen-1] == '/')
594 dir[--dlen] = '\0'; /* strip trailing '/' */
596 if (dlen + 1 + cmdlen >= sizeof(fname)) {
597 ewprintf("path too long");
600 snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
601 if(access(fname, F_OK) == 0) {