File: [local] / src / usr.bin / mg / cscope.c (download)
Revision 1.22, Wed Mar 8 04:43:11 2023 UTC (15 months ago) by guenther
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, HEAD Changes since 1.21: +2 -12 lines
Delete obsolete /* ARGSUSED */ lint comments.
ok miod@ millert@
|
/* $OpenBSD: cscope.c,v 1.22 2023/03/08 04:43:11 guenther Exp $ */
/*
* This file is in the public domain.
*
* Author: Sunil Nimmagadda <sunil@openbsd.org>
*/
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.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
*/
int
cssymbol(int f, int n)
{
return (do_cscope(CSSYMBOL));
}
/*
* Find this global definition. Bound to C-c s d
*/
int
csdefinition(int f, int n)
{
return (do_cscope(CSDEFINITION));
}
/*
* Find functions called by this function. Bound to C-c s l
*/
int
csfuncalled(int f, int n)
{
return (do_cscope(CSCALLEDFUNCS));
}
/*
* Find functions calling this function. Bound to C-c s c
*/
int
cscallerfuncs(int f, int n)
{
return (do_cscope(CSCALLERFUNCS));
}
/*
* Find this text. Bound to C-c s t
*/
int
csfindtext(int f, int n)
{
return (do_cscope(CSTEXT));
}
/*
* Find this egrep pattern. Bound to C-c s e
*/
int
csegrep(int f, int n)
{
return (do_cscope(CSEGREP));
}
/*
* Find this file. Bound to C-c s f
*/
int
csfindfile(int f, int n)
{
return (do_cscope(CSFINDFILE));
}
/*
* Find files #including this file. Bound to C-c s i
*/
int
csfindinc(int f, int n)
{
return (do_cscope(CSINCLUDES));
}
/*
* Create list of files to index in the given directory
* using cscope-indexer.
*/
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 sz;
ssize_t len;
int clen;
line = NULL;
sz = 0;
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)
return(dobeep_msgs("stat:", strerror(errno)));
else if (S_ISDIR(sb.st_mode) == 0)
return(dobeep_msgs(dir, "Not a directory"));
if (csexists("cscope-indexer") == FALSE)
return(dobeep_msg("no such file or directory, cscope-indexer"));
clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
if (clen < 0 || clen >= sizeof(cmd))
return (FALSE);
if ((fpipe = popen(cmd, "r")) == NULL)
return(dobeep_msg("problem opening pipe"));
bp = bfind("*cscope*", TRUE);
if (bclear(bp) != TRUE) {
pclose(fpipe);
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)) {
pclose(fpipe);
return (FALSE);
}
addline(bp, title);
addline(bp, "");
while ((len = getline(&line, &sz, fpipe)) != -1) {
if (line[len - 1] == *bp->b_nlchr)
line[len - 1] = '\0';
addline(bp, line);
}
free(line);
if (ferror(fpipe))
ewprintf("Problem reading pipe");
pclose(fpipe);
return (popbuftop(bp, WNONE));
}
/*
* Next Symbol. Bound to C-c s n
*/
int
csnextmatch(int f, int n)
{
struct csrecord *r;
struct csmatch *m;
if (curmatch == NULL) {
if ((r = TAILQ_FIRST(&csrecords)) == NULL)
return(dobeep_msg("The *cscope* buffer does "
"not exist yet"));
currecord = r;
curmatch = TAILQ_FIRST(&r->matches);
} else {
m = TAILQ_NEXT(curmatch, entry);
if (m == NULL) {
r = TAILQ_NEXT(currecord, entry);
if (r == NULL) {
return(dobeep_msg("The end of *cscope* buffer "
"has been reached"));
} else {
currecord = r;
curmatch = TAILQ_FIRST(&currecord->matches);
}
} else
curmatch = m;
}
return (jumptomatch());
}
/*
* Previous Symbol. Bound to C-c s p
*/
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) {
return(dobeep_msg("The beginning of *cscope* "
"buffer has been reached"));
} 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)
return(dobeep_msg("The *cscope* buffer does not "
"exist yet"));
} else {
if ((r = TAILQ_NEXT(currecord, entry)) == NULL)
return(dobeep_msg("The end of *cscope* buffer has "
"been reached"));
}
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)
return(dobeep_msg("The *cscope* buffer does not"
"exist yet"));
} else {
if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL)
return(dobeep_msg("The beginning of *cscope* buffer "
"has been reached"));
}
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 sz;
ssize_t len;
buf = NULL;
sz = 0;
/* If current buffer isn't a source file just return */
if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0)
return(dobeep_msg("C-c s not defined"));
if (curtoken(0, 1, pattern) == FALSE)
return (FALSE);
p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
if (p == NULL)
return (ABORT);
else if (p[0] == '\0')
return (FALSE);
if (csexists("cscope") == FALSE)
return(dobeep_msg("no such file or directory, cscope"));
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)
return(dobeep_msg("problem opening pipe"));
bp = bfind("*cscope*", TRUE);
if (bclear(bp) != TRUE) {
pclose(fpipe);
return (FALSE);
}
bp->b_flag |= BFREADONLY;
clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
if (clen < 0 || clen >= sizeof(title)) {
pclose(fpipe);
return (FALSE);
}
addline(bp, title);
addline(bp, "");
addline(bp, "-------------------------------------------------------------------------------");
while ((len = getline(&buf, &sz, fpipe)) != -1) {
if (buf[len - 1] == *bp->b_nlchr)
buf[len - 1] = '\0';
if (addentry(bp, buf) != TRUE) {
free(buf);
return (FALSE);
}
nores = 1;
}
free(buf);
if (ferror(fpipe))
ewprintf("Problem reading pipe");
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((unsigned char)*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 len, 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)
return(dobeep_msg("out of memory"));
while ((dir = strsep(&path, ":")) != NULL) {
if (*dir == '\0')
continue;
dlen = strlen(dir);
while (dlen > 0 && dir[dlen-1] == '/')
dir[--dlen] = '\0'; /* strip trailing '/' */
len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
if (len < 0 || len >= sizeof(fname)) {
(void)dobeep_msg("path too long");
goto cleanup;
}
if(access(fname, F_OK) == 0) {
free(pathc);
return (TRUE);
}
}
cleanup:
free(pathc);
return (FALSE);
}