xref: /minix3/external/bsd/nvi/dist/ex/ex_cscope.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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