]> pd.if.org Git - pd_readline/blobdiff - mg/cscope.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / cscope.c
diff --git a/mg/cscope.c b/mg/cscope.c
new file mode 100644 (file)
index 0000000..9a8db5e
--- /dev/null
@@ -0,0 +1,609 @@
+/*     $OpenBSD: cscope.c,v 1.3 2012/07/02 08:08:31 lum Exp $  */
+
+/*
+ * This file is in the public domain.
+ *
+ * Author: Sunil Nimmagadda <sunil@sunilnimmagadda.com>
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+
+#define CSSYMBOL      0
+#define CSDEFINITION  1
+#define CSCALLEDFUNCS 2
+#define CSCALLERFUNCS 3
+#define CSTEXT        4
+#define CSEGREP       6
+#define CSFINDFILE    7
+#define CSINCLUDES    8
+
+struct cstokens {
+       const char *fname;
+       const char *function;
+       const char *lineno;
+       const char *pattern;
+};
+
+struct csmatch {
+       TAILQ_ENTRY(csmatch) entry;
+       int lineno;
+};
+
+struct csrecord {
+       TAILQ_ENTRY(csrecord) entry;
+       char *filename;
+       TAILQ_HEAD(matches, csmatch) matches;
+};
+
+static TAILQ_HEAD(csrecords, csrecord) csrecords = TAILQ_HEAD_INITIALIZER(csrecords);
+static struct csrecord *addentryr;
+static struct csrecord *currecord;
+static struct csmatch  *curmatch;
+static const char      *addentryfn;
+static const char      *csprompt[] = {
+       "Find this symbol: ",
+       "Find this global definition: ",
+       "Find functions called by this function: ",
+       "Find functions calling this function: ",
+       "Find this text string: ",
+       "Change this text string: ",
+       "Find this egrep pattern: ",
+       "Find this file: ",
+       "Find files #including this file: "
+};
+
+static int  addentry(struct buffer *, char *);
+static void csflush(void);
+static int  do_cscope(int);
+static int  csexists(const char *);
+static int  getattr(char *, struct cstokens *);
+static int  jumptomatch(void);
+static void prettyprint(struct buffer *, struct cstokens *);
+static const char *ltrim(const char *);
+
+/*
+ * Find this symbol. Bound to C-c s s
+ */
+/* ARGSUSED */
+int
+cssymbol(int f, int n)
+{
+       return (do_cscope(CSSYMBOL));
+}
+
+/*
+ * Find this global definition. Bound to C-c s d
+ */
+/* ARGSUSED */int
+csdefinition(int f, int n)
+{
+       return (do_cscope(CSDEFINITION));
+}
+
+/*
+ * Find functions called by this function. Bound to C-c s l
+ */
+/* ARGSUSED */
+int
+csfuncalled(int f, int n)
+{
+       return (do_cscope(CSCALLEDFUNCS));
+}
+
+/*
+ * Find functions calling this function. Bound to C-c s c
+ */
+/* ARGSUSED */
+int
+cscallerfuncs(int f, int n)
+{
+       return (do_cscope(CSCALLERFUNCS));
+}
+
+/*
+ * Find this text. Bound to C-c s t
+ */
+/* ARGSUSED */
+int
+csfindtext(int f, int n)
+{
+       return (do_cscope(CSTEXT));
+}
+
+/*
+ * Find this egrep pattern. Bound to C-c s e
+ */
+/* ARGSUSED */
+int
+csegrep(int f, int n)
+{
+       return (do_cscope(CSEGREP));
+}
+
+/*
+ * Find this file. Bound to C-c s f
+ */
+/* ARGSUSED */
+int
+csfindfile(int f, int n)
+{
+       return (do_cscope(CSFINDFILE));
+}
+
+/*
+ * Find files #including this file. Bound to C-c s i
+ */
+/* ARGSUSED */
+int
+csfindinc(int f, int n)
+{
+       return (do_cscope(CSINCLUDES));
+}
+
+/*
+ * Create list of files to index in the given directory
+ * using cscope-indexer.
+ */
+/* ARGSUSED */
+int
+cscreatelist(int f, int n)
+{
+       struct buffer *bp;
+       struct stat sb;
+       FILE *fpipe;
+       char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp;
+       size_t len;
+       int clen;
+       
+       if (getbufcwd(dir, sizeof(dir)) == FALSE)
+               dir[0] = '\0';
+       
+       bufp = eread("Index files in directory: ", dir, 
+           sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
+       
+       if (bufp == NULL)
+               return (ABORT);
+       else if (bufp[0] == '\0')
+               return (FALSE);
+               
+       if (stat(dir, &sb) == -1) {
+               ewprintf("stat: %s", strerror(errno));
+               return (FALSE);
+       } else if (S_ISDIR(sb.st_mode) == 0) {
+               ewprintf("%s: Not a directory", dir);
+               return (FALSE);
+       }
+       
+       if (csexists("cscope-indexer") == FALSE) {
+               ewprintf("no such file or directory, cscope-indexer");
+               return (FALSE);
+       }
+       
+       clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
+       if (clen < 0 || clen >= sizeof(cmd))
+               return (FALSE);
+
+       if ((fpipe = popen(cmd, "r")) == NULL) {
+               ewprintf("problem opening pipe");
+               return (FALSE);
+       }
+       
+       bp = bfind("*cscope*", TRUE);
+       if (bclear(bp) != TRUE)
+               return (FALSE);
+       bp->b_flag |= BFREADONLY;
+
+       clen = snprintf(title, sizeof(title), "%s%s",
+           "Creating cscope file list 'cscope.files' in: ", dir);
+       if (clen < 0 || clen >= sizeof(title))
+               return (FALSE);
+       addline(bp, title);
+       addline(bp, "");
+       /* All lines are NUL terminated */
+       while ((line = fgetln(fpipe, &len)) != NULL) {
+               line[len - 1] = '\0';
+               addline(bp, line);
+       }
+       pclose(fpipe);
+       return (popbuftop(bp, WNONE));  
+}
+
+/*
+ * Next Symbol. Bound to C-c s n
+ */
+/* ARGSUSED */
+int
+csnextmatch(int f, int n)
+{
+       struct csrecord *r;
+       struct csmatch *m;
+       
+       if (curmatch == NULL) {
+               if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
+                       ewprintf("The *cscope* buffer does not exist yet");
+                       return (FALSE);
+               }
+               currecord = r;
+               curmatch = TAILQ_FIRST(&r->matches);
+       } else {
+               m = TAILQ_NEXT(curmatch, entry);
+               if (m == NULL) {
+                       r = TAILQ_NEXT(currecord, entry);
+                       if (r == NULL) {
+                               ewprintf("The end of *cscope* buffer has been"
+                                   " reached");
+                               return (FALSE);
+                       } else {
+                               currecord = r;
+                               curmatch = TAILQ_FIRST(&currecord->matches);
+                       }
+               } else
+                       curmatch = m;
+       }
+       return (jumptomatch());
+}
+
+/*
+ * Previous Symbol. Bound to C-c s p
+ */
+/* ARGSUSED */
+int
+csprevmatch(int f, int n)
+{
+       struct csmatch *m;
+       struct csrecord *r;
+
+       if (curmatch == NULL)
+               return (FALSE);
+       else {
+               m  = TAILQ_PREV(curmatch, matches, entry);
+               if (m)
+                       curmatch = m;
+               else {
+                       r = TAILQ_PREV(currecord, csrecords, entry);
+                       if (r == NULL) {
+                               ewprintf("The beginning of *cscope* buffer has"
+                                   " been reached");
+                               return (FALSE);
+                       } else {
+                               currecord = r;
+                               curmatch = TAILQ_LAST(&currecord->matches,
+                                   matches);
+                       }
+               }
+       }
+       return (jumptomatch());
+}
+
+/*
+ * Next file.
+ */
+int
+csnextfile(int f, int n)
+{
+       struct csrecord *r;
+       
+       if (curmatch == NULL) {
+               if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
+                       ewprintf("The *cscope* buffer does not exist yet");
+                       return (FALSE);
+               }
+
+       } else {
+               if ((r = TAILQ_NEXT(currecord, entry)) == NULL) {
+                       ewprintf("The end of *cscope* buffer has been reached");
+                       return (FALSE);
+               }
+       }
+       currecord = r;
+       curmatch = TAILQ_FIRST(&currecord->matches);
+       return (jumptomatch()); 
+}
+
+/*
+ * Previous file.
+ */
+int
+csprevfile(int f, int n)
+{
+       struct csrecord *r;
+       
+       if (curmatch == NULL) {
+               if ((r = TAILQ_FIRST(&csrecords)) == NULL) {
+                       ewprintf("The *cscope* buffer does not exist yet");
+                       return (FALSE);
+               }
+
+       } else {
+               if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) {
+                       ewprintf("The beginning of *cscope* buffer has been"
+                           " reached");
+                       return (FALSE);
+               }
+       }
+       currecord = r;
+       curmatch = TAILQ_FIRST(&currecord->matches);
+       return (jumptomatch()); 
+}
+
+/*
+ * The current symbol location is extracted from currecord->filename and 
+ * curmatch->lineno. Load the file similar to filevisit and goto the 
+ * lineno recorded.
+ */
+int
+jumptomatch(void)
+{
+       struct buffer *bp;
+       char *adjf;
+       
+       if (curmatch == NULL || currecord == NULL)
+               return (FALSE);
+       adjf = adjustname(currecord->filename, TRUE);
+       if (adjf == NULL)
+               return (FALSE);
+       if ((bp = findbuffer(adjf)) == NULL)
+               return (FALSE);
+       curbp = bp;
+       if (showbuffer(bp, curwp, WFFULL) != TRUE)
+               return (FALSE);
+       if (bp->b_fname[0] == '\0') {
+               if (readin(adjf) != TRUE)
+                       killbuffer(bp);
+       }
+       gotoline(FFARG, curmatch->lineno);
+       return (TRUE);
+       
+}
+
+/*
+ * Ask for the symbol, construct cscope commandline with the symbol
+ * and passed in index. Popen cscope, read the output into *cscope* 
+ * buffer and pop it.
+ */
+int
+do_cscope(int i)
+{
+       struct buffer *bp;
+       FILE *fpipe;
+       char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
+       char *p, *buf;
+       int clen, nores = 0;
+       size_t len;
+
+       /* If current buffer isn't a source file just return */
+       if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0) {
+               ewprintf("C-c s not defined");
+               return (FALSE);
+       }
+       
+       if (curtoken(0, 1, pattern) == FALSE)
+               return (FALSE); 
+       p = eread(csprompt[i], pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF);
+       if (p == NULL)
+               return (ABORT);
+       else if (p[0] == '\0')
+               return (FALSE);
+
+       if (csexists("cscope") == FALSE) {
+               ewprintf("no such file or directory, cscope");
+               return (FALSE);
+       }
+       
+       csflush();
+       clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
+           i, pattern);
+       if (clen < 0 || clen >= sizeof(cmd))
+               return (FALSE);
+
+       if ((fpipe = popen(cmd, "r")) == NULL) {
+               ewprintf("problem opening pipe");
+               return (FALSE);
+       }
+       
+       bp = bfind("*cscope*", TRUE);
+       if (bclear(bp) != TRUE)
+               return (FALSE);
+       bp->b_flag |= BFREADONLY;
+
+       clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
+       if (clen < 0 || clen >= sizeof(title))
+               return (FALSE);
+       addline(bp, title);
+       addline(bp, "");
+       addline(bp, "-------------------------------------------------------------------------------");
+       /* All lines are NUL terminated */
+       while ((buf = fgetln(fpipe, &len)) != NULL) {
+               buf[len - 1] = '\0';
+               if (addentry(bp, buf) != TRUE)
+                       return (FALSE);
+               nores = 1;
+       }; 
+       pclose(fpipe);
+       addline(bp, "-------------------------------------------------------------------------------");
+       if (nores == 0)
+               ewprintf("No matches were found.");
+       return (popbuftop(bp, WNONE));
+}
+
+/*
+ * For each line read from cscope output, extract the tokens,
+ * add them to list and pretty print a line in *cscope* buffer.
+ */
+int
+addentry(struct buffer *bp, char *csline)
+{
+       struct csrecord *r;
+       struct csmatch *m;
+       struct cstokens t;
+       int lineno;
+       char buf[BUFSIZ];
+       const char *errstr;
+
+       r = NULL;
+       if (getattr(csline, &t) == FALSE)
+               return (FALSE);
+
+       lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
+       if (errstr)
+               return (FALSE);
+               
+       if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
+               if ((r = malloc(sizeof(struct csrecord))) == NULL)
+                       return (FALSE);
+               addentryr = r;
+               if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
+                       goto cleanup;
+               addentryfn = r->filename;
+               TAILQ_INIT(&r->matches);
+               if ((m = malloc(sizeof(struct csmatch))) == NULL)
+                       goto cleanup;
+               m->lineno = lineno;
+               TAILQ_INSERT_TAIL(&r->matches, m, entry);
+               TAILQ_INSERT_TAIL(&csrecords, r, entry);
+               addline(bp, "");
+               if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
+                       goto cleanup;
+               addline(bp, buf);
+       } else {
+               if ((m = malloc(sizeof(struct csmatch))) == NULL)
+                       goto cleanup;
+               m->lineno = lineno;
+               TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
+       }
+       prettyprint(bp, &t);
+       return (TRUE);
+cleanup:
+       free(r);
+       return (FALSE);
+}
+
+/*
+ * Cscope line: <filename> <function> <lineno> <pattern>
+ */
+int
+getattr(char *line, struct cstokens *t)
+{
+       char *p;
+
+       if ((p = strchr(line, ' ')) == NULL)
+               return (FALSE);
+       *p++ = '\0';
+       t->fname = line;
+       line = p;
+
+       if ((p = strchr(line, ' ')) == NULL)
+               return (FALSE);
+       *p++ = '\0';
+       t->function = line;
+       line = p;
+
+       if ((p = strchr(line, ' ')) == NULL)
+               return (FALSE);
+       *p++ = '\0';
+       t->lineno = line;
+
+       if (*p == '\0')
+               return (FALSE);
+       t->pattern = p;
+
+       return (TRUE);
+}
+
+void
+prettyprint(struct buffer *bp, struct cstokens *t)
+{
+       char buf[BUFSIZ];
+
+       if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
+           t->function, t->lineno, ltrim(t->pattern)) < 0)
+               return;
+       addline(bp, buf);
+}
+
+const char *
+ltrim(const char *s)
+{
+       while (isblank(*s))
+               s++;
+       return s;
+}
+
+void
+csflush(void)
+{
+       struct csrecord *r;
+       struct csmatch *m;
+       
+       while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
+               free(r->filename);
+               while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
+                       TAILQ_REMOVE(&r->matches, m, entry);
+                       free(m);
+               }
+               TAILQ_REMOVE(&csrecords, r, entry);
+               free(r);
+       }
+       addentryr = NULL;
+       addentryfn = NULL;
+       currecord = NULL;
+       curmatch = NULL;
+}
+
+/*
+ * Check if the cmd exists in $PATH. Split on ":" and iterate through
+ * all paths in $PATH.
+ */
+int
+csexists(const char *cmd)
+{
+       char fname[NFILEN], *dir, *path, *pathc, *tmp;
+       int  cmdlen, dlen;
+
+       /* Special case if prog contains '/' */
+       if (strchr(cmd, '/')) {
+               if (access(cmd, F_OK) == -1)
+                       return (FALSE);
+               else
+                       return (TRUE);
+       }
+       if ((tmp = getenv("PATH")) == NULL)
+               return (FALSE);
+       if ((pathc = path = strndup(tmp, NFILEN)) == NULL) {
+               ewprintf("out of memory");
+               return (FALSE);
+       }
+       cmdlen = strlen(cmd);
+       while ((dir = strsep(&path, ":")) != NULL) {
+               if (*dir == '\0')
+                       *dir = '.';
+
+               dlen = strlen(dir);
+               while (dir[dlen-1] == '/')
+                       dir[--dlen] = '\0';     /* strip trailing '/' */
+
+               if (dlen + 1 + cmdlen >= sizeof(fname))  {
+                       ewprintf("path too long");
+                       goto cleanup;
+               }
+               snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
+               if(access(fname, F_OK) == 0) {
+                      free(pathc);
+                      return (TRUE);
+              }
+       }
+cleanup:
+       free(pathc);
+       return (FALSE);
+}