1*0a6a1f1dSLionel Sambuc /* $NetBSD: ex_cscope.c,v 1.7 2014/08/26 15:19:38 aymeric Exp $ */
284d9c625SLionel Sambuc /*-
384d9c625SLionel Sambuc * Copyright (c) 1994, 1996
484d9c625SLionel Sambuc * Rob Mayoff. All rights reserved.
584d9c625SLionel Sambuc * Copyright (c) 1996
684d9c625SLionel Sambuc * Keith Bostic. All rights reserved.
784d9c625SLionel Sambuc *
884d9c625SLionel Sambuc * See the LICENSE file for redistribution information.
984d9c625SLionel Sambuc */
1084d9c625SLionel Sambuc
1184d9c625SLionel Sambuc #include "config.h"
1284d9c625SLionel Sambuc
13*0a6a1f1dSLionel Sambuc #include <sys/cdefs.h>
14*0a6a1f1dSLionel Sambuc #if 0
1584d9c625SLionel Sambuc #ifndef lint
1684d9c625SLionel Sambuc static const char sccsid[] = "Id: ex_cscope.c,v 10.21 2003/11/05 17:11:54 skimo Exp (Berkeley) Date: 2003/11/05 17:11:54 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_cscope.c,v 1.7 2014/08/26 15:19:38 aymeric Exp $");
20*0a6a1f1dSLionel Sambuc #endif
2184d9c625SLionel Sambuc
2284d9c625SLionel Sambuc #include <sys/param.h>
2384d9c625SLionel Sambuc #include <sys/types.h> /* XXX: param.h may not have included types.h */
2484d9c625SLionel Sambuc #include <sys/queue.h>
2584d9c625SLionel Sambuc #include <sys/stat.h>
2684d9c625SLionel Sambuc #include <sys/time.h>
2784d9c625SLionel Sambuc #include <sys/wait.h>
2884d9c625SLionel Sambuc
2984d9c625SLionel Sambuc #include <bitstring.h>
3084d9c625SLionel Sambuc #include <ctype.h>
3184d9c625SLionel Sambuc #include <errno.h>
3284d9c625SLionel Sambuc #include <fcntl.h>
3384d9c625SLionel Sambuc #include <limits.h>
3484d9c625SLionel Sambuc #include <stddef.h>
3584d9c625SLionel Sambuc #include <stdio.h>
3684d9c625SLionel Sambuc #include <stdlib.h>
3784d9c625SLionel Sambuc #include <string.h>
3884d9c625SLionel Sambuc #include <termios.h>
3984d9c625SLionel Sambuc #include <unistd.h>
4084d9c625SLionel Sambuc
4184d9c625SLionel Sambuc #include "../common/common.h"
4284d9c625SLionel Sambuc #include "pathnames.h"
4384d9c625SLionel Sambuc #include "tag.h"
4484d9c625SLionel Sambuc
4584d9c625SLionel Sambuc #define CSCOPE_DBFILE "cscope.out"
4684d9c625SLionel Sambuc #define CSCOPE_PATHS "cscope.tpath"
4784d9c625SLionel Sambuc
4884d9c625SLionel Sambuc /*
4984d9c625SLionel Sambuc * 0name find all uses of name
5084d9c625SLionel Sambuc * 1name find definition of name
5184d9c625SLionel Sambuc * 2name find all function calls made from name
5284d9c625SLionel Sambuc * 3name find callers of name
5384d9c625SLionel Sambuc * 4string find text string (cscope 12.9)
5484d9c625SLionel Sambuc * 4name find assignments to name (cscope 13.3)
5584d9c625SLionel Sambuc * 5pattern change pattern -- NOT USED
5684d9c625SLionel Sambuc * 6pattern find pattern
5784d9c625SLionel Sambuc * 7name find files with name as substring
5884d9c625SLionel Sambuc * 8name find files #including name
5984d9c625SLionel Sambuc */
6084d9c625SLionel Sambuc #define FINDHELP "\
6184d9c625SLionel Sambuc find c|d|e|f|g|i|s|t buffer|pattern\n\
6284d9c625SLionel Sambuc c: find callers of name\n\
6384d9c625SLionel Sambuc d: find all function calls made from name\n\
6484d9c625SLionel Sambuc e: find pattern\n\
6584d9c625SLionel Sambuc f: find files with name as substring\n\
6684d9c625SLionel Sambuc g: find definition of name\n\
6784d9c625SLionel Sambuc i: find files #including name\n\
6884d9c625SLionel Sambuc s: find all uses of name\n\
6984d9c625SLionel Sambuc t: find assignments to name"
7084d9c625SLionel Sambuc
7184d9c625SLionel Sambuc static int cscope_add __P((SCR *, EXCMD *, const CHAR_T *));
7284d9c625SLionel Sambuc static int cscope_find __P((SCR *, EXCMD*, const CHAR_T *));
7384d9c625SLionel Sambuc static int cscope_help __P((SCR *, EXCMD *, const CHAR_T *));
7484d9c625SLionel Sambuc static int cscope_kill __P((SCR *, EXCMD *, const CHAR_T *));
7584d9c625SLionel Sambuc static int cscope_reset __P((SCR *, EXCMD *, const CHAR_T *));
7684d9c625SLionel Sambuc
7784d9c625SLionel Sambuc typedef struct _cc {
7884d9c625SLionel Sambuc const char *name;
7984d9c625SLionel Sambuc int (*function) __P((SCR *, EXCMD *, const CHAR_T *));
8084d9c625SLionel Sambuc const char *help_msg;
8184d9c625SLionel Sambuc const char *usage_msg;
8284d9c625SLionel Sambuc } CC;
8384d9c625SLionel Sambuc
8484d9c625SLionel Sambuc static CC const cscope_cmds[] = {
8584d9c625SLionel Sambuc { "add", cscope_add,
8684d9c625SLionel Sambuc "Add a new cscope database", "add file | directory" },
8784d9c625SLionel Sambuc { "find", cscope_find,
8884d9c625SLionel Sambuc "Query the databases for a pattern", FINDHELP },
8984d9c625SLionel Sambuc { "help", cscope_help,
9084d9c625SLionel Sambuc "Show help for cscope commands", "help [command]" },
9184d9c625SLionel Sambuc { "kill", cscope_kill,
9284d9c625SLionel Sambuc "Kill a cscope connection", "kill number" },
9384d9c625SLionel Sambuc { "reset", cscope_reset,
9484d9c625SLionel Sambuc "Discard all current cscope connections", "reset" },
9584d9c625SLionel Sambuc { NULL, NULL, NULL, NULL }
9684d9c625SLionel Sambuc };
9784d9c625SLionel Sambuc
9884d9c625SLionel Sambuc static TAGQ *create_cs_cmd __P((SCR *, const char *, size_t *));
9984d9c625SLionel Sambuc static int csc_help __P((SCR *, const char *));
10084d9c625SLionel Sambuc static void csc_file __P((SCR *,
10184d9c625SLionel Sambuc CSC *, char *, char **, size_t *, int *));
10284d9c625SLionel Sambuc static int get_paths __P((SCR *, CSC *));
10384d9c625SLionel Sambuc static CC const *lookup_ccmd __P((const char *));
10484d9c625SLionel Sambuc static int parse __P((SCR *, CSC *, TAGQ *, int *));
10584d9c625SLionel Sambuc static int read_prompt __P((SCR *, CSC *));
10684d9c625SLionel Sambuc static int run_cscope __P((SCR *, CSC *, const char *));
10784d9c625SLionel Sambuc static int start_cscopes __P((SCR *, EXCMD *));
10884d9c625SLionel Sambuc static int terminate __P((SCR *, CSC *, int));
10984d9c625SLionel Sambuc
11084d9c625SLionel Sambuc /*
11184d9c625SLionel Sambuc * ex_cscope --
11284d9c625SLionel Sambuc * Perform an ex cscope.
11384d9c625SLionel Sambuc *
11484d9c625SLionel Sambuc * PUBLIC: int ex_cscope __P((SCR *, EXCMD *));
11584d9c625SLionel Sambuc */
11684d9c625SLionel Sambuc int
ex_cscope(SCR * sp,EXCMD * cmdp)11784d9c625SLionel Sambuc ex_cscope(SCR *sp, EXCMD *cmdp)
11884d9c625SLionel Sambuc {
11984d9c625SLionel Sambuc CC const *ccp;
12084d9c625SLionel Sambuc EX_PRIVATE *exp;
12184d9c625SLionel Sambuc int i;
12284d9c625SLionel Sambuc CHAR_T *cmd;
12384d9c625SLionel Sambuc CHAR_T *p;
12484d9c625SLionel Sambuc const char *np;
12584d9c625SLionel Sambuc size_t nlen;
12684d9c625SLionel Sambuc
12784d9c625SLionel Sambuc /* Initialize the default cscope directories. */
12884d9c625SLionel Sambuc exp = EXP(sp);
12984d9c625SLionel Sambuc if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp))
13084d9c625SLionel Sambuc return (1);
13184d9c625SLionel Sambuc F_SET(exp, EXP_CSCINIT);
13284d9c625SLionel Sambuc
13384d9c625SLionel Sambuc /* Skip leading whitespace. */
13484d9c625SLionel Sambuc for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p)
13584d9c625SLionel Sambuc if (!ISBLANK((UCHAR_T)*p))
13684d9c625SLionel Sambuc break;
13784d9c625SLionel Sambuc if (i == 0)
13884d9c625SLionel Sambuc goto usage;
13984d9c625SLionel Sambuc
14084d9c625SLionel Sambuc /* Skip the command to any arguments. */
14184d9c625SLionel Sambuc for (cmd = p; i > 0; --i, ++p)
14284d9c625SLionel Sambuc if (ISBLANK((UCHAR_T)*p))
14384d9c625SLionel Sambuc break;
14484d9c625SLionel Sambuc if (*p != '\0') {
14584d9c625SLionel Sambuc *p++ = '\0';
14684d9c625SLionel Sambuc for (; *p && ISBLANK((UCHAR_T)*p); ++p);
14784d9c625SLionel Sambuc }
14884d9c625SLionel Sambuc
14984d9c625SLionel Sambuc INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen);
15084d9c625SLionel Sambuc if ((ccp = lookup_ccmd(np)) == NULL) {
15184d9c625SLionel Sambuc usage: msgq(sp, M_ERR, "309|Use \"cscope help\" for help");
15284d9c625SLionel Sambuc return (1);
15384d9c625SLionel Sambuc }
15484d9c625SLionel Sambuc
15584d9c625SLionel Sambuc /* Call the underlying function. */
15684d9c625SLionel Sambuc return (ccp->function(sp, cmdp, p));
15784d9c625SLionel Sambuc }
15884d9c625SLionel Sambuc
15984d9c625SLionel Sambuc /*
16084d9c625SLionel Sambuc * start_cscopes --
16184d9c625SLionel Sambuc * Initialize the cscope package.
16284d9c625SLionel Sambuc */
16384d9c625SLionel Sambuc static int
start_cscopes(SCR * sp,EXCMD * cmdp)16484d9c625SLionel Sambuc start_cscopes(SCR *sp, EXCMD *cmdp)
16584d9c625SLionel Sambuc {
16684d9c625SLionel Sambuc size_t blen, len;
16784d9c625SLionel Sambuc char *bp, *cscopes, *p, *t;
16884d9c625SLionel Sambuc const CHAR_T *wp;
16984d9c625SLionel Sambuc size_t wlen;
17084d9c625SLionel Sambuc
17184d9c625SLionel Sambuc /*
17284d9c625SLionel Sambuc * EXTENSION #1:
17384d9c625SLionel Sambuc *
17484d9c625SLionel Sambuc * If the CSCOPE_DIRS environment variable is set, we treat it as a
17584d9c625SLionel Sambuc * list of cscope directories that we're using, similar to the tags
17684d9c625SLionel Sambuc * edit option.
17784d9c625SLionel Sambuc *
17884d9c625SLionel Sambuc * XXX
17984d9c625SLionel Sambuc * This should probably be an edit option, although that implies that
18084d9c625SLionel Sambuc * we start/stop cscope processes periodically, instead of once when
18184d9c625SLionel Sambuc * the editor starts.
18284d9c625SLionel Sambuc */
18384d9c625SLionel Sambuc if ((cscopes = getenv("CSCOPE_DIRS")) == NULL)
18484d9c625SLionel Sambuc return (0);
18584d9c625SLionel Sambuc len = strlen(cscopes);
18684d9c625SLionel Sambuc GET_SPACE_RETC(sp, bp, blen, len);
18784d9c625SLionel Sambuc memcpy(bp, cscopes, len + 1);
18884d9c625SLionel Sambuc
18984d9c625SLionel Sambuc for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;)
19084d9c625SLionel Sambuc if (*p != '\0') {
19184d9c625SLionel Sambuc CHAR2INT(sp, p, strlen(p) + 1, wp, wlen);
19284d9c625SLionel Sambuc (void)cscope_add(sp, cmdp, wp);
19384d9c625SLionel Sambuc }
19484d9c625SLionel Sambuc
19584d9c625SLionel Sambuc FREE_SPACE(sp, bp, blen);
19684d9c625SLionel Sambuc return (0);
19784d9c625SLionel Sambuc }
19884d9c625SLionel Sambuc
19984d9c625SLionel Sambuc /*
20084d9c625SLionel Sambuc * cscope_add --
20184d9c625SLionel Sambuc * The cscope add command.
20284d9c625SLionel Sambuc */
20384d9c625SLionel Sambuc static int
cscope_add(SCR * sp,EXCMD * cmdp,const CHAR_T * dname)20484d9c625SLionel Sambuc cscope_add(SCR *sp, EXCMD *cmdp, const CHAR_T *dname)
20584d9c625SLionel Sambuc {
20684d9c625SLionel Sambuc struct stat sb;
20784d9c625SLionel Sambuc EX_PRIVATE *exp;
20884d9c625SLionel Sambuc CSC *csc;
20984d9c625SLionel Sambuc size_t len;
21084d9c625SLionel Sambuc int cur_argc;
21184d9c625SLionel Sambuc const char *dbname;
21284d9c625SLionel Sambuc char path[MAXPATHLEN];
21384d9c625SLionel Sambuc const char *np;
21484d9c625SLionel Sambuc char *npp;
21584d9c625SLionel Sambuc size_t nlen;
21684d9c625SLionel Sambuc
21784d9c625SLionel Sambuc exp = EXP(sp);
21884d9c625SLionel Sambuc
21984d9c625SLionel Sambuc /*
22084d9c625SLionel Sambuc * 0 additional args: usage.
22184d9c625SLionel Sambuc * 1 additional args: matched a file.
22284d9c625SLionel Sambuc * >1 additional args: object, too many args.
22384d9c625SLionel Sambuc */
22484d9c625SLionel Sambuc cur_argc = cmdp->argc;
22584d9c625SLionel Sambuc if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) {
22684d9c625SLionel Sambuc return (1);
22784d9c625SLionel Sambuc }
22884d9c625SLionel Sambuc if (cmdp->argc == cur_argc) {
22984d9c625SLionel Sambuc (void)csc_help(sp, "add");
23084d9c625SLionel Sambuc return (1);
23184d9c625SLionel Sambuc }
23284d9c625SLionel Sambuc if (cmdp->argc == cur_argc + 1)
23384d9c625SLionel Sambuc dname = cmdp->argv[cur_argc]->bp;
23484d9c625SLionel Sambuc else {
23584d9c625SLionel Sambuc ex_emsg(sp, np, EXM_FILECOUNT);
23684d9c625SLionel Sambuc return (1);
23784d9c625SLionel Sambuc }
23884d9c625SLionel Sambuc
23984d9c625SLionel Sambuc INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen);
24084d9c625SLionel Sambuc
24184d9c625SLionel Sambuc /*
24284d9c625SLionel Sambuc * The user can specify a specific file (so they can have multiple
24384d9c625SLionel Sambuc * Cscope databases in a single directory) or a directory. If the
24484d9c625SLionel Sambuc * file doesn't exist, we're done. If it's a directory, append the
24584d9c625SLionel Sambuc * standard database file name and try again. Store the directory
24684d9c625SLionel Sambuc * name regardless so that we can use it as a base for searches.
24784d9c625SLionel Sambuc */
24884d9c625SLionel Sambuc if (stat(np, &sb)) {
24984d9c625SLionel Sambuc msgq(sp, M_SYSERR, "%s", np);
25084d9c625SLionel Sambuc return (1);
25184d9c625SLionel Sambuc }
25284d9c625SLionel Sambuc if (S_ISDIR(sb.st_mode)) {
25384d9c625SLionel Sambuc (void)snprintf(path, sizeof(path),
25484d9c625SLionel Sambuc "%s/%s", np, CSCOPE_DBFILE);
25584d9c625SLionel Sambuc if (stat(path, &sb)) {
25684d9c625SLionel Sambuc msgq(sp, M_SYSERR, "%s", path);
25784d9c625SLionel Sambuc return (1);
25884d9c625SLionel Sambuc }
25984d9c625SLionel Sambuc dbname = CSCOPE_DBFILE;
26084d9c625SLionel Sambuc } else if ((npp = strrchr(np, '/')) != NULL) {
26184d9c625SLionel Sambuc *npp = '\0';
26284d9c625SLionel Sambuc dbname = npp + 1;
26384d9c625SLionel Sambuc } else {
26484d9c625SLionel Sambuc dbname = np;
26584d9c625SLionel Sambuc np = ".";
26684d9c625SLionel Sambuc }
26784d9c625SLionel Sambuc
26884d9c625SLionel Sambuc /* Allocate a cscope connection structure and initialize its fields. */
26984d9c625SLionel Sambuc len = strlen(np);
27084d9c625SLionel Sambuc CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len);
27184d9c625SLionel Sambuc csc->dname = csc->buf;
27284d9c625SLionel Sambuc csc->dlen = len;
27384d9c625SLionel Sambuc memcpy(csc->dname, np, len);
27484d9c625SLionel Sambuc csc->mtime = sb.st_mtime;
27584d9c625SLionel Sambuc
27684d9c625SLionel Sambuc /* Get the search paths for the cscope. */
27784d9c625SLionel Sambuc if (get_paths(sp, csc))
27884d9c625SLionel Sambuc goto err;
27984d9c625SLionel Sambuc
28084d9c625SLionel Sambuc /* Start the cscope process. */
28184d9c625SLionel Sambuc if (run_cscope(sp, csc, dbname))
28284d9c625SLionel Sambuc goto err;
28384d9c625SLionel Sambuc
28484d9c625SLionel Sambuc /*
28584d9c625SLionel Sambuc * Add the cscope connection to the screen's list. From now on,
28684d9c625SLionel Sambuc * on error, we have to call terminate, which expects the csc to
28784d9c625SLionel Sambuc * be on the chain.
28884d9c625SLionel Sambuc */
28984d9c625SLionel Sambuc LIST_INSERT_HEAD(&exp->cscq, csc, q);
29084d9c625SLionel Sambuc
29184d9c625SLionel Sambuc /* Read the initial prompt from the cscope to make sure it's okay. */
29284d9c625SLionel Sambuc return read_prompt(sp, csc);
29384d9c625SLionel Sambuc
29484d9c625SLionel Sambuc err: free(csc);
29584d9c625SLionel Sambuc return (1);
29684d9c625SLionel Sambuc }
29784d9c625SLionel Sambuc
29884d9c625SLionel Sambuc /*
29984d9c625SLionel Sambuc * get_paths --
30084d9c625SLionel Sambuc * Get the directories to search for the files associated with this
30184d9c625SLionel Sambuc * cscope database.
30284d9c625SLionel Sambuc */
30384d9c625SLionel Sambuc static int
get_paths(SCR * sp,CSC * csc)30484d9c625SLionel Sambuc get_paths(SCR *sp, CSC *csc)
30584d9c625SLionel Sambuc {
30684d9c625SLionel Sambuc struct stat sb;
30784d9c625SLionel Sambuc int fd, nentries;
30884d9c625SLionel Sambuc size_t len;
30984d9c625SLionel Sambuc char *p, **pathp, buf[MAXPATHLEN * 2];
31084d9c625SLionel Sambuc
31184d9c625SLionel Sambuc /*
31284d9c625SLionel Sambuc * EXTENSION #2:
31384d9c625SLionel Sambuc *
31484d9c625SLionel Sambuc * If there's a cscope directory with a file named CSCOPE_PATHS, it
31584d9c625SLionel Sambuc * contains a colon-separated list of paths in which to search for
31684d9c625SLionel Sambuc * files returned by cscope.
31784d9c625SLionel Sambuc *
31884d9c625SLionel Sambuc * XXX
31984d9c625SLionel Sambuc * These paths are absolute paths, and not relative to the cscope
32084d9c625SLionel Sambuc * directory. To fix this, rewrite the each path using the cscope
32184d9c625SLionel Sambuc * directory as a prefix.
32284d9c625SLionel Sambuc */
32384d9c625SLionel Sambuc (void)snprintf(buf, sizeof(buf), "%s/%s", csc->dname, CSCOPE_PATHS);
32484d9c625SLionel Sambuc if (stat(buf, &sb) == 0) {
32584d9c625SLionel Sambuc /* Read in the CSCOPE_PATHS file. */
32684d9c625SLionel Sambuc len = sb.st_size;
32784d9c625SLionel Sambuc MALLOC_RET(sp, csc->pbuf, char *, len + 1);
32884d9c625SLionel Sambuc if ((fd = open(buf, O_RDONLY, 0)) < 0 ||
32984d9c625SLionel Sambuc (size_t)read(fd, csc->pbuf, len) != len) {
33084d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, buf, "%s");
33184d9c625SLionel Sambuc if (fd >= 0)
33284d9c625SLionel Sambuc (void)close(fd);
33384d9c625SLionel Sambuc return (1);
33484d9c625SLionel Sambuc }
33584d9c625SLionel Sambuc (void)close(fd);
33684d9c625SLionel Sambuc csc->pbuf[len] = '\0';
33784d9c625SLionel Sambuc
33884d9c625SLionel Sambuc /* Count up the entries. */
33984d9c625SLionel Sambuc for (nentries = 0, p = csc->pbuf; *p != '\0'; ++p)
34084d9c625SLionel Sambuc if (p[0] == ':' && p[1] != '\0')
34184d9c625SLionel Sambuc ++nentries;
34284d9c625SLionel Sambuc
34384d9c625SLionel Sambuc /* Build an array of pointers to the paths. */
34484d9c625SLionel Sambuc CALLOC_GOTO(sp,
34584d9c625SLionel Sambuc csc->paths, char **, nentries + 1, sizeof(char **));
34684d9c625SLionel Sambuc for (pathp = csc->paths, p = strtok(csc->pbuf, ":");
34784d9c625SLionel Sambuc p != NULL; p = strtok(NULL, ":"))
34884d9c625SLionel Sambuc *pathp++ = p;
34984d9c625SLionel Sambuc return (0);
35084d9c625SLionel Sambuc }
35184d9c625SLionel Sambuc
35284d9c625SLionel Sambuc /*
35384d9c625SLionel Sambuc * If the CSCOPE_PATHS file doesn't exist, we look for files
35484d9c625SLionel Sambuc * relative to the cscope directory.
35584d9c625SLionel Sambuc */
35684d9c625SLionel Sambuc if ((csc->pbuf = strdup(csc->dname)) == NULL) {
35784d9c625SLionel Sambuc msgq(sp, M_SYSERR, NULL);
35884d9c625SLionel Sambuc return (1);
35984d9c625SLionel Sambuc }
36084d9c625SLionel Sambuc CALLOC_GOTO(sp, csc->paths, char **, 2, sizeof(char *));
36184d9c625SLionel Sambuc csc->paths[0] = csc->pbuf;
36284d9c625SLionel Sambuc return (0);
36384d9c625SLionel Sambuc
36484d9c625SLionel Sambuc alloc_err:
36584d9c625SLionel Sambuc if (csc->pbuf != NULL) {
36684d9c625SLionel Sambuc free(csc->pbuf);
36784d9c625SLionel Sambuc csc->pbuf = NULL;
36884d9c625SLionel Sambuc }
36984d9c625SLionel Sambuc return (1);
37084d9c625SLionel Sambuc }
37184d9c625SLionel Sambuc
37284d9c625SLionel Sambuc /*
37384d9c625SLionel Sambuc * run_cscope --
37484d9c625SLionel Sambuc * Fork off the cscope process.
37584d9c625SLionel Sambuc */
37684d9c625SLionel Sambuc static int
run_cscope(SCR * sp,CSC * csc,const char * dbname)37784d9c625SLionel Sambuc run_cscope(SCR *sp, CSC *csc, const char *dbname)
37884d9c625SLionel Sambuc {
37984d9c625SLionel Sambuc int to_cs[2], from_cs[2];
38084d9c625SLionel Sambuc char cmd[MAXPATHLEN * 2];
38184d9c625SLionel Sambuc
38284d9c625SLionel Sambuc /*
38384d9c625SLionel Sambuc * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
38484d9c625SLionel Sambuc * from_cs[0] and writes to to_cs[1].
38584d9c625SLionel Sambuc */
38684d9c625SLionel Sambuc to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1;
38784d9c625SLionel Sambuc if (pipe(to_cs) < 0 || pipe(from_cs) < 0) {
38884d9c625SLionel Sambuc msgq(sp, M_SYSERR, "pipe");
38984d9c625SLionel Sambuc goto err;
39084d9c625SLionel Sambuc }
39184d9c625SLionel Sambuc switch (csc->pid = vfork()) {
39284d9c625SLionel Sambuc case -1:
39384d9c625SLionel Sambuc msgq(sp, M_SYSERR, "vfork");
39484d9c625SLionel Sambuc err: if (to_cs[0] != -1)
39584d9c625SLionel Sambuc (void)close(to_cs[0]);
39684d9c625SLionel Sambuc if (to_cs[1] != -1)
39784d9c625SLionel Sambuc (void)close(to_cs[1]);
39884d9c625SLionel Sambuc if (from_cs[0] != -1)
39984d9c625SLionel Sambuc (void)close(from_cs[0]);
40084d9c625SLionel Sambuc if (from_cs[1] != -1)
40184d9c625SLionel Sambuc (void)close(from_cs[1]);
40284d9c625SLionel Sambuc return (1);
40384d9c625SLionel Sambuc case 0: /* child: run cscope. */
40484d9c625SLionel Sambuc (void)dup2(to_cs[0], STDIN_FILENO);
40584d9c625SLionel Sambuc (void)dup2(from_cs[1], STDOUT_FILENO);
40684d9c625SLionel Sambuc (void)dup2(from_cs[1], STDERR_FILENO);
40784d9c625SLionel Sambuc
40884d9c625SLionel Sambuc /* Close unused file descriptors. */
40984d9c625SLionel Sambuc (void)close(to_cs[1]);
41084d9c625SLionel Sambuc (void)close(from_cs[0]);
41184d9c625SLionel Sambuc
41284d9c625SLionel Sambuc /* Run the cscope command. */
41384d9c625SLionel Sambuc #define CSCOPE_CMD_FMT "cd '%s' && exec cscope -dl -f %s"
41484d9c625SLionel Sambuc (void)snprintf(cmd, sizeof(cmd),
41584d9c625SLionel Sambuc CSCOPE_CMD_FMT, csc->dname, dbname);
41684d9c625SLionel Sambuc (void)execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL);
41784d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, cmd, "execl: %s");
41884d9c625SLionel Sambuc _exit (127);
41984d9c625SLionel Sambuc /* NOTREACHED */
42084d9c625SLionel Sambuc default: /* parent. */
42184d9c625SLionel Sambuc /* Close unused file descriptors. */
42284d9c625SLionel Sambuc (void)close(to_cs[0]);
42384d9c625SLionel Sambuc (void)close(from_cs[1]);
42484d9c625SLionel Sambuc
42584d9c625SLionel Sambuc /*
42684d9c625SLionel Sambuc * Save the file descriptors for later duplication, and
42784d9c625SLionel Sambuc * reopen as streams.
42884d9c625SLionel Sambuc */
42984d9c625SLionel Sambuc csc->to_fd = to_cs[1];
43084d9c625SLionel Sambuc csc->to_fp = fdopen(to_cs[1], "w");
43184d9c625SLionel Sambuc csc->from_fd = from_cs[0];
43284d9c625SLionel Sambuc csc->from_fp = fdopen(from_cs[0], "r");
43384d9c625SLionel Sambuc break;
43484d9c625SLionel Sambuc }
43584d9c625SLionel Sambuc return (0);
43684d9c625SLionel Sambuc }
43784d9c625SLionel Sambuc
43884d9c625SLionel Sambuc /*
43984d9c625SLionel Sambuc * cscope_find --
44084d9c625SLionel Sambuc * The cscope find command.
44184d9c625SLionel Sambuc */
44284d9c625SLionel Sambuc static int
cscope_find(SCR * sp,EXCMD * cmdp,const CHAR_T * pattern)44384d9c625SLionel Sambuc cscope_find(SCR *sp, EXCMD *cmdp, const CHAR_T *pattern)
44484d9c625SLionel Sambuc {
44584d9c625SLionel Sambuc CSC *csc, *csc_next;
44684d9c625SLionel Sambuc EX_PRIVATE *exp;
44784d9c625SLionel Sambuc FREF *frp;
44884d9c625SLionel Sambuc TAGQ *rtqp, *tqp;
44984d9c625SLionel Sambuc TAG *rtp;
45084d9c625SLionel Sambuc db_recno_t lno;
45184d9c625SLionel Sambuc size_t cno, search;
45284d9c625SLionel Sambuc int force, istmp, matches;
45384d9c625SLionel Sambuc const char *np = NULL;
45484d9c625SLionel Sambuc size_t nlen;
45584d9c625SLionel Sambuc
45684d9c625SLionel Sambuc exp = EXP(sp);
45784d9c625SLionel Sambuc
45884d9c625SLionel Sambuc /* Check for connections. */
45984d9c625SLionel Sambuc if (LIST_EMPTY(&exp->cscq)) {
46084d9c625SLionel Sambuc msgq(sp, M_ERR, "310|No cscope connections running");
46184d9c625SLionel Sambuc return (1);
46284d9c625SLionel Sambuc }
46384d9c625SLionel Sambuc
46484d9c625SLionel Sambuc /*
46584d9c625SLionel Sambuc * Allocate all necessary memory before doing anything hard. If the
46684d9c625SLionel Sambuc * tags stack is empty, we'll need the `local context' TAGQ structure
46784d9c625SLionel Sambuc * later.
46884d9c625SLionel Sambuc */
46984d9c625SLionel Sambuc rtp = NULL;
47084d9c625SLionel Sambuc rtqp = NULL;
47184d9c625SLionel Sambuc if (TAILQ_EMPTY(&exp->tq)) {
47284d9c625SLionel Sambuc /* Initialize the `local context' tag queue structure. */
47384d9c625SLionel Sambuc CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
47484d9c625SLionel Sambuc TAILQ_INIT(&rtqp->tagq);
47584d9c625SLionel Sambuc
47684d9c625SLionel Sambuc /* Initialize and link in its tag structure. */
47784d9c625SLionel Sambuc CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
47884d9c625SLionel Sambuc TAILQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
47984d9c625SLionel Sambuc rtqp->current = rtp;
48084d9c625SLionel Sambuc }
48184d9c625SLionel Sambuc
48284d9c625SLionel Sambuc /* Create the cscope command. */
48384d9c625SLionel Sambuc INT2CHAR(sp, pattern, STRLEN(pattern) + 1, np, nlen);
48484d9c625SLionel Sambuc np = strdup(np);
48584d9c625SLionel Sambuc if ((tqp = create_cs_cmd(sp, np, &search)) == NULL)
48684d9c625SLionel Sambuc goto err;
48784d9c625SLionel Sambuc
48884d9c625SLionel Sambuc /*
48984d9c625SLionel Sambuc * Stick the current context in a convenient place, we'll lose it
49084d9c625SLionel Sambuc * when we switch files.
49184d9c625SLionel Sambuc */
49284d9c625SLionel Sambuc frp = sp->frp;
49384d9c625SLionel Sambuc lno = sp->lno;
49484d9c625SLionel Sambuc cno = sp->cno;
49584d9c625SLionel Sambuc istmp = F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN);
49684d9c625SLionel Sambuc
49784d9c625SLionel Sambuc /* Search all open connections for a match. */
49884d9c625SLionel Sambuc matches = 0;
49984d9c625SLionel Sambuc LIST_FOREACH_SAFE(csc, &exp->cscq, q, csc_next) {
50084d9c625SLionel Sambuc /*
50184d9c625SLionel Sambuc * Send the command to the cscope program. (We skip the
50284d9c625SLionel Sambuc * first two bytes of the command, because we stored the
50384d9c625SLionel Sambuc * search cscope command character and a leading space
50484d9c625SLionel Sambuc * there.)
50584d9c625SLionel Sambuc */
50684d9c625SLionel Sambuc (void)fprintf(csc->to_fp, "%zu%s\n", search, tqp->tag + 2);
50784d9c625SLionel Sambuc (void)fflush(csc->to_fp);
50884d9c625SLionel Sambuc
50984d9c625SLionel Sambuc /* Read the output. */
51084d9c625SLionel Sambuc if (parse(sp, csc, tqp, &matches)) {
51184d9c625SLionel Sambuc free(rtqp);
51284d9c625SLionel Sambuc tagq_free(sp, tqp);
51384d9c625SLionel Sambuc return (1);
51484d9c625SLionel Sambuc }
51584d9c625SLionel Sambuc }
51684d9c625SLionel Sambuc
51784d9c625SLionel Sambuc if (matches == 0) {
51884d9c625SLionel Sambuc free(rtqp);
51984d9c625SLionel Sambuc msgq(sp, M_INFO, "278|No matches for query");
52084d9c625SLionel Sambuc return (0);
52184d9c625SLionel Sambuc }
52284d9c625SLionel Sambuc
52384d9c625SLionel Sambuc tqp->current = TAILQ_FIRST(&tqp->tagq);
52484d9c625SLionel Sambuc
52584d9c625SLionel Sambuc /* Try to switch to the first tag. */
52684d9c625SLionel Sambuc force = FL_ISSET(cmdp->iflags, E_C_FORCE);
52784d9c625SLionel Sambuc if (F_ISSET(cmdp, E_NEWSCREEN)) {
52884d9c625SLionel Sambuc if (ex_tag_Nswitch(sp, tqp->current, force))
52984d9c625SLionel Sambuc goto err;
53084d9c625SLionel Sambuc
53184d9c625SLionel Sambuc /* Everything else gets done in the new screen. */
53284d9c625SLionel Sambuc sp = sp->nextdisp;
53384d9c625SLionel Sambuc exp = EXP(sp);
53484d9c625SLionel Sambuc } else
53584d9c625SLionel Sambuc if (ex_tag_nswitch(sp, tqp->current, force))
53684d9c625SLionel Sambuc goto err;
53784d9c625SLionel Sambuc
53884d9c625SLionel Sambuc /*
53984d9c625SLionel Sambuc * If this is the first tag, put a `current location' queue entry
54084d9c625SLionel Sambuc * in place, so we can pop all the way back to the current mark.
54184d9c625SLionel Sambuc * Note, it doesn't point to much of anything, it's a placeholder.
54284d9c625SLionel Sambuc */
54384d9c625SLionel Sambuc if (TAILQ_EMPTY(&exp->tq)) {
54484d9c625SLionel Sambuc TAILQ_INSERT_HEAD(&exp->tq, rtqp, q);
545*0a6a1f1dSLionel Sambuc F_SET(rtqp, TAG_IS_LINKED);
54684d9c625SLionel Sambuc } else {
54784d9c625SLionel Sambuc free(rtqp);
54884d9c625SLionel Sambuc rtqp = TAILQ_FIRST(&exp->tq);
54984d9c625SLionel Sambuc }
55084d9c625SLionel Sambuc
55184d9c625SLionel Sambuc /* Link the current TAGQ structure into place. */
55284d9c625SLionel Sambuc TAILQ_INSERT_HEAD(&exp->tq, tqp, q);
553*0a6a1f1dSLionel Sambuc F_SET(tqp, TAG_IS_LINKED);
55484d9c625SLionel Sambuc
55584d9c625SLionel Sambuc (void)cscope_search(sp, tqp, tqp->current);
55684d9c625SLionel Sambuc
55784d9c625SLionel Sambuc /*
55884d9c625SLionel Sambuc * Move the current context from the temporary save area into the
55984d9c625SLionel Sambuc * right structure.
56084d9c625SLionel Sambuc *
56184d9c625SLionel Sambuc * If we were in a temporary file, we don't have a context to which
56284d9c625SLionel Sambuc * we can return, so just make it be the same as what we're moving
56384d9c625SLionel Sambuc * to. It will be a little odd that ^T doesn't change anything, but
56484d9c625SLionel Sambuc * I don't think it's a big deal.
56584d9c625SLionel Sambuc */
56684d9c625SLionel Sambuc if (istmp) {
56784d9c625SLionel Sambuc rtqp->current->frp = sp->frp;
56884d9c625SLionel Sambuc rtqp->current->lno = sp->lno;
56984d9c625SLionel Sambuc rtqp->current->cno = sp->cno;
57084d9c625SLionel Sambuc } else {
57184d9c625SLionel Sambuc rtqp->current->frp = frp;
57284d9c625SLionel Sambuc rtqp->current->lno = lno;
57384d9c625SLionel Sambuc rtqp->current->cno = cno;
57484d9c625SLionel Sambuc }
57584d9c625SLionel Sambuc
57684d9c625SLionel Sambuc return (0);
57784d9c625SLionel Sambuc
57884d9c625SLionel Sambuc err:
57984d9c625SLionel Sambuc alloc_err:
58084d9c625SLionel Sambuc free(rtqp);
58184d9c625SLionel Sambuc free(rtp);
58284d9c625SLionel Sambuc free(__UNCONST(np));
58384d9c625SLionel Sambuc return (1);
58484d9c625SLionel Sambuc }
58584d9c625SLionel Sambuc
58684d9c625SLionel Sambuc /*
58784d9c625SLionel Sambuc * create_cs_cmd --
58884d9c625SLionel Sambuc * Build a cscope command, creating and initializing the base TAGQ.
58984d9c625SLionel Sambuc */
59084d9c625SLionel Sambuc static TAGQ *
create_cs_cmd(SCR * sp,const char * pattern,size_t * searchp)59184d9c625SLionel Sambuc create_cs_cmd(SCR *sp, const char *pattern, size_t *searchp)
59284d9c625SLionel Sambuc {
59384d9c625SLionel Sambuc CB *cbp;
59484d9c625SLionel Sambuc TAGQ *tqp;
59584d9c625SLionel Sambuc size_t tlen;
59684d9c625SLionel Sambuc const char *p;
59784d9c625SLionel Sambuc
59884d9c625SLionel Sambuc /*
59984d9c625SLionel Sambuc * Cscope supports a "change pattern" command which we never use,
60084d9c625SLionel Sambuc * cscope command 5. Set CSCOPE_QUERIES[5] to " " since the user
60184d9c625SLionel Sambuc * can't pass " " as the first character of pattern. That way the
60284d9c625SLionel Sambuc * user can't ask for pattern 5 so we don't need any special-case
60384d9c625SLionel Sambuc * code.
60484d9c625SLionel Sambuc */
60584d9c625SLionel Sambuc #define CSCOPE_QUERIES "sgdct efi"
60684d9c625SLionel Sambuc
60784d9c625SLionel Sambuc if (pattern == NULL)
60884d9c625SLionel Sambuc goto usage;
60984d9c625SLionel Sambuc
61084d9c625SLionel Sambuc /* Skip leading blanks, check for command character. */
61184d9c625SLionel Sambuc for (; isblank((unsigned char)pattern[0]); ++pattern);
61284d9c625SLionel Sambuc if (pattern[0] == '\0' || !isblank((unsigned char)pattern[1]))
61384d9c625SLionel Sambuc goto usage;
61484d9c625SLionel Sambuc for (*searchp = 0, p = CSCOPE_QUERIES;
61584d9c625SLionel Sambuc *p != '\0' && *p != pattern[0]; ++*searchp, ++p);
61684d9c625SLionel Sambuc if (*p == '\0') {
61784d9c625SLionel Sambuc msgq(sp, M_ERR,
61884d9c625SLionel Sambuc "311|%s: unknown search type: use one of %s",
61984d9c625SLionel Sambuc KEY_NAME(sp, pattern[0]), CSCOPE_QUERIES);
62084d9c625SLionel Sambuc return (NULL);
62184d9c625SLionel Sambuc }
62284d9c625SLionel Sambuc
62384d9c625SLionel Sambuc /* Skip <blank> characters to the pattern. */
62484d9c625SLionel Sambuc for (p = pattern + 1; *p != '\0' && isblank((unsigned char)*p); ++p);
62584d9c625SLionel Sambuc if (*p == '\0') {
62684d9c625SLionel Sambuc usage: (void)csc_help(sp, "find");
62784d9c625SLionel Sambuc return (NULL);
62884d9c625SLionel Sambuc }
62984d9c625SLionel Sambuc
63084d9c625SLionel Sambuc /* The user can specify the contents of a buffer as the pattern. */
63184d9c625SLionel Sambuc cbp = NULL;
63284d9c625SLionel Sambuc if (p[0] == '"' && p[1] != '\0' && p[2] == '\0')
63384d9c625SLionel Sambuc CBNAME(sp, cbp, p[1]);
63484d9c625SLionel Sambuc if (cbp != NULL) {
63584d9c625SLionel Sambuc TEXT *t = TAILQ_FIRST(&cbp->textq);
63684d9c625SLionel Sambuc INT2CHAR(sp, t->lb, t->len, p, tlen);
63784d9c625SLionel Sambuc } else
63884d9c625SLionel Sambuc tlen = strlen(p);
63984d9c625SLionel Sambuc
64084d9c625SLionel Sambuc /* Allocate and initialize the TAGQ structure. */
64184d9c625SLionel Sambuc CALLOC(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + tlen + 3);
64284d9c625SLionel Sambuc if (tqp == NULL)
64384d9c625SLionel Sambuc return (NULL);
64484d9c625SLionel Sambuc TAILQ_INIT(&tqp->tagq);
64584d9c625SLionel Sambuc tqp->tag = tqp->buf;
64684d9c625SLionel Sambuc tqp->tag[0] = pattern[0];
64784d9c625SLionel Sambuc tqp->tag[1] = ' ';
64884d9c625SLionel Sambuc tqp->tlen = tlen + 2;
64984d9c625SLionel Sambuc memcpy(tqp->tag + 2, p, tlen);
65084d9c625SLionel Sambuc tqp->tag[tlen + 2] = '\0';
65184d9c625SLionel Sambuc F_SET(tqp, TAG_CSCOPE);
65284d9c625SLionel Sambuc
65384d9c625SLionel Sambuc return (tqp);
65484d9c625SLionel Sambuc }
65584d9c625SLionel Sambuc
65684d9c625SLionel Sambuc /*
65784d9c625SLionel Sambuc * parse --
65884d9c625SLionel Sambuc * Parse the cscope output.
65984d9c625SLionel Sambuc */
66084d9c625SLionel Sambuc static int
parse(SCR * sp,CSC * csc,TAGQ * tqp,int * matchesp)66184d9c625SLionel Sambuc parse(SCR *sp, CSC *csc, TAGQ *tqp, int *matchesp)
66284d9c625SLionel Sambuc {
66384d9c625SLionel Sambuc TAG *tp;
66484d9c625SLionel Sambuc db_recno_t slno = 0;
66584d9c625SLionel Sambuc size_t dlen, nlen = 0, slen = 0;
66684d9c625SLionel Sambuc int ch, i, isolder = 0, nlines;
66784d9c625SLionel Sambuc char *dname = NULL, *name = NULL, *search, *p, *t, dummy[2], buf[2048];
66884d9c625SLionel Sambuc
66984d9c625SLionel Sambuc for (;;) {
67084d9c625SLionel Sambuc if (!fgets(buf, sizeof(buf), csc->from_fp))
67184d9c625SLionel Sambuc goto io_err;
67284d9c625SLionel Sambuc
67384d9c625SLionel Sambuc /*
67484d9c625SLionel Sambuc * If the database is out of date, or there's some other
67584d9c625SLionel Sambuc * problem, cscope will output error messages before the
67684d9c625SLionel Sambuc * number-of-lines output. Display/discard any output
67784d9c625SLionel Sambuc * that doesn't match what we want.
67884d9c625SLionel Sambuc */
67984d9c625SLionel Sambuc #define CSCOPE_NLINES_FMT "cscope: %d lines%1[\n]"
68084d9c625SLionel Sambuc if (sscanf(buf, CSCOPE_NLINES_FMT, &nlines, dummy) == 2)
68184d9c625SLionel Sambuc break;
68284d9c625SLionel Sambuc if ((p = strchr(buf, '\n')) != NULL)
68384d9c625SLionel Sambuc *p = '\0';
68484d9c625SLionel Sambuc msgq(sp, M_ERR, "%s: \"%s\"", csc->dname, buf);
68584d9c625SLionel Sambuc }
68684d9c625SLionel Sambuc
68784d9c625SLionel Sambuc while (nlines--) {
68884d9c625SLionel Sambuc if (fgets(buf, sizeof(buf), csc->from_fp) == NULL)
68984d9c625SLionel Sambuc goto io_err;
69084d9c625SLionel Sambuc
69184d9c625SLionel Sambuc /* If the line's too long for the buffer, discard it. */
69284d9c625SLionel Sambuc if ((p = strchr(buf, '\n')) == NULL) {
69384d9c625SLionel Sambuc while ((ch = getc(csc->from_fp)) != EOF && ch != '\n');
69484d9c625SLionel Sambuc continue;
69584d9c625SLionel Sambuc }
69684d9c625SLionel Sambuc *p = '\0';
69784d9c625SLionel Sambuc
69884d9c625SLionel Sambuc /*
69984d9c625SLionel Sambuc * The cscope output is in the following format:
70084d9c625SLionel Sambuc *
70184d9c625SLionel Sambuc * <filename> <context> <line number> <pattern>
70284d9c625SLionel Sambuc *
70384d9c625SLionel Sambuc * Figure out how long everything is so we can allocate in one
70484d9c625SLionel Sambuc * swell foop, but discard anything that looks wrong.
70584d9c625SLionel Sambuc */
70684d9c625SLionel Sambuc for (p = buf, i = 0;
70784d9c625SLionel Sambuc i < 3 && (t = strsep(&p, "\t ")) != NULL; ++i)
70884d9c625SLionel Sambuc switch (i) {
70984d9c625SLionel Sambuc case 0: /* Filename. */
71084d9c625SLionel Sambuc name = t;
71184d9c625SLionel Sambuc nlen = strlen(name);
71284d9c625SLionel Sambuc break;
71384d9c625SLionel Sambuc case 1: /* Context. */
71484d9c625SLionel Sambuc break;
71584d9c625SLionel Sambuc case 2: /* Line number. */
71684d9c625SLionel Sambuc slno = (db_recno_t)atol(t);
71784d9c625SLionel Sambuc break;
71884d9c625SLionel Sambuc }
71984d9c625SLionel Sambuc if (i != 3 || p == NULL || t == NULL)
72084d9c625SLionel Sambuc continue;
72184d9c625SLionel Sambuc
72284d9c625SLionel Sambuc /* The rest of the string is the search pattern. */
72384d9c625SLionel Sambuc search = p;
72484d9c625SLionel Sambuc slen = strlen(p);
72584d9c625SLionel Sambuc
72684d9c625SLionel Sambuc /* Resolve the file name. */
72784d9c625SLionel Sambuc csc_file(sp, csc, name, &dname, &dlen, &isolder);
72884d9c625SLionel Sambuc
72984d9c625SLionel Sambuc /*
73084d9c625SLionel Sambuc * If the file is older than the cscope database, that is,
73184d9c625SLionel Sambuc * the database was built since the file was last modified,
73284d9c625SLionel Sambuc * or there wasn't a search string, use the line number.
73384d9c625SLionel Sambuc */
73484d9c625SLionel Sambuc if (isolder || strcmp(search, "<unknown>") == 0) {
73584d9c625SLionel Sambuc search = NULL;
73684d9c625SLionel Sambuc slen = 0;
73784d9c625SLionel Sambuc }
73884d9c625SLionel Sambuc
73984d9c625SLionel Sambuc /*
74084d9c625SLionel Sambuc * Allocate and initialize a tag structure plus the variable
74184d9c625SLionel Sambuc * length cscope information that follows it.
74284d9c625SLionel Sambuc */
74384d9c625SLionel Sambuc CALLOC_RET(sp, tp,
74484d9c625SLionel Sambuc TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1);
74584d9c625SLionel Sambuc tp->fname = (char *)tp->buf;
74684d9c625SLionel Sambuc if (dlen != 0) {
74784d9c625SLionel Sambuc memcpy(tp->fname, dname, dlen);
74884d9c625SLionel Sambuc tp->fname[dlen] = '/';
74984d9c625SLionel Sambuc ++dlen;
75084d9c625SLionel Sambuc }
75184d9c625SLionel Sambuc memcpy(tp->fname + dlen, name, nlen + 1);
75284d9c625SLionel Sambuc tp->fnlen = dlen + nlen;
75384d9c625SLionel Sambuc tp->slno = slno;
75484d9c625SLionel Sambuc if (slen != 0) {
75584d9c625SLionel Sambuc tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1);
75684d9c625SLionel Sambuc MEMCPYW(tp->search, search, (tp->slen = slen) + 1);
75784d9c625SLionel Sambuc }
75884d9c625SLionel Sambuc TAILQ_INSERT_TAIL(&tqp->tagq, tp, q);
75984d9c625SLionel Sambuc
76084d9c625SLionel Sambuc ++*matchesp;
76184d9c625SLionel Sambuc }
76284d9c625SLionel Sambuc
76384d9c625SLionel Sambuc return read_prompt(sp, csc);
76484d9c625SLionel Sambuc
76584d9c625SLionel Sambuc io_err: if (feof(csc->from_fp))
76684d9c625SLionel Sambuc errno = EIO;
76784d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, "%s", csc->dname);
76884d9c625SLionel Sambuc terminate(sp, csc, 0);
76984d9c625SLionel Sambuc return (1);
77084d9c625SLionel Sambuc }
77184d9c625SLionel Sambuc
77284d9c625SLionel Sambuc /*
77384d9c625SLionel Sambuc * csc_file --
77484d9c625SLionel Sambuc * Search for the right path to this file.
77584d9c625SLionel Sambuc */
77684d9c625SLionel Sambuc static void
csc_file(SCR * sp,CSC * csc,char * name,char ** dirp,size_t * dlenp,int * isolderp)77784d9c625SLionel Sambuc csc_file(SCR *sp, CSC *csc, char *name, char **dirp, size_t *dlenp, int *isolderp)
77884d9c625SLionel Sambuc {
77984d9c625SLionel Sambuc struct stat sb;
78084d9c625SLionel Sambuc char **pp, buf[MAXPATHLEN];
78184d9c625SLionel Sambuc
78284d9c625SLionel Sambuc /*
78384d9c625SLionel Sambuc * Check for the file in all of the listed paths. If we don't
78484d9c625SLionel Sambuc * find it, we simply return it unchanged. We have to do this
78584d9c625SLionel Sambuc * now, even though it's expensive, because if the user changes
78684d9c625SLionel Sambuc * directories, we can't change our minds as to where the file
78784d9c625SLionel Sambuc * lives.
78884d9c625SLionel Sambuc */
78984d9c625SLionel Sambuc for (pp = csc->paths; *pp != NULL; ++pp) {
79084d9c625SLionel Sambuc (void)snprintf(buf, sizeof(buf), "%s/%s", *pp, name);
79184d9c625SLionel Sambuc if (stat(buf, &sb) == 0) {
79284d9c625SLionel Sambuc *dirp = *pp;
79384d9c625SLionel Sambuc *dlenp = strlen(*pp);
79484d9c625SLionel Sambuc *isolderp = sb.st_mtime < csc->mtime;
79584d9c625SLionel Sambuc return;
79684d9c625SLionel Sambuc }
79784d9c625SLionel Sambuc }
79884d9c625SLionel Sambuc *dlenp = 0;
79984d9c625SLionel Sambuc }
80084d9c625SLionel Sambuc
80184d9c625SLionel Sambuc /*
80284d9c625SLionel Sambuc * cscope_help --
80384d9c625SLionel Sambuc * The cscope help command.
80484d9c625SLionel Sambuc */
80584d9c625SLionel Sambuc static int
cscope_help(SCR * sp,EXCMD * cmdp,const CHAR_T * subcmd)80684d9c625SLionel Sambuc cscope_help(SCR *sp, EXCMD *cmdp, const CHAR_T *subcmd)
80784d9c625SLionel Sambuc {
80884d9c625SLionel Sambuc const char *np;
80984d9c625SLionel Sambuc size_t nlen;
81084d9c625SLionel Sambuc
81184d9c625SLionel Sambuc INT2CHAR(sp, subcmd, STRLEN(subcmd) + 1, np, nlen);
81284d9c625SLionel Sambuc return (csc_help(sp, np));
81384d9c625SLionel Sambuc }
81484d9c625SLionel Sambuc
81584d9c625SLionel Sambuc /*
81684d9c625SLionel Sambuc * csc_help --
81784d9c625SLionel Sambuc * Display help/usage messages.
81884d9c625SLionel Sambuc */
81984d9c625SLionel Sambuc static int
csc_help(SCR * sp,const char * cmd)82084d9c625SLionel Sambuc csc_help(SCR *sp, const char *cmd)
82184d9c625SLionel Sambuc {
82284d9c625SLionel Sambuc CC const *ccp;
82384d9c625SLionel Sambuc
82484d9c625SLionel Sambuc if (cmd != NULL && *cmd != '\0') {
82584d9c625SLionel Sambuc if ((ccp = lookup_ccmd(cmd)) == NULL) {
82684d9c625SLionel Sambuc ex_printf(sp,
82784d9c625SLionel Sambuc "%s doesn't match any cscope command\n", cmd);
82884d9c625SLionel Sambuc return (1);
82984d9c625SLionel Sambuc } else {
83084d9c625SLionel Sambuc ex_printf(sp,
83184d9c625SLionel Sambuc "Command: %s (%s)\n", ccp->name, ccp->help_msg);
83284d9c625SLionel Sambuc ex_printf(sp, " Usage: %s\n", ccp->usage_msg);
83384d9c625SLionel Sambuc return (0);
83484d9c625SLionel Sambuc }
83584d9c625SLionel Sambuc }
83684d9c625SLionel Sambuc
83784d9c625SLionel Sambuc ex_printf(sp, "cscope commands:\n");
83884d9c625SLionel Sambuc for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
83984d9c625SLionel Sambuc ex_printf(sp, " %*s: %s\n", 5, ccp->name, ccp->help_msg);
84084d9c625SLionel Sambuc return (0);
84184d9c625SLionel Sambuc }
84284d9c625SLionel Sambuc
84384d9c625SLionel Sambuc /*
84484d9c625SLionel Sambuc * cscope_kill --
84584d9c625SLionel Sambuc * The cscope kill command.
84684d9c625SLionel Sambuc */
84784d9c625SLionel Sambuc static int
cscope_kill(SCR * sp,EXCMD * cmdp,const CHAR_T * cn)84884d9c625SLionel Sambuc cscope_kill(SCR *sp, EXCMD *cmdp, const CHAR_T *cn)
84984d9c625SLionel Sambuc {
85084d9c625SLionel Sambuc const char *np;
85184d9c625SLionel Sambuc size_t nlen;
85284d9c625SLionel Sambuc
85384d9c625SLionel Sambuc INT2CHAR(sp, cn, STRLEN(cn) + 1, np, nlen);
85484d9c625SLionel Sambuc return (terminate(sp, NULL, atoi(np)));
85584d9c625SLionel Sambuc }
85684d9c625SLionel Sambuc
85784d9c625SLionel Sambuc /*
85884d9c625SLionel Sambuc * terminate --
85984d9c625SLionel Sambuc * Detach from a cscope process.
86084d9c625SLionel Sambuc */
86184d9c625SLionel Sambuc static int
terminate(SCR * sp,CSC * csc,int n)86284d9c625SLionel Sambuc terminate(SCR *sp, CSC *csc, int n)
86384d9c625SLionel Sambuc {
86484d9c625SLionel Sambuc EX_PRIVATE *exp;
86584d9c625SLionel Sambuc int i, pstat;
86684d9c625SLionel Sambuc
86784d9c625SLionel Sambuc exp = EXP(sp);
86884d9c625SLionel Sambuc
86984d9c625SLionel Sambuc /*
87084d9c625SLionel Sambuc * We either get a csc structure or a number. If not provided a
87184d9c625SLionel Sambuc * csc structure, find the right one.
87284d9c625SLionel Sambuc */
87384d9c625SLionel Sambuc if (csc == NULL) {
87484d9c625SLionel Sambuc if (n < 1)
87584d9c625SLionel Sambuc goto badno;
87684d9c625SLionel Sambuc i = 1;
87784d9c625SLionel Sambuc LIST_FOREACH(csc, &exp->cscq, q)
87884d9c625SLionel Sambuc if (i++ == n)
87984d9c625SLionel Sambuc break;
88084d9c625SLionel Sambuc if (csc == NULL) {
88184d9c625SLionel Sambuc badno: msgq(sp, M_ERR, "312|%d: no such cscope session", n);
88284d9c625SLionel Sambuc return (1);
88384d9c625SLionel Sambuc }
88484d9c625SLionel Sambuc }
88584d9c625SLionel Sambuc
88684d9c625SLionel Sambuc /*
88784d9c625SLionel Sambuc * XXX
88884d9c625SLionel Sambuc * Theoretically, we have the only file descriptors to the process,
88984d9c625SLionel Sambuc * so closing them should let it exit gracefully, deleting temporary
89084d9c625SLionel Sambuc * files, etc. The original vi cscope integration sent the cscope
89184d9c625SLionel Sambuc * connection a SIGTERM signal, so I'm not sure if closing the file
89284d9c625SLionel Sambuc * descriptors is sufficient.
89384d9c625SLionel Sambuc */
89484d9c625SLionel Sambuc if (csc->from_fp != NULL)
89584d9c625SLionel Sambuc (void)fclose(csc->from_fp);
89684d9c625SLionel Sambuc if (csc->to_fp != NULL)
89784d9c625SLionel Sambuc (void)fclose(csc->to_fp);
89884d9c625SLionel Sambuc (void)waitpid(csc->pid, &pstat, 0);
89984d9c625SLionel Sambuc
90084d9c625SLionel Sambuc /* Discard cscope connection information. */
90184d9c625SLionel Sambuc LIST_REMOVE(csc, q);
90284d9c625SLionel Sambuc if (csc->pbuf != NULL)
90384d9c625SLionel Sambuc free(csc->pbuf);
90484d9c625SLionel Sambuc if (csc->paths != NULL)
90584d9c625SLionel Sambuc free(csc->paths);
90684d9c625SLionel Sambuc free(csc);
90784d9c625SLionel Sambuc return (0);
90884d9c625SLionel Sambuc }
90984d9c625SLionel Sambuc
91084d9c625SLionel Sambuc /*
91184d9c625SLionel Sambuc * cscope_reset --
91284d9c625SLionel Sambuc * The cscope reset command.
91384d9c625SLionel Sambuc */
91484d9c625SLionel Sambuc static int
cscope_reset(SCR * sp,EXCMD * cmdp,const CHAR_T * notusedp)91584d9c625SLionel Sambuc cscope_reset(SCR *sp, EXCMD *cmdp, const CHAR_T *notusedp)
91684d9c625SLionel Sambuc {
91784d9c625SLionel Sambuc EX_PRIVATE *exp;
91884d9c625SLionel Sambuc
91984d9c625SLionel Sambuc for (exp = EXP(sp); !LIST_EMPTY(&exp->cscq);) {
92084d9c625SLionel Sambuc static CHAR_T one[] = {'1', 0};
92184d9c625SLionel Sambuc if (cscope_kill(sp, cmdp, one))
92284d9c625SLionel Sambuc return (1);
92384d9c625SLionel Sambuc }
92484d9c625SLionel Sambuc return (0);
92584d9c625SLionel Sambuc }
92684d9c625SLionel Sambuc
92784d9c625SLionel Sambuc /*
92884d9c625SLionel Sambuc * cscope_display --
92984d9c625SLionel Sambuc * Display current connections.
93084d9c625SLionel Sambuc *
93184d9c625SLionel Sambuc * PUBLIC: int cscope_display __P((SCR *));
93284d9c625SLionel Sambuc */
93384d9c625SLionel Sambuc int
cscope_display(SCR * sp)93484d9c625SLionel Sambuc cscope_display(SCR *sp)
93584d9c625SLionel Sambuc {
93684d9c625SLionel Sambuc EX_PRIVATE *exp;
93784d9c625SLionel Sambuc CSC *csc;
93884d9c625SLionel Sambuc int i;
93984d9c625SLionel Sambuc
94084d9c625SLionel Sambuc exp = EXP(sp);
94184d9c625SLionel Sambuc if (LIST_EMPTY(&exp->cscq)) {
94284d9c625SLionel Sambuc ex_printf(sp, "No cscope connections.\n");
94384d9c625SLionel Sambuc return (0);
94484d9c625SLionel Sambuc }
94584d9c625SLionel Sambuc i = 1;
94684d9c625SLionel Sambuc LIST_FOREACH(csc, &exp->cscq, q)
94784d9c625SLionel Sambuc ex_printf(sp,
94884d9c625SLionel Sambuc "%2d %s (process %lu)\n", i++, csc->dname, (u_long)csc->pid);
94984d9c625SLionel Sambuc return (0);
95084d9c625SLionel Sambuc }
95184d9c625SLionel Sambuc
95284d9c625SLionel Sambuc /*
95384d9c625SLionel Sambuc * cscope_search --
95484d9c625SLionel Sambuc * Search a file for a cscope entry.
95584d9c625SLionel Sambuc *
95684d9c625SLionel Sambuc * PUBLIC: int cscope_search __P((SCR *, TAGQ *, TAG *));
95784d9c625SLionel Sambuc */
95884d9c625SLionel Sambuc int
cscope_search(SCR * sp,TAGQ * tqp,TAG * tp)95984d9c625SLionel Sambuc cscope_search(SCR *sp, TAGQ *tqp, TAG *tp)
96084d9c625SLionel Sambuc {
96184d9c625SLionel Sambuc MARK m;
96284d9c625SLionel Sambuc
96384d9c625SLionel Sambuc /* If we don't have a search pattern, use the line number. */
96484d9c625SLionel Sambuc if (tp->search == NULL) {
96584d9c625SLionel Sambuc if (!db_exist(sp, tp->slno)) {
96684d9c625SLionel Sambuc tag_msg(sp, TAG_BADLNO, tqp->tag);
96784d9c625SLionel Sambuc return (1);
96884d9c625SLionel Sambuc }
96984d9c625SLionel Sambuc m.lno = tp->slno;
97084d9c625SLionel Sambuc } else {
97184d9c625SLionel Sambuc /*
97284d9c625SLionel Sambuc * Search for the tag; cheap fallback for C functions
97384d9c625SLionel Sambuc * if the name is the same but the arguments have changed.
97484d9c625SLionel Sambuc */
97584d9c625SLionel Sambuc m.lno = 1;
97684d9c625SLionel Sambuc m.cno = 0;
97784d9c625SLionel Sambuc if (f_search(sp, &m, &m,
97884d9c625SLionel Sambuc tp->search, tp->slen, NULL, SEARCH_CSCOPE | SEARCH_FIRST)) {
97984d9c625SLionel Sambuc tag_msg(sp, TAG_SEARCH, tqp->tag);
98084d9c625SLionel Sambuc return (1);
98184d9c625SLionel Sambuc }
98284d9c625SLionel Sambuc
98384d9c625SLionel Sambuc /*
98484d9c625SLionel Sambuc * !!!
98584d9c625SLionel Sambuc * Historically, tags set the search direction if it wasn't
98684d9c625SLionel Sambuc * already set.
98784d9c625SLionel Sambuc */
98884d9c625SLionel Sambuc if (sp->searchdir == NOTSET)
98984d9c625SLionel Sambuc sp->searchdir = FORWARD;
99084d9c625SLionel Sambuc }
99184d9c625SLionel Sambuc
99284d9c625SLionel Sambuc /*
99384d9c625SLionel Sambuc * !!!
99484d9c625SLionel Sambuc * Tags move to the first non-blank, NOT the search pattern start.
99584d9c625SLionel Sambuc */
99684d9c625SLionel Sambuc sp->lno = m.lno;
99784d9c625SLionel Sambuc sp->cno = 0;
99884d9c625SLionel Sambuc (void)nonblank(sp, sp->lno, &sp->cno);
99984d9c625SLionel Sambuc return (0);
100084d9c625SLionel Sambuc }
100184d9c625SLionel Sambuc
100284d9c625SLionel Sambuc
100384d9c625SLionel Sambuc /*
100484d9c625SLionel Sambuc * lookup_ccmd --
100584d9c625SLionel Sambuc * Return a pointer to the command structure.
100684d9c625SLionel Sambuc */
100784d9c625SLionel Sambuc static CC const *
lookup_ccmd(const char * name)100884d9c625SLionel Sambuc lookup_ccmd(const char *name)
100984d9c625SLionel Sambuc {
101084d9c625SLionel Sambuc CC const *ccp;
101184d9c625SLionel Sambuc size_t len;
101284d9c625SLionel Sambuc
101384d9c625SLionel Sambuc len = strlen(name);
101484d9c625SLionel Sambuc for (ccp = cscope_cmds; ccp->name != NULL; ++ccp)
101584d9c625SLionel Sambuc if (strncmp(name, ccp->name, len) == 0)
101684d9c625SLionel Sambuc return (ccp);
101784d9c625SLionel Sambuc return (NULL);
101884d9c625SLionel Sambuc }
101984d9c625SLionel Sambuc
102084d9c625SLionel Sambuc /*
102184d9c625SLionel Sambuc * read_prompt --
102284d9c625SLionel Sambuc * Read a prompt from cscope.
102384d9c625SLionel Sambuc */
102484d9c625SLionel Sambuc static int
read_prompt(SCR * sp,CSC * csc)102584d9c625SLionel Sambuc read_prompt(SCR *sp, CSC *csc)
102684d9c625SLionel Sambuc {
102784d9c625SLionel Sambuc int ch;
102884d9c625SLionel Sambuc
102984d9c625SLionel Sambuc #define CSCOPE_PROMPT ">> "
103084d9c625SLionel Sambuc for (;;) {
103184d9c625SLionel Sambuc while ((ch =
103284d9c625SLionel Sambuc getc(csc->from_fp)) != EOF && ch != CSCOPE_PROMPT[0]);
103384d9c625SLionel Sambuc if (ch == EOF) {
103484d9c625SLionel Sambuc terminate(sp, csc, 0);
103584d9c625SLionel Sambuc return (1);
103684d9c625SLionel Sambuc }
103784d9c625SLionel Sambuc if (getc(csc->from_fp) != CSCOPE_PROMPT[1])
103884d9c625SLionel Sambuc continue;
103984d9c625SLionel Sambuc if (getc(csc->from_fp) != CSCOPE_PROMPT[2])
104084d9c625SLionel Sambuc continue;
104184d9c625SLionel Sambuc break;
104284d9c625SLionel Sambuc }
104384d9c625SLionel Sambuc return (0);
104484d9c625SLionel Sambuc }
1045