mirror of
https://github.com/neovim/neovim.git
synced 2025-09-29 22:48:34 +00:00

Diagnostic : False positive. Rationale : Coverity thinks we are forgetting to add more char to hold NULL, but it's not taking into account that two chars from cntxformat will no be present in the result. In fact, we can even allocate one byte less than currently done. Resolution : Add explanatory comment and allocate one less byte. Marked as "Intentional" at coverity's database.
2118 lines
53 KiB
C
2118 lines
53 KiB
C
/*
|
|
* CSCOPE support for Vim added by Andy Kahn <kahn@zk3.dec.com>
|
|
* Ported to Win32 by Sergey Khorev <sergey.khorev@gmail.com>
|
|
*
|
|
* The basic idea/structure of cscope for Vim was borrowed from Nvi. There
|
|
* might be a few lines of code that look similar to what Nvi has.
|
|
*
|
|
* See README.txt for an overview of the Vim source code.
|
|
*/
|
|
#include <stdbool.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "nvim/vim.h"
|
|
#include "nvim/ascii.h"
|
|
#include "nvim/if_cscope.h"
|
|
#include "nvim/charset.h"
|
|
#include "nvim/eval.h"
|
|
#include "nvim/fileio.h"
|
|
#include "nvim/message.h"
|
|
#include "nvim/misc1.h"
|
|
#include "nvim/misc2.h"
|
|
#include "nvim/memory.h"
|
|
#include "nvim/os/time.h"
|
|
#include "nvim/path.h"
|
|
#include "nvim/quickfix.h"
|
|
#include "nvim/strings.h"
|
|
#include "nvim/tag.h"
|
|
#include "nvim/tempfile.h"
|
|
#include "nvim/window.h"
|
|
#include "nvim/os/os.h"
|
|
#include "nvim/os/input.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#if defined(UNIX)
|
|
# include <sys/wait.h>
|
|
#endif
|
|
#include "nvim/if_cscope_defs.h"
|
|
|
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
# include "if_cscope.c.generated.h"
|
|
#endif
|
|
|
|
static csinfo_T * csinfo = NULL;
|
|
static int csinfo_size = 0; /* number of items allocated in
|
|
csinfo[] */
|
|
|
|
static int eap_arg_len; /* length of eap->arg, set in
|
|
cs_lookup_cmd() */
|
|
static cscmd_T cs_cmds[] =
|
|
{
|
|
{ "add", cs_add,
|
|
N_("Add a new database"), "add file|dir [pre-path] [flags]", 0 },
|
|
{ "find", cs_find,
|
|
N_("Query for a pattern"), "find c|d|e|f|g|i|s|t name", 1 },
|
|
{ "help", cs_help,
|
|
N_("Show this message"), "help", 0 },
|
|
{ "kill", cs_kill,
|
|
N_("Kill a connection"), "kill #", 0 },
|
|
{ "reset", cs_reset,
|
|
N_("Reinit all connections"), "reset", 0 },
|
|
{ "show", cs_show,
|
|
N_("Show connections"), "show", 0 },
|
|
{ NULL, NULL, NULL, NULL, 0 }
|
|
};
|
|
|
|
static void cs_usage_msg(csid_e x)
|
|
{
|
|
(void)EMSG2(_("E560: Usage: cs[cope] %s"), cs_cmds[(int)x].usage);
|
|
}
|
|
|
|
|
|
static enum {
|
|
EXP_CSCOPE_SUBCMD, /* expand ":cscope" sub-commands */
|
|
EXP_SCSCOPE_SUBCMD, /* expand ":scscope" sub-commands */
|
|
EXP_CSCOPE_FIND, /* expand ":cscope find" arguments */
|
|
EXP_CSCOPE_KILL /* expand ":cscope kill" arguments */
|
|
} expand_what;
|
|
|
|
/*
|
|
* Function given to ExpandGeneric() to obtain the cscope command
|
|
* expansion.
|
|
*/
|
|
char_u *get_cscope_name(expand_T *xp, int idx)
|
|
{
|
|
int current_idx;
|
|
int i;
|
|
|
|
switch (expand_what) {
|
|
case EXP_CSCOPE_SUBCMD:
|
|
/* Complete with sub-commands of ":cscope":
|
|
* add, find, help, kill, reset, show */
|
|
return (char_u *)cs_cmds[idx].name;
|
|
case EXP_SCSCOPE_SUBCMD:
|
|
/* Complete with sub-commands of ":scscope": same sub-commands as
|
|
* ":cscope" but skip commands which don't support split windows */
|
|
for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++)
|
|
if (cs_cmds[i].cansplit)
|
|
if (current_idx++ == idx)
|
|
break;
|
|
return (char_u *)cs_cmds[i].name;
|
|
case EXP_CSCOPE_FIND:
|
|
{
|
|
const char *query_type[] =
|
|
{
|
|
"c", "d", "e", "f", "g", "i", "s", "t", NULL
|
|
};
|
|
|
|
/* Complete with query type of ":cscope find {query_type}".
|
|
* {query_type} can be letters (c, d, ... t) or numbers (0, 1,
|
|
* ..., 8) but only complete with letters, since numbers are
|
|
* redundant. */
|
|
return (char_u *)query_type[idx];
|
|
}
|
|
case EXP_CSCOPE_KILL:
|
|
{
|
|
static char connection[5];
|
|
|
|
/* ":cscope kill" accepts connection numbers or partial names of
|
|
* the pathname of the cscope database as argument. Only complete
|
|
* with connection numbers. -1 can also be used to kill all
|
|
* connections. */
|
|
for (i = 0, current_idx = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname == NULL)
|
|
continue;
|
|
if (current_idx++ == idx) {
|
|
vim_snprintf(connection, sizeof(connection), "%d", i);
|
|
return (char_u *)connection;
|
|
}
|
|
}
|
|
return (current_idx == idx && idx > 0) ? (char_u *)"-1" : NULL;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle command line completion for :cscope command.
|
|
*/
|
|
void set_context_in_cscope_cmd(expand_T *xp, char_u *arg, cmdidx_T cmdidx)
|
|
{
|
|
char_u *p;
|
|
|
|
/* Default: expand subcommands */
|
|
xp->xp_context = EXPAND_CSCOPE;
|
|
xp->xp_pattern = arg;
|
|
expand_what = (cmdidx == CMD_scscope)
|
|
? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD;
|
|
|
|
/* (part of) subcommand already typed */
|
|
if (*arg != NUL) {
|
|
p = skiptowhite(arg);
|
|
if (*p != NUL) { /* past first word */
|
|
xp->xp_pattern = skipwhite(p);
|
|
if (*skiptowhite(xp->xp_pattern) != NUL)
|
|
xp->xp_context = EXPAND_NOTHING;
|
|
else if (STRNICMP(arg, "add", p - arg) == 0)
|
|
xp->xp_context = EXPAND_FILES;
|
|
else if (STRNICMP(arg, "kill", p - arg) == 0)
|
|
expand_what = EXP_CSCOPE_KILL;
|
|
else if (STRNICMP(arg, "find", p - arg) == 0)
|
|
expand_what = EXP_CSCOPE_FIND;
|
|
else
|
|
xp->xp_context = EXPAND_NOTHING;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* PRIVATE: do_cscope_general
|
|
*
|
|
* Find the command, print help if invalid, and then call the corresponding
|
|
* command function.
|
|
*/
|
|
static void
|
|
do_cscope_general (
|
|
exarg_T *eap,
|
|
int make_split /* whether to split window */
|
|
)
|
|
{
|
|
cscmd_T *cmdp;
|
|
|
|
if ((cmdp = cs_lookup_cmd(eap)) == NULL) {
|
|
cs_help(eap);
|
|
return;
|
|
}
|
|
|
|
if (make_split) {
|
|
if (!cmdp->cansplit) {
|
|
(void)MSG_PUTS(_(
|
|
"This cscope command does not support splitting the window.\n"));
|
|
return;
|
|
}
|
|
postponed_split = -1;
|
|
postponed_split_flags = cmdmod.split;
|
|
postponed_split_tab = cmdmod.tab;
|
|
}
|
|
|
|
cmdp->func(eap);
|
|
|
|
postponed_split_flags = 0;
|
|
postponed_split_tab = 0;
|
|
}
|
|
|
|
/*
|
|
* PUBLIC: do_cscope
|
|
*/
|
|
void do_cscope(exarg_T *eap)
|
|
{
|
|
do_cscope_general(eap, FALSE);
|
|
}
|
|
|
|
/*
|
|
* PUBLIC: do_scscope
|
|
*
|
|
* same as do_cscope, but splits window, too.
|
|
*/
|
|
void do_scscope(exarg_T *eap)
|
|
{
|
|
do_cscope_general(eap, TRUE);
|
|
}
|
|
|
|
/*
|
|
* PUBLIC: do_cstag
|
|
*
|
|
*/
|
|
void do_cstag(exarg_T *eap)
|
|
{
|
|
int ret = FALSE;
|
|
|
|
if (*eap->arg == NUL) {
|
|
(void)EMSG(_("E562: Usage: cstag <ident>"));
|
|
return;
|
|
}
|
|
|
|
switch (p_csto) {
|
|
case 0:
|
|
if (cs_check_for_connections()) {
|
|
ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
|
|
FALSE, *eap->cmdlinep);
|
|
if (ret == FALSE) {
|
|
cs_free_tags();
|
|
if (msg_col)
|
|
msg_putchar('\n');
|
|
|
|
if (cs_check_for_tags())
|
|
ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
|
|
}
|
|
} else if (cs_check_for_tags()) {
|
|
ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
|
|
}
|
|
break;
|
|
case 1:
|
|
if (cs_check_for_tags()) {
|
|
ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
|
|
if (ret == FALSE) {
|
|
if (msg_col)
|
|
msg_putchar('\n');
|
|
|
|
if (cs_check_for_connections()) {
|
|
ret = cs_find_common("g", (char *)(eap->arg), eap->forceit,
|
|
FALSE, FALSE, *eap->cmdlinep);
|
|
if (ret == FALSE)
|
|
cs_free_tags();
|
|
}
|
|
}
|
|
} else if (cs_check_for_connections()) {
|
|
ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
|
|
FALSE, *eap->cmdlinep);
|
|
if (ret == FALSE)
|
|
cs_free_tags();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!ret) {
|
|
(void)EMSG(_("E257: cstag: tag not found"));
|
|
g_do_tagpreview = 0;
|
|
}
|
|
|
|
} /* do_cscope */
|
|
|
|
|
|
/*
|
|
* PUBLIC: cs_find
|
|
*
|
|
* this simulates a vim_fgets(), but for cscope, returns the next line
|
|
* from the cscope output. should only be called from find_tags()
|
|
*
|
|
* returns TRUE if eof, FALSE otherwise
|
|
*/
|
|
int cs_fgets(char_u *buf, int size)
|
|
{
|
|
char *p;
|
|
|
|
if ((p = cs_manage_matches(NULL, NULL, -1, Get)) == NULL)
|
|
return TRUE;
|
|
STRLCPY(buf, p, size);
|
|
|
|
return FALSE;
|
|
} /* cs_fgets */
|
|
|
|
|
|
/*
|
|
* PUBLIC: cs_free_tags
|
|
*
|
|
* called only from do_tag(), when popping the tag stack
|
|
*/
|
|
void cs_free_tags(void)
|
|
{
|
|
cs_manage_matches(NULL, NULL, -1, Free);
|
|
}
|
|
|
|
/*
|
|
* PUBLIC: cs_print_tags
|
|
*
|
|
* called from do_tag()
|
|
*/
|
|
void cs_print_tags(void)
|
|
{
|
|
cs_manage_matches(NULL, NULL, -1, Print);
|
|
}
|
|
|
|
/*
|
|
* "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
|
|
*
|
|
* Checks for the existence of a |cscope| connection. If no
|
|
* parameters are specified, then the function returns:
|
|
*
|
|
* 0, if cscope was not available (not compiled in), or if there
|
|
* are no cscope connections; or
|
|
* 1, if there is at least one cscope connection.
|
|
*
|
|
* If parameters are specified, then the value of {num}
|
|
* determines how existence of a cscope connection is checked:
|
|
*
|
|
* {num} Description of existence check
|
|
* ----- ------------------------------
|
|
* 0 Same as no parameters (e.g., "cscope_connection()").
|
|
* 1 Ignore {prepend}, and use partial string matches for
|
|
* {dbpath}.
|
|
* 2 Ignore {prepend}, and use exact string matches for
|
|
* {dbpath}.
|
|
* 3 Use {prepend}, use partial string matches for both
|
|
* {dbpath} and {prepend}.
|
|
* 4 Use {prepend}, use exact string matches for both
|
|
* {dbpath} and {prepend}.
|
|
*
|
|
* Note: All string comparisons are case sensitive!
|
|
*/
|
|
int cs_connection(int num, char_u *dbpath, char_u *ppath)
|
|
{
|
|
int i;
|
|
|
|
if (num < 0 || num > 4 || (num > 0 && !dbpath))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (!csinfo[i].fname)
|
|
continue;
|
|
|
|
if (num == 0)
|
|
return TRUE;
|
|
|
|
switch (num) {
|
|
case 1:
|
|
if (strstr(csinfo[i].fname, (char *)dbpath))
|
|
return TRUE;
|
|
break;
|
|
case 2:
|
|
if (strcmp(csinfo[i].fname, (char *)dbpath) == 0)
|
|
return TRUE;
|
|
break;
|
|
case 3:
|
|
if (strstr(csinfo[i].fname, (char *)dbpath)
|
|
&& ((!ppath && !csinfo[i].ppath)
|
|
|| (ppath
|
|
&& csinfo[i].ppath
|
|
&& strstr(csinfo[i].ppath, (char *)ppath))))
|
|
return TRUE;
|
|
break;
|
|
case 4:
|
|
if ((strcmp(csinfo[i].fname, (char *)dbpath) == 0)
|
|
&& ((!ppath && !csinfo[i].ppath)
|
|
|| (ppath
|
|
&& csinfo[i].ppath
|
|
&& (strcmp(csinfo[i].ppath, (char *)ppath) == 0))))
|
|
return TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
} /* cs_connection */
|
|
|
|
|
|
/*
|
|
* PRIVATE functions
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* PRIVATE: cs_add
|
|
*
|
|
* add cscope database or a directory name (to look for cscope.out)
|
|
* to the cscope connection list
|
|
*
|
|
* MAXPATHL 256
|
|
*/
|
|
static int cs_add(exarg_T *eap)
|
|
{
|
|
char *fname, *ppath, *flags = NULL;
|
|
|
|
if ((fname = strtok((char *)NULL, (const char *)" ")) == NULL) {
|
|
cs_usage_msg(Add);
|
|
return CSCOPE_FAILURE;
|
|
}
|
|
if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL)
|
|
flags = strtok((char *)NULL, (const char *)" ");
|
|
|
|
return cs_add_common(fname, ppath, flags);
|
|
}
|
|
|
|
static void cs_stat_emsg(char *fname)
|
|
{
|
|
char *stat_emsg = _("E563: stat(%s) error: %d");
|
|
char *buf = xmalloc(strlen(stat_emsg) + MAXPATHL + 10);
|
|
|
|
(void)sprintf(buf, stat_emsg, fname, errno);
|
|
(void)EMSG(buf);
|
|
free(buf);
|
|
}
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_add_common
|
|
*
|
|
* the common routine to add a new cscope connection. called by
|
|
* cs_add() and cs_reset(). i really don't like to do this, but this
|
|
* routine uses a number of goto statements.
|
|
*/
|
|
static int
|
|
cs_add_common (
|
|
char *arg1, /* filename - may contain environment variables */
|
|
char *arg2, /* prepend path - may contain environment variables */
|
|
char *flags
|
|
)
|
|
{
|
|
char *fname = NULL;
|
|
char *fname2 = NULL;
|
|
char *ppath = NULL;
|
|
int i;
|
|
int len;
|
|
int usedlen = 0;
|
|
char_u *fbuf = NULL;
|
|
|
|
/* get the filename (arg1), expand it, and try to stat it */
|
|
fname = xmalloc(MAXPATHL + 1);
|
|
|
|
expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
|
|
len = (int)STRLEN(fname);
|
|
fbuf = (char_u *)fname;
|
|
(void)modify_fname((char_u *)":p", &usedlen,
|
|
(char_u **)&fname, &fbuf, &len);
|
|
if (fname == NULL)
|
|
goto add_err;
|
|
fname = (char *)vim_strnsave((char_u *)fname, len);
|
|
free(fbuf);
|
|
FileInfo file_info;
|
|
bool file_info_ok = os_fileinfo(fname, &file_info);
|
|
if (!file_info_ok) {
|
|
staterr:
|
|
if (p_csverbose)
|
|
cs_stat_emsg(fname);
|
|
goto add_err;
|
|
}
|
|
|
|
// get the prepend path (arg2), expand it, and see if it exists
|
|
if (arg2 != NULL) {
|
|
ppath = xmalloc(MAXPATHL + 1);
|
|
expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL);
|
|
if (!os_file_exists((char_u *)ppath))
|
|
goto staterr;
|
|
}
|
|
|
|
/* if filename is a directory, append the cscope database name to it */
|
|
if ((file_info.stat.st_mode & S_IFMT) == S_IFDIR) {
|
|
fname2 = (char *)xmalloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2);
|
|
|
|
while (fname[strlen(fname)-1] == '/'
|
|
) {
|
|
fname[strlen(fname)-1] = '\0';
|
|
if (fname[0] == '\0')
|
|
break;
|
|
}
|
|
if (fname[0] == '\0')
|
|
(void)sprintf(fname2, "/%s", CSCOPE_DBFILE);
|
|
else
|
|
(void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE);
|
|
|
|
file_info_ok = os_fileinfo(fname2, &file_info);
|
|
if (!file_info_ok) {
|
|
if (p_csverbose)
|
|
cs_stat_emsg(fname2);
|
|
goto add_err;
|
|
}
|
|
|
|
i = cs_insert_filelist(fname2, ppath, flags, &file_info);
|
|
}
|
|
else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode))
|
|
{
|
|
i = cs_insert_filelist(fname, ppath, flags, &file_info);
|
|
} else {
|
|
if (p_csverbose)
|
|
(void)EMSG2(
|
|
_("E564: %s is not a directory or a valid cscope database"),
|
|
fname);
|
|
goto add_err;
|
|
}
|
|
|
|
if (i != -1) {
|
|
if (cs_create_connection(i) == CSCOPE_FAILURE
|
|
|| cs_read_prompt(i) == CSCOPE_FAILURE) {
|
|
cs_release_csp(i, TRUE);
|
|
goto add_err;
|
|
}
|
|
|
|
if (p_csverbose) {
|
|
msg_clr_eos();
|
|
(void)smsg_attr(hl_attr(HLF_R),
|
|
(char_u *)_("Added cscope database %s"),
|
|
csinfo[i].fname);
|
|
}
|
|
}
|
|
|
|
free(fname);
|
|
free(fname2);
|
|
free(ppath);
|
|
return CSCOPE_SUCCESS;
|
|
|
|
add_err:
|
|
free(fname2);
|
|
free(fname);
|
|
free(ppath);
|
|
return CSCOPE_FAILURE;
|
|
} /* cs_add_common */
|
|
|
|
|
|
static int cs_check_for_connections(void)
|
|
{
|
|
return cs_cnt_connections() > 0;
|
|
} /* cs_check_for_connections */
|
|
|
|
static int cs_check_for_tags(void)
|
|
{
|
|
return p_tags[0] != NUL && curbuf->b_p_tags != NULL;
|
|
} /* cs_check_for_tags */
|
|
|
|
/*
|
|
* PRIVATE: cs_cnt_connections
|
|
*
|
|
* count the number of cscope connections
|
|
*/
|
|
static int cs_cnt_connections(void)
|
|
{
|
|
short i;
|
|
short cnt = 0;
|
|
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname != NULL)
|
|
cnt++;
|
|
}
|
|
return cnt;
|
|
} /* cs_cnt_connections */
|
|
|
|
static void
|
|
cs_reading_emsg (
|
|
int idx /* connection index */
|
|
)
|
|
{
|
|
EMSGN(_("E262: error reading cscope connection %" PRId64), idx);
|
|
}
|
|
|
|
#define CSREAD_BUFSIZE 2048
|
|
/*
|
|
* PRIVATE: cs_cnt_matches
|
|
*
|
|
* count the number of matches for a given cscope connection.
|
|
*/
|
|
static int cs_cnt_matches(int idx)
|
|
{
|
|
char *stok;
|
|
int nlines;
|
|
|
|
char *buf = xmalloc(CSREAD_BUFSIZE);
|
|
for (;; ) {
|
|
if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) {
|
|
if (feof(csinfo[idx].fr_fp))
|
|
errno = EIO;
|
|
|
|
cs_reading_emsg(idx);
|
|
|
|
free(buf);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* If the database is out of date, or there's some other problem,
|
|
* cscope will output error messages before the number-of-lines output.
|
|
* Display/discard any output that doesn't match what we want.
|
|
* Accept "\S*cscope: X lines", also matches "mlcscope".
|
|
*/
|
|
if ((stok = strtok(buf, (const char *)" ")) == NULL)
|
|
continue;
|
|
if (strstr((const char *)stok, "cscope:") == NULL)
|
|
continue;
|
|
|
|
if ((stok = strtok(NULL, (const char *)" ")) == NULL)
|
|
continue;
|
|
nlines = atoi(stok);
|
|
if (nlines < 0) {
|
|
nlines = 0;
|
|
break;
|
|
}
|
|
|
|
if ((stok = strtok(NULL, (const char *)" ")) == NULL)
|
|
continue;
|
|
if (strncmp((const char *)stok, "lines", 5))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
free(buf);
|
|
return nlines;
|
|
} /* cs_cnt_matches */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_create_cmd
|
|
*
|
|
* Creates the actual cscope command query from what the user entered.
|
|
*/
|
|
static char *cs_create_cmd(char *csoption, char *pattern)
|
|
{
|
|
char *cmd;
|
|
short search;
|
|
char *pat;
|
|
|
|
switch (csoption[0]) {
|
|
case '0': case 's':
|
|
search = 0;
|
|
break;
|
|
case '1': case 'g':
|
|
search = 1;
|
|
break;
|
|
case '2': case 'd':
|
|
search = 2;
|
|
break;
|
|
case '3': case 'c':
|
|
search = 3;
|
|
break;
|
|
case '4': case 't':
|
|
search = 4;
|
|
break;
|
|
case '6': case 'e':
|
|
search = 6;
|
|
break;
|
|
case '7': case 'f':
|
|
search = 7;
|
|
break;
|
|
case '8': case 'i':
|
|
search = 8;
|
|
break;
|
|
default:
|
|
(void)EMSG(_("E561: unknown cscope search type"));
|
|
cs_usage_msg(Find);
|
|
return NULL;
|
|
}
|
|
|
|
/* Skip white space before the patter, except for text and pattern search,
|
|
* they may want to use the leading white space. */
|
|
pat = pattern;
|
|
if (search != 4 && search != 6)
|
|
while (vim_iswhite(*pat))
|
|
++pat;
|
|
|
|
cmd = xmalloc(strlen(pat) + 2);
|
|
|
|
(void)sprintf(cmd, "%d%s", search, pat);
|
|
|
|
return cmd;
|
|
} /* cs_create_cmd */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_create_connection
|
|
*
|
|
* This piece of code was taken/adapted from nvi. do we need to add
|
|
* the BSD license notice?
|
|
*/
|
|
static int cs_create_connection(int i)
|
|
{
|
|
#ifdef UNIX
|
|
int to_cs[2], from_cs[2];
|
|
#endif
|
|
int len;
|
|
char *prog, *cmd, *ppath = NULL;
|
|
|
|
#if defined(UNIX)
|
|
/*
|
|
* Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
|
|
* from_cs[0] and writes to to_cs[1].
|
|
*/
|
|
to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1;
|
|
if (pipe(to_cs) < 0 || pipe(from_cs) < 0) {
|
|
(void)EMSG(_("E566: Could not create cscope pipes"));
|
|
err_closing:
|
|
if (to_cs[0] != -1)
|
|
(void)close(to_cs[0]);
|
|
if (to_cs[1] != -1)
|
|
(void)close(to_cs[1]);
|
|
if (from_cs[0] != -1)
|
|
(void)close(from_cs[0]);
|
|
if (from_cs[1] != -1)
|
|
(void)close(from_cs[1]);
|
|
return CSCOPE_FAILURE;
|
|
}
|
|
|
|
switch (csinfo[i].pid = fork()) {
|
|
case -1:
|
|
(void)EMSG(_("E622: Could not fork for cscope"));
|
|
goto err_closing;
|
|
case 0: /* child: run cscope. */
|
|
if (dup2(to_cs[0], STDIN_FILENO) == -1)
|
|
PERROR("cs_create_connection 1");
|
|
if (dup2(from_cs[1], STDOUT_FILENO) == -1)
|
|
PERROR("cs_create_connection 2");
|
|
if (dup2(from_cs[1], STDERR_FILENO) == -1)
|
|
PERROR("cs_create_connection 3");
|
|
|
|
/* close unused */
|
|
(void)close(to_cs[1]);
|
|
(void)close(from_cs[0]);
|
|
#else
|
|
/* WIN32 */
|
|
/* Create pipes to communicate with cscope */
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = TRUE;
|
|
sa.lpSecurityDescriptor = NULL;
|
|
|
|
if (!(pipe_stdin = CreatePipe(&stdin_rd, &stdin_wr, &sa, 0))
|
|
|| !(pipe_stdout = CreatePipe(&stdout_rd, &stdout_wr, &sa, 0))) {
|
|
(void)EMSG(_("E566: Could not create cscope pipes"));
|
|
err_closing:
|
|
if (pipe_stdin) {
|
|
CloseHandle(stdin_rd);
|
|
CloseHandle(stdin_wr);
|
|
}
|
|
if (pipe_stdout) {
|
|
CloseHandle(stdout_rd);
|
|
CloseHandle(stdout_wr);
|
|
}
|
|
return CSCOPE_FAILURE;
|
|
}
|
|
#endif
|
|
/* expand the cscope exec for env var's */
|
|
prog = xmalloc(MAXPATHL + 1);
|
|
expand_env((char_u *)p_csprg, (char_u *)prog, MAXPATHL);
|
|
|
|
/* alloc space to hold the cscope command */
|
|
len = (int)(strlen(prog) + strlen(csinfo[i].fname) + 32);
|
|
if (csinfo[i].ppath) {
|
|
/* expand the prepend path for env var's */
|
|
ppath = xmalloc(MAXPATHL + 1);
|
|
expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
|
|
|
|
len += (int)strlen(ppath);
|
|
}
|
|
|
|
if (csinfo[i].flags)
|
|
len += (int)strlen(csinfo[i].flags);
|
|
|
|
cmd = xmalloc(len);
|
|
|
|
/* run the cscope command; is there execl for non-unix systems? */
|
|
#if defined(UNIX)
|
|
(void)sprintf(cmd, "exec %s -dl -f %s", prog, csinfo[i].fname);
|
|
#else
|
|
/* WIN32 */
|
|
(void)sprintf(cmd, "%s -dl -f %s", prog, csinfo[i].fname);
|
|
#endif
|
|
if (csinfo[i].ppath != NULL) {
|
|
(void)strcat(cmd, " -P");
|
|
(void)strcat(cmd, csinfo[i].ppath);
|
|
}
|
|
if (csinfo[i].flags != NULL) {
|
|
(void)strcat(cmd, " ");
|
|
(void)strcat(cmd, csinfo[i].flags);
|
|
}
|
|
# ifdef UNIX
|
|
/* on Win32 we still need prog */
|
|
free(prog);
|
|
# endif
|
|
free(ppath);
|
|
|
|
#if defined(UNIX)
|
|
# if defined(HAVE_SETSID) || defined(HAVE_SETPGID)
|
|
/* Change our process group to avoid cscope receiving SIGWINCH. */
|
|
# if defined(HAVE_SETSID)
|
|
(void)setsid();
|
|
# else
|
|
if (setpgid(0, 0) == -1)
|
|
PERROR(_("cs_create_connection setpgid failed"));
|
|
# endif
|
|
# endif
|
|
if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1)
|
|
PERROR(_("cs_create_connection exec failed"));
|
|
|
|
exit(127);
|
|
/* NOTREACHED */
|
|
default: /* parent. */
|
|
/*
|
|
* Save the file descriptors for later duplication, and
|
|
* reopen as streams.
|
|
*/
|
|
if ((csinfo[i].to_fp = fdopen(to_cs[1], "w")) == NULL)
|
|
PERROR(_("cs_create_connection: fdopen for to_fp failed"));
|
|
if ((csinfo[i].fr_fp = fdopen(from_cs[0], "r")) == NULL)
|
|
PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
|
|
|
|
/* close unused */
|
|
(void)close(to_cs[0]);
|
|
(void)close(from_cs[1]);
|
|
|
|
break;
|
|
}
|
|
|
|
#else
|
|
/* WIN32 */
|
|
/* Create a new process to run cscope and use pipes to talk with it */
|
|
GetStartupInfo(&si);
|
|
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE; /* Hide child application window */
|
|
si.hStdOutput = stdout_wr;
|
|
si.hStdError = stdout_wr;
|
|
si.hStdInput = stdin_rd;
|
|
created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
|
|
NULL, NULL, &si, &pi);
|
|
free(prog);
|
|
free(cmd);
|
|
|
|
if (!created) {
|
|
PERROR(_("cs_create_connection exec failed"));
|
|
(void)EMSG(_("E623: Could not spawn cscope process"));
|
|
goto err_closing;
|
|
}
|
|
/* else */
|
|
csinfo[i].pid = pi.dwProcessId;
|
|
csinfo[i].hProc = pi.hProcess;
|
|
CloseHandle(pi.hThread);
|
|
|
|
/* TODO - tidy up after failure to create files on pipe handles. */
|
|
if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdin_wr,
|
|
_O_TEXT|_O_APPEND)) < 0)
|
|
|| ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL))
|
|
PERROR(_("cs_create_connection: fdopen for to_fp failed"));
|
|
if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdout_rd,
|
|
_O_TEXT|_O_RDONLY)) < 0)
|
|
|| ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL))
|
|
PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
|
|
|
|
/* Close handles for file descriptors inherited by the cscope process */
|
|
CloseHandle(stdin_rd);
|
|
CloseHandle(stdout_wr);
|
|
|
|
#endif /* !UNIX */
|
|
|
|
return CSCOPE_SUCCESS;
|
|
} /* cs_create_connection */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_find
|
|
*
|
|
* query cscope using command line interface. parse the output and use tselect
|
|
* to allow choices. like Nvi, creates a pipe to send to/from query/cscope.
|
|
*
|
|
* returns TRUE if we jump to a tag or abort, FALSE if not.
|
|
*/
|
|
static int cs_find(exarg_T *eap)
|
|
{
|
|
char *opt, *pat;
|
|
int i;
|
|
|
|
if (cs_check_for_connections() == FALSE) {
|
|
(void)EMSG(_("E567: no cscope connections"));
|
|
return FALSE;
|
|
}
|
|
|
|
if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL) {
|
|
cs_usage_msg(Find);
|
|
return FALSE;
|
|
}
|
|
|
|
pat = opt + strlen(opt) + 1;
|
|
if (pat >= (char *)eap->arg + eap_arg_len) {
|
|
cs_usage_msg(Find);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Let's replace the NULs written by strtok() with spaces - we need the
|
|
* spaces to correctly display the quickfix/location list window's title.
|
|
*/
|
|
for (i = 0; i < eap_arg_len; ++i)
|
|
if (NUL == eap->arg[i])
|
|
eap->arg[i] = ' ';
|
|
|
|
return cs_find_common(opt, pat, eap->forceit, TRUE,
|
|
eap->cmdidx == CMD_lcscope, *eap->cmdlinep);
|
|
} /* cs_find */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_find_common
|
|
*
|
|
* common code for cscope find, shared by cs_find() and do_cstag()
|
|
*/
|
|
static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, char_u *cmdline)
|
|
{
|
|
int i;
|
|
char *cmd;
|
|
int *nummatches;
|
|
int totmatches;
|
|
char cmdletter;
|
|
char *qfpos;
|
|
|
|
/* get cmd letter */
|
|
switch (opt[0]) {
|
|
case '0':
|
|
cmdletter = 's';
|
|
break;
|
|
case '1':
|
|
cmdletter = 'g';
|
|
break;
|
|
case '2':
|
|
cmdletter = 'd';
|
|
break;
|
|
case '3':
|
|
cmdletter = 'c';
|
|
break;
|
|
case '4':
|
|
cmdletter = 't';
|
|
break;
|
|
case '6':
|
|
cmdletter = 'e';
|
|
break;
|
|
case '7':
|
|
cmdletter = 'f';
|
|
break;
|
|
case '8':
|
|
cmdletter = 'i';
|
|
break;
|
|
default:
|
|
cmdletter = opt[0];
|
|
}
|
|
|
|
qfpos = (char *)vim_strchr(p_csqf, cmdletter);
|
|
if (qfpos != NULL) {
|
|
qfpos++;
|
|
/* next symbol must be + or - */
|
|
if (strchr(CSQF_FLAGS, *qfpos) == NULL) {
|
|
char *nf = _("E469: invalid cscopequickfix flag %c for %c");
|
|
/* strlen will be enough because we use chars */
|
|
char *buf = xmalloc(strlen(nf));
|
|
|
|
sprintf(buf, nf, *qfpos, *(qfpos-1));
|
|
(void)EMSG(buf);
|
|
free(buf);
|
|
return FALSE;
|
|
}
|
|
|
|
if (*qfpos != '0') {
|
|
apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)"cscope",
|
|
curbuf->b_fname, TRUE, curbuf);
|
|
if (did_throw || force_abort)
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* create the actual command to send to cscope */
|
|
cmd = cs_create_cmd(opt, pat);
|
|
if (cmd == NULL)
|
|
return FALSE;
|
|
|
|
nummatches = xmalloc(sizeof(int) * csinfo_size);
|
|
|
|
/* Send query to all open connections, then count the total number
|
|
* of matches so we can alloc all in one swell foop. */
|
|
for (i = 0; i < csinfo_size; i++)
|
|
nummatches[i] = 0;
|
|
totmatches = 0;
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL)
|
|
continue;
|
|
|
|
/* send cmd to cscope */
|
|
(void)fprintf(csinfo[i].to_fp, "%s\n", cmd);
|
|
(void)fflush(csinfo[i].to_fp);
|
|
|
|
nummatches[i] = cs_cnt_matches(i);
|
|
|
|
if (nummatches[i] > -1)
|
|
totmatches += nummatches[i];
|
|
|
|
if (nummatches[i] == 0)
|
|
(void)cs_read_prompt(i);
|
|
}
|
|
free(cmd);
|
|
|
|
if (totmatches == 0) {
|
|
char *nf = _("E259: no matches found for cscope query %s of %s");
|
|
char *buf;
|
|
|
|
if (!verbose) {
|
|
free(nummatches);
|
|
return FALSE;
|
|
}
|
|
|
|
buf = xmalloc(strlen(opt) + strlen(pat) + strlen(nf));
|
|
sprintf(buf, nf, opt, pat);
|
|
(void)EMSG(buf);
|
|
free(buf);
|
|
free(nummatches);
|
|
return FALSE;
|
|
}
|
|
|
|
if (qfpos != NULL && *qfpos != '0' && totmatches > 0) {
|
|
/* fill error list */
|
|
FILE *f;
|
|
char_u *tmp = vim_tempname();
|
|
qf_info_T *qi = NULL;
|
|
win_T *wp = NULL;
|
|
|
|
f = mch_fopen((char *)tmp, "w");
|
|
if (f == NULL)
|
|
EMSG2(_(e_notopen), tmp);
|
|
else {
|
|
cs_file_results(f, nummatches);
|
|
fclose(f);
|
|
if (use_ll) /* Use location list */
|
|
wp = curwin;
|
|
/* '-' starts a new error list */
|
|
if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
|
|
*qfpos == '-', cmdline) > 0) {
|
|
if (postponed_split != 0) {
|
|
win_split(postponed_split > 0 ? postponed_split : 0,
|
|
postponed_split_flags);
|
|
RESET_BINDING(curwin);
|
|
postponed_split = 0;
|
|
}
|
|
|
|
apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)"cscope",
|
|
curbuf->b_fname, TRUE, curbuf);
|
|
if (use_ll)
|
|
/*
|
|
* In the location list window, use the displayed location
|
|
* list. Otherwise, use the location list for the window.
|
|
*/
|
|
qi = (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
|
|
? wp->w_llist_ref : wp->w_llist;
|
|
qf_jump(qi, 0, 0, forceit);
|
|
}
|
|
}
|
|
os_remove((char *)tmp);
|
|
free(tmp);
|
|
free(nummatches);
|
|
return TRUE;
|
|
} else {
|
|
char **matches = NULL, **contexts = NULL;
|
|
int matched = 0;
|
|
|
|
/* read output */
|
|
cs_fill_results((char *)pat, totmatches, nummatches, &matches,
|
|
&contexts, &matched);
|
|
free(nummatches);
|
|
if (matches == NULL)
|
|
return FALSE;
|
|
|
|
(void)cs_manage_matches(matches, contexts, matched, Store);
|
|
|
|
return do_tag((char_u *)pat, DT_CSCOPE, 0, forceit, verbose);
|
|
}
|
|
|
|
} /* cs_find_common */
|
|
|
|
/*
|
|
* PRIVATE: cs_help
|
|
*
|
|
* print help
|
|
*/
|
|
static int cs_help(exarg_T *eap)
|
|
{
|
|
cscmd_T *cmdp = cs_cmds;
|
|
|
|
(void)MSG_PUTS(_("cscope commands:\n"));
|
|
while (cmdp->name != NULL) {
|
|
char *help = _(cmdp->help);
|
|
int space_cnt = 30 - vim_strsize((char_u *)help);
|
|
|
|
/* Use %*s rather than %30s to ensure proper alignment in utf-8 */
|
|
if (space_cnt < 0)
|
|
space_cnt = 0;
|
|
(void)smsg((char_u *)_("%-5s: %s%*s (Usage: %s)"),
|
|
cmdp->name,
|
|
help, space_cnt, " ",
|
|
cmdp->usage);
|
|
if (strcmp(cmdp->name, "find") == 0)
|
|
MSG_PUTS(_("\n"
|
|
" c: Find functions calling this function\n"
|
|
" d: Find functions called by this function\n"
|
|
" e: Find this egrep pattern\n"
|
|
" f: Find this file\n"
|
|
" g: Find this definition\n"
|
|
" i: Find files #including this file\n"
|
|
" s: Find this C symbol\n"
|
|
" t: Find this text string\n"));
|
|
|
|
cmdp++;
|
|
}
|
|
|
|
wait_return(TRUE);
|
|
return 0;
|
|
} /* cs_help */
|
|
|
|
|
|
static void clear_csinfo(int i)
|
|
{
|
|
csinfo[i].fname = NULL;
|
|
csinfo[i].ppath = NULL;
|
|
csinfo[i].flags = NULL;
|
|
csinfo[i].file_id = FILE_ID_EMPTY;
|
|
csinfo[i].pid = 0;
|
|
csinfo[i].fr_fp = NULL;
|
|
csinfo[i].to_fp = NULL;
|
|
}
|
|
|
|
#ifndef UNIX
|
|
static char *GetWin32Error(void)
|
|
{
|
|
char *msg = NULL;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL, GetLastError(), 0, (LPSTR)&msg, 0, NULL);
|
|
if (msg != NULL) {
|
|
/* remove trailing \r\n */
|
|
char *pcrlf = strstr(msg, "\r\n");
|
|
if (pcrlf != NULL)
|
|
*pcrlf = '\0';
|
|
}
|
|
return msg;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* PRIVATE: cs_insert_filelist
|
|
*
|
|
* insert a new cscope database filename into the filelist
|
|
*/
|
|
static int cs_insert_filelist(char *fname, char *ppath, char *flags,
|
|
FileInfo *file_info)
|
|
{
|
|
short i, j;
|
|
|
|
i = -1; /* can be set to the index of an empty item in csinfo */
|
|
for (j = 0; j < csinfo_size; j++) {
|
|
if (csinfo[j].fname != NULL
|
|
&& os_fileid_equal_fileinfo(&(csinfo[j].file_id), file_info)) {
|
|
if (p_csverbose)
|
|
(void)EMSG(_("E568: duplicate cscope database not added"));
|
|
return -1;
|
|
}
|
|
|
|
if (csinfo[j].fname == NULL && i == -1)
|
|
i = j; /* remember first empty entry */
|
|
}
|
|
|
|
if (i == -1) {
|
|
i = csinfo_size;
|
|
if (csinfo_size == 0) {
|
|
/* First time allocation: allocate only 1 connection. It should
|
|
* be enough for most users. If more is needed, csinfo will be
|
|
* reallocated. */
|
|
csinfo_size = 1;
|
|
csinfo = xcalloc(1, sizeof(csinfo_T));
|
|
} else {
|
|
/* Reallocate space for more connections. */
|
|
csinfo_size *= 2;
|
|
csinfo = xrealloc(csinfo, sizeof(csinfo_T)*csinfo_size);
|
|
}
|
|
for (j = csinfo_size/2; j < csinfo_size; j++)
|
|
clear_csinfo(j);
|
|
}
|
|
|
|
csinfo[i].fname = xmalloc(strlen(fname) + 1);
|
|
|
|
(void)strcpy(csinfo[i].fname, (const char *)fname);
|
|
|
|
if (ppath != NULL) {
|
|
csinfo[i].ppath = xmalloc(strlen(ppath) + 1);
|
|
(void)strcpy(csinfo[i].ppath, (const char *)ppath);
|
|
} else
|
|
csinfo[i].ppath = NULL;
|
|
|
|
if (flags != NULL) {
|
|
csinfo[i].flags = xmalloc(strlen(flags) + 1);
|
|
(void)strcpy(csinfo[i].flags, (const char *)flags);
|
|
} else
|
|
csinfo[i].flags = NULL;
|
|
|
|
os_fileinfo_id(file_info, &(csinfo[i].file_id));
|
|
return i;
|
|
} /* cs_insert_filelist */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_lookup_cmd
|
|
*
|
|
* find cscope command in command table
|
|
*/
|
|
static cscmd_T * cs_lookup_cmd(exarg_T *eap)
|
|
{
|
|
cscmd_T *cmdp;
|
|
char *stok;
|
|
size_t len;
|
|
|
|
if (eap->arg == NULL)
|
|
return NULL;
|
|
|
|
/* Store length of eap->arg before it gets modified by strtok(). */
|
|
eap_arg_len = (int)STRLEN(eap->arg);
|
|
|
|
if ((stok = strtok((char *)(eap->arg), (const char *)" ")) == NULL)
|
|
return NULL;
|
|
|
|
len = strlen(stok);
|
|
for (cmdp = cs_cmds; cmdp->name != NULL; ++cmdp) {
|
|
if (strncmp((const char *)(stok), cmdp->name, len) == 0)
|
|
return cmdp;
|
|
}
|
|
return NULL;
|
|
} /* cs_lookup_cmd */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_kill
|
|
*
|
|
* nuke em
|
|
*/
|
|
static int cs_kill(exarg_T *eap)
|
|
{
|
|
char *stok;
|
|
short i;
|
|
|
|
if ((stok = strtok((char *)NULL, (const char *)" ")) == NULL) {
|
|
cs_usage_msg(Kill);
|
|
return CSCOPE_FAILURE;
|
|
}
|
|
|
|
/* only single digit positive and negative integers are allowed */
|
|
if ((strlen(stok) < 2 && VIM_ISDIGIT((int)(stok[0])))
|
|
|| (strlen(stok) < 3 && stok[0] == '-'
|
|
&& VIM_ISDIGIT((int)(stok[1]))))
|
|
i = atoi(stok);
|
|
else {
|
|
/* It must be part of a name. We will try to find a match
|
|
* within all the names in the csinfo data structure
|
|
*/
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((i != -1) && (i >= csinfo_size || i < -1 || csinfo[i].fname == NULL)) {
|
|
if (p_csverbose)
|
|
(void)EMSG2(_("E261: cscope connection %s not found"), stok);
|
|
} else {
|
|
if (i == -1) {
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname)
|
|
cs_kill_execute(i, csinfo[i].fname);
|
|
}
|
|
} else
|
|
cs_kill_execute(i, stok);
|
|
}
|
|
|
|
return 0;
|
|
} /* cs_kill */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_kill_execute
|
|
*
|
|
* Actually kills a specific cscope connection.
|
|
*/
|
|
static void
|
|
cs_kill_execute (
|
|
int i, /* cscope table index */
|
|
char *cname /* cscope database name */
|
|
)
|
|
{
|
|
if (p_csverbose) {
|
|
msg_clr_eos();
|
|
(void)smsg_attr(hl_attr(HLF_R) | MSG_HIST,
|
|
(char_u *)_("cscope connection %s closed"), cname);
|
|
}
|
|
cs_release_csp(i, TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_make_vim_style_matches
|
|
*
|
|
* convert the cscope output into a ctags style entry (as might be found
|
|
* in a ctags tags file). there's one catch though: cscope doesn't tell you
|
|
* the type of the tag you are looking for. for example, in Darren Hiebert's
|
|
* ctags (the one that comes with vim), #define's use a line number to find the
|
|
* tag in a file while function definitions use a regexp search pattern.
|
|
*
|
|
* i'm going to always use the line number because cscope does something
|
|
* quirky (and probably other things i don't know about):
|
|
*
|
|
* if you have "# define" in your source file, which is
|
|
* perfectly legal, cscope thinks you have "#define". this
|
|
* will result in a failed regexp search. :(
|
|
*
|
|
* besides, even if this particular case didn't happen, the search pattern
|
|
* would still have to be modified to escape all the special regular expression
|
|
* characters to comply with ctags formatting.
|
|
*/
|
|
static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, char *tagstr)
|
|
{
|
|
/* vim style is ctags:
|
|
*
|
|
* <tagstr>\t<filename>\t<linenum_or_search>"\t<extra>
|
|
*
|
|
* but as mentioned above, we'll always use the line number and
|
|
* put the search pattern (if one exists) as "extra"
|
|
*
|
|
* buf is used as part of vim's method of handling tags, and
|
|
* (i think) vim frees it when you pop your tags and get replaced
|
|
* by new ones on the tag stack.
|
|
*/
|
|
char *buf;
|
|
size_t amt;
|
|
|
|
if (search != NULL) {
|
|
amt = strlen(fname) + strlen(slno) + strlen(tagstr) + strlen(search) + 6;
|
|
buf = xmalloc(amt);
|
|
|
|
(void)sprintf(buf, "%s\t%s\t%s;\"\t%s", tagstr, fname, slno, search);
|
|
} else {
|
|
amt = strlen(fname) + strlen(slno) + strlen(tagstr) + 5;
|
|
buf = xmalloc(amt);
|
|
|
|
(void)sprintf(buf, "%s\t%s\t%s;\"", tagstr, fname, slno);
|
|
}
|
|
|
|
return buf;
|
|
} /* cs_make_vim_style_matches */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_manage_matches
|
|
*
|
|
* this is kind of hokey, but i don't see an easy way round this..
|
|
*
|
|
* Store: keep a ptr to the (malloc'd) memory of matches originally
|
|
* generated from cs_find(). the matches are originally lines directly
|
|
* from cscope output, but transformed to look like something out of a
|
|
* ctags. see cs_make_vim_style_matches for more details.
|
|
*
|
|
* Get: used only from cs_fgets(), this simulates a vim_fgets() to return
|
|
* the next line from the cscope output. it basically keeps track of which
|
|
* lines have been "used" and returns the next one.
|
|
*
|
|
* Free: frees up everything and resets
|
|
*
|
|
* Print: prints the tags
|
|
*/
|
|
static char *cs_manage_matches(char **matches, char **contexts, int totmatches, mcmd_e cmd)
|
|
{
|
|
static char **mp = NULL;
|
|
static char **cp = NULL;
|
|
static int cnt = -1;
|
|
static int next = -1;
|
|
char *p = NULL;
|
|
|
|
switch (cmd) {
|
|
case Store:
|
|
assert(matches != NULL);
|
|
assert(totmatches > 0);
|
|
if (mp != NULL || cp != NULL)
|
|
(void)cs_manage_matches(NULL, NULL, -1, Free);
|
|
mp = matches;
|
|
cp = contexts;
|
|
cnt = totmatches;
|
|
next = 0;
|
|
break;
|
|
case Get:
|
|
if (next >= cnt)
|
|
return NULL;
|
|
|
|
p = mp[next];
|
|
next++;
|
|
break;
|
|
case Free:
|
|
if (mp != NULL) {
|
|
if (cnt > 0)
|
|
while (cnt--) {
|
|
free(mp[cnt]);
|
|
if (cp != NULL)
|
|
free(cp[cnt]);
|
|
}
|
|
free(mp);
|
|
free(cp);
|
|
}
|
|
mp = NULL;
|
|
cp = NULL;
|
|
cnt = 0;
|
|
next = 0;
|
|
break;
|
|
case Print:
|
|
cs_print_tags_priv(mp, cp, cnt);
|
|
break;
|
|
default: /* should not reach here */
|
|
(void)EMSG(_("E570: fatal error in cs_manage_matches"));
|
|
return NULL;
|
|
}
|
|
|
|
return p;
|
|
} /* cs_manage_matches */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_parse_results
|
|
*
|
|
* parse cscope output
|
|
*/
|
|
static char *cs_parse_results(int cnumber, char *buf, int bufsize, char **context, char **linenumber, char **search)
|
|
{
|
|
int ch;
|
|
char *p;
|
|
char *name;
|
|
|
|
if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL) {
|
|
if (feof(csinfo[cnumber].fr_fp))
|
|
errno = EIO;
|
|
|
|
cs_reading_emsg(cnumber);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* If the line's too long for the buffer, discard it. */
|
|
if ((p = strchr(buf, '\n')) == NULL) {
|
|
while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n')
|
|
;
|
|
return NULL;
|
|
}
|
|
*p = '\0';
|
|
|
|
/*
|
|
* cscope output is in the following format:
|
|
*
|
|
* <filename> <context> <line number> <pattern>
|
|
*/
|
|
if ((name = strtok((char *)buf, (const char *)" ")) == NULL)
|
|
return NULL;
|
|
if ((*context = strtok(NULL, (const char *)" ")) == NULL)
|
|
return NULL;
|
|
if ((*linenumber = strtok(NULL, (const char *)" ")) == NULL)
|
|
return NULL;
|
|
*search = *linenumber + strlen(*linenumber) + 1; /* +1 to skip \0 */
|
|
|
|
/* --- nvi ---
|
|
* If the file is older than the cscope database, that is,
|
|
* the database was built since the file was last modified,
|
|
* or there wasn't a search string, use the line number.
|
|
*/
|
|
if (strcmp(*search, "<unknown>") == 0)
|
|
*search = NULL;
|
|
|
|
name = cs_resolve_file(cnumber, name);
|
|
return name;
|
|
}
|
|
|
|
/*
|
|
* PRIVATE: cs_file_results
|
|
*
|
|
* write cscope find results to file
|
|
*/
|
|
static void cs_file_results(FILE *f, int *nummatches_a)
|
|
{
|
|
int i, j;
|
|
char *search, *slno;
|
|
char *fullname;
|
|
char *cntx;
|
|
char *context;
|
|
|
|
char *buf = xmalloc(CSREAD_BUFSIZE);
|
|
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (nummatches_a[i] < 1)
|
|
continue;
|
|
|
|
for (j = 0; j < nummatches_a[i]; j++) {
|
|
if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
|
|
&slno, &search)) == NULL)
|
|
continue;
|
|
|
|
context = xmalloc(strlen(cntx) + 5);
|
|
|
|
if (strcmp(cntx, "<global>")==0)
|
|
strcpy(context, "<<global>>");
|
|
else
|
|
sprintf(context, "<<%s>>", cntx);
|
|
|
|
if (search == NULL)
|
|
fprintf(f, "%s\t%s\t%s\n", fullname, slno, context);
|
|
else
|
|
fprintf(f, "%s\t%s\t%s %s\n", fullname, slno, context, search);
|
|
|
|
free(context);
|
|
free(fullname);
|
|
} /* for all matches */
|
|
|
|
(void)cs_read_prompt(i);
|
|
|
|
} /* for all cscope connections */
|
|
free(buf);
|
|
}
|
|
|
|
/*
|
|
* PRIVATE: cs_fill_results
|
|
*
|
|
* get parsed cscope output and calls cs_make_vim_style_matches to convert
|
|
* into ctags format
|
|
* When there are no matches sets "*matches_p" to NULL.
|
|
*/
|
|
static void cs_fill_results(char *tagstr, int totmatches, int *nummatches_a, char ***matches_p, char ***cntxts_p, int *matched)
|
|
{
|
|
int i, j;
|
|
char *buf;
|
|
char *search, *slno;
|
|
int totsofar = 0;
|
|
char **matches = NULL;
|
|
char **cntxts = NULL;
|
|
char *fullname;
|
|
char *cntx;
|
|
|
|
assert(totmatches > 0);
|
|
|
|
buf = xmalloc(CSREAD_BUFSIZE);
|
|
matches = xmalloc(sizeof(char *) * totmatches);
|
|
cntxts = xmalloc(sizeof(char *) * totmatches);
|
|
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (nummatches_a[i] < 1)
|
|
continue;
|
|
|
|
for (j = 0; j < nummatches_a[i]; j++) {
|
|
if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
|
|
&slno, &search)) == NULL)
|
|
continue;
|
|
|
|
matches[totsofar] = cs_make_vim_style_matches(fullname, slno, search,
|
|
tagstr);
|
|
|
|
free(fullname);
|
|
|
|
if (strcmp(cntx, "<global>") == 0)
|
|
cntxts[totsofar] = NULL;
|
|
else {
|
|
cntxts[totsofar] = xstrdup(cntx);
|
|
}
|
|
|
|
totsofar++;
|
|
|
|
} /* for all matches */
|
|
|
|
(void)cs_read_prompt(i);
|
|
|
|
} /* for all cscope connections */
|
|
|
|
if (totsofar == 0) {
|
|
/* No matches, free the arrays and return NULL in "*matches_p". */
|
|
free(matches);
|
|
matches = NULL;
|
|
free(cntxts);
|
|
cntxts = NULL;
|
|
}
|
|
*matched = totsofar;
|
|
*matches_p = matches;
|
|
*cntxts_p = cntxts;
|
|
|
|
free(buf);
|
|
} /* cs_fill_results */
|
|
|
|
|
|
/* get the requested path components */
|
|
static char *cs_pathcomponents(char *path)
|
|
{
|
|
int i;
|
|
char *s;
|
|
|
|
if (p_cspc == 0)
|
|
return path;
|
|
|
|
s = path + strlen(path) - 1;
|
|
for (i = 0; i < p_cspc; ++i)
|
|
while (s > path && *--s != '/'
|
|
)
|
|
;
|
|
if ((s > path && *s == '/')
|
|
)
|
|
++s;
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* PRIVATE: cs_print_tags_priv
|
|
*
|
|
* called from cs_manage_matches()
|
|
*/
|
|
static void cs_print_tags_priv(char **matches, char **cntxts, int num_matches)
|
|
{
|
|
char *ptag;
|
|
char *fname, *lno, *extra, *tbuf;
|
|
int i, idx, num;
|
|
char *globalcntx = "GLOBAL";
|
|
char *context;
|
|
char *cstag_msg = _("Cscope tag: %s");
|
|
|
|
assert (num_matches > 0);
|
|
|
|
tbuf = xmalloc(strlen(matches[0]) + 1);
|
|
|
|
strcpy(tbuf, matches[0]);
|
|
ptag = strtok(tbuf, "\t");
|
|
|
|
size_t newsize = strlen(cstag_msg) + strlen(ptag);
|
|
char *buf = xmalloc(newsize);
|
|
size_t bufsize = newsize; // Track available bufsize
|
|
(void)sprintf(buf, cstag_msg, ptag);
|
|
MSG_PUTS_ATTR(buf, hl_attr(HLF_T));
|
|
|
|
free(tbuf);
|
|
|
|
MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */
|
|
msg_advance(msg_col + 2);
|
|
MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T));
|
|
|
|
num = 1;
|
|
for (i = 0; i < num_matches; i++) {
|
|
idx = i;
|
|
|
|
/* if we really wanted to, we could avoid this malloc and strcpy
|
|
* by parsing matches[i] on the fly and placing stuff into buf
|
|
* directly, but that's too much of a hassle
|
|
*/
|
|
tbuf = xmalloc(strlen(matches[idx]) + 1);
|
|
(void)strcpy(tbuf, matches[idx]);
|
|
|
|
if (strtok(tbuf, (const char *)"\t") == NULL)
|
|
continue;
|
|
if ((fname = strtok(NULL, (const char *)"\t")) == NULL)
|
|
continue;
|
|
if ((lno = strtok(NULL, (const char *)"\t")) == NULL)
|
|
continue;
|
|
extra = strtok(NULL, (const char *)"\t");
|
|
|
|
lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */
|
|
|
|
const char *csfmt_str = "%4d %6s ";
|
|
/* hopefully 'num' (num of matches) will be less than 10^16 */
|
|
newsize = strlen(csfmt_str) + 16 + strlen(lno);
|
|
if (bufsize < newsize) {
|
|
buf = xrealloc(buf, newsize);
|
|
bufsize = newsize;
|
|
}
|
|
(void)sprintf(buf, csfmt_str, num, lno);
|
|
MSG_PUTS_ATTR(buf, hl_attr(HLF_CM));
|
|
MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM));
|
|
|
|
/* compute the required space for the context */
|
|
if (cntxts[idx] != NULL)
|
|
context = cntxts[idx];
|
|
else
|
|
context = globalcntx;
|
|
|
|
const char *cntxformat = " <<%s>>";
|
|
// '%s' won't appear in result string, so:
|
|
// newsize = len(cntxformat) - 2 + len(context) + 1 (for NUL).
|
|
newsize = strlen(context) + strlen(cntxformat) - 1;
|
|
|
|
if (bufsize < newsize) {
|
|
buf = xrealloc(buf, newsize);
|
|
bufsize = newsize;
|
|
}
|
|
(void)sprintf(buf, cntxformat, context);
|
|
|
|
/* print the context only if it fits on the same line */
|
|
if (msg_col + (int)strlen(buf) >= (int)Columns)
|
|
msg_putchar('\n');
|
|
msg_advance(12);
|
|
MSG_PUTS_LONG(buf);
|
|
msg_putchar('\n');
|
|
if (extra != NULL) {
|
|
msg_advance(13);
|
|
MSG_PUTS_LONG(extra);
|
|
}
|
|
|
|
free(tbuf); /* only after printing extra due to strtok use */
|
|
|
|
if (msg_col)
|
|
msg_putchar('\n');
|
|
|
|
os_breakcheck();
|
|
if (got_int) {
|
|
got_int = FALSE; /* don't print any more matches */
|
|
break;
|
|
}
|
|
|
|
num++;
|
|
} /* for all matches */
|
|
|
|
free(buf);
|
|
} /* cs_print_tags_priv */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_read_prompt
|
|
*
|
|
* read a cscope prompt (basically, skip over the ">> ")
|
|
*/
|
|
static int cs_read_prompt(int i)
|
|
{
|
|
int ch;
|
|
char *buf = NULL; /* buffer for possible error message from cscope */
|
|
int bufpos = 0;
|
|
char *cs_emsg;
|
|
int maxlen;
|
|
static char *eprompt = "Press the RETURN key to continue:";
|
|
int epromptlen = (int)strlen(eprompt);
|
|
int n;
|
|
|
|
cs_emsg = _("E609: Cscope error: %s");
|
|
/* compute maximum allowed len for Cscope error message */
|
|
maxlen = (int)(IOSIZE - strlen(cs_emsg));
|
|
|
|
for (;; ) {
|
|
while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
|
|
/* if there is room and char is printable */
|
|
if (bufpos < maxlen - 1 && vim_isprintc(ch)) {
|
|
// lazy buffer allocation
|
|
if (buf == NULL) {
|
|
buf = xmalloc(maxlen);
|
|
}
|
|
{
|
|
/* append character to the message */
|
|
buf[bufpos++] = ch;
|
|
buf[bufpos] = NUL;
|
|
if (bufpos >= epromptlen
|
|
&& strcmp(&buf[bufpos - epromptlen], eprompt) == 0) {
|
|
/* remove eprompt from buf */
|
|
buf[bufpos - epromptlen] = NUL;
|
|
|
|
/* print message to user */
|
|
(void)EMSG2(cs_emsg, buf);
|
|
|
|
/* send RETURN to cscope */
|
|
(void)putc('\n', csinfo[i].to_fp);
|
|
(void)fflush(csinfo[i].to_fp);
|
|
|
|
/* clear buf */
|
|
bufpos = 0;
|
|
buf[bufpos] = NUL;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (n = 0; n < (int)strlen(CSCOPE_PROMPT); ++n) {
|
|
if (n > 0)
|
|
ch = getc(csinfo[i].fr_fp);
|
|
if (ch == EOF) {
|
|
PERROR("cs_read_prompt EOF");
|
|
if (buf != NULL && buf[0] != NUL)
|
|
(void)EMSG2(cs_emsg, buf);
|
|
else if (p_csverbose)
|
|
cs_reading_emsg(i); /* don't have additional information */
|
|
cs_release_csp(i, TRUE);
|
|
free(buf);
|
|
return CSCOPE_FAILURE;
|
|
}
|
|
|
|
if (ch != CSCOPE_PROMPT[n]) {
|
|
ch = EOF;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ch == EOF)
|
|
continue; /* didn't find the prompt */
|
|
break; /* did find the prompt */
|
|
}
|
|
|
|
free(buf);
|
|
return CSCOPE_SUCCESS;
|
|
}
|
|
|
|
#if defined(UNIX) && defined(SIGALRM)
|
|
/*
|
|
* Used to catch and ignore SIGALRM below.
|
|
*/
|
|
static RETSIGTYPE
|
|
sig_handler SIGDEFARG(sigarg) {
|
|
/* do nothing */
|
|
SIGRETURN;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* PRIVATE: cs_release_csp
|
|
*
|
|
* Does the actual free'ing for the cs ptr with an optional flag of whether
|
|
* or not to free the filename. Called by cs_kill and cs_reset.
|
|
*/
|
|
static void cs_release_csp(int i, int freefnpp)
|
|
{
|
|
/*
|
|
* Trying to exit normally (not sure whether it is fit to UNIX cscope
|
|
*/
|
|
if (csinfo[i].to_fp != NULL) {
|
|
(void)fputs("q\n", csinfo[i].to_fp);
|
|
(void)fflush(csinfo[i].to_fp);
|
|
}
|
|
#if defined(UNIX)
|
|
{
|
|
int waitpid_errno;
|
|
int pstat;
|
|
pid_t pid;
|
|
|
|
# if defined(HAVE_SIGACTION)
|
|
struct sigaction sa, old;
|
|
|
|
/* Use sigaction() to limit the waiting time to two seconds. */
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_handler = sig_handler;
|
|
# ifdef SA_NODEFER
|
|
sa.sa_flags = SA_NODEFER;
|
|
# else
|
|
sa.sa_flags = 0;
|
|
# endif
|
|
sigaction(SIGALRM, &sa, &old);
|
|
alarm(2); /* 2 sec timeout */
|
|
|
|
/* Block until cscope exits or until timer expires */
|
|
pid = waitpid(csinfo[i].pid, &pstat, 0);
|
|
waitpid_errno = errno;
|
|
|
|
/* cancel pending alarm if still there and restore signal */
|
|
alarm(0);
|
|
sigaction(SIGALRM, &old, NULL);
|
|
# else
|
|
int waited;
|
|
|
|
/* Can't use sigaction(), loop for two seconds. First yield the CPU
|
|
* to give cscope a chance to exit quickly. */
|
|
sleep(0);
|
|
for (waited = 0; waited < 40; ++waited) {
|
|
pid = waitpid(csinfo[i].pid, &pstat, WNOHANG);
|
|
waitpid_errno = errno;
|
|
if (pid != 0)
|
|
break; /* break unless the process is still running */
|
|
os_delay(50L, false); /* sleep 50 ms */
|
|
}
|
|
# endif
|
|
/*
|
|
* If the cscope process is still running: kill it.
|
|
* Safety check: If the PID would be zero here, the entire X session
|
|
* would be killed. -1 and 1 are dangerous as well.
|
|
*/
|
|
if (pid < 0 && csinfo[i].pid > 1) {
|
|
# ifdef ECHILD
|
|
int alive = TRUE;
|
|
|
|
if (waitpid_errno == ECHILD) {
|
|
/*
|
|
* When using 'vim -g', vim is forked and cscope process is
|
|
* no longer a child process but a sibling. So waitpid()
|
|
* fails with errno being ECHILD (No child processes).
|
|
* Don't send SIGKILL to cscope immediately but wait
|
|
* (polling) for it to exit normally as result of sending
|
|
* the "q" command, hence giving it a chance to clean up
|
|
* its temporary files.
|
|
*/
|
|
int waited;
|
|
|
|
sleep(0);
|
|
for (waited = 0; waited < 40; ++waited) {
|
|
/* Check whether cscope process is still alive */
|
|
if (kill(csinfo[i].pid, 0) != 0) {
|
|
alive = FALSE; /* cscope process no longer exists */
|
|
break;
|
|
}
|
|
os_delay(50L, false); /* sleep 50ms */
|
|
}
|
|
}
|
|
if (alive)
|
|
# endif
|
|
{
|
|
kill(csinfo[i].pid, SIGKILL);
|
|
(void)waitpid(csinfo[i].pid, &pstat, 0);
|
|
}
|
|
}
|
|
}
|
|
#else /* !UNIX */
|
|
if (csinfo[i].hProc != NULL) {
|
|
/* Give cscope a chance to exit normally */
|
|
if (WaitForSingleObject(csinfo[i].hProc, 1000) == WAIT_TIMEOUT)
|
|
TerminateProcess(csinfo[i].hProc, 0);
|
|
CloseHandle(csinfo[i].hProc);
|
|
}
|
|
#endif
|
|
|
|
if (csinfo[i].fr_fp != NULL)
|
|
(void)fclose(csinfo[i].fr_fp);
|
|
if (csinfo[i].to_fp != NULL)
|
|
(void)fclose(csinfo[i].to_fp);
|
|
|
|
if (freefnpp) {
|
|
free(csinfo[i].fname);
|
|
free(csinfo[i].ppath);
|
|
free(csinfo[i].flags);
|
|
}
|
|
|
|
clear_csinfo(i);
|
|
} /* cs_release_csp */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_reset
|
|
*
|
|
* calls cs_kill on all cscope connections then reinits
|
|
*/
|
|
static int cs_reset(exarg_T *eap)
|
|
{
|
|
char **dblist = NULL, **pplist = NULL, **fllist = NULL;
|
|
int i;
|
|
char buf[20]; /* for sprintf " (#%d)" */
|
|
|
|
if (csinfo_size == 0)
|
|
return CSCOPE_SUCCESS;
|
|
|
|
/* malloc our db and ppath list */
|
|
dblist = xmalloc(csinfo_size * sizeof(char *));
|
|
pplist = xmalloc(csinfo_size * sizeof(char *));
|
|
fllist = xmalloc(csinfo_size * sizeof(char *));
|
|
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
dblist[i] = csinfo[i].fname;
|
|
pplist[i] = csinfo[i].ppath;
|
|
fllist[i] = csinfo[i].flags;
|
|
if (csinfo[i].fname != NULL)
|
|
cs_release_csp(i, FALSE);
|
|
}
|
|
|
|
/* rebuild the cscope connection list */
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (dblist[i] != NULL) {
|
|
cs_add_common(dblist[i], pplist[i], fllist[i]);
|
|
if (p_csverbose) {
|
|
/* don't use smsg_attr() because we want to display the
|
|
* connection number in the same line as
|
|
* "Added cscope database..."
|
|
*/
|
|
sprintf(buf, " (#%d)", i);
|
|
MSG_PUTS_ATTR(buf, hl_attr(HLF_R));
|
|
}
|
|
}
|
|
free(dblist[i]);
|
|
free(pplist[i]);
|
|
free(fllist[i]);
|
|
}
|
|
free(dblist);
|
|
free(pplist);
|
|
free(fllist);
|
|
|
|
if (p_csverbose)
|
|
MSG_ATTR(_("All cscope databases reset"), hl_attr(HLF_R) | MSG_HIST);
|
|
return CSCOPE_SUCCESS;
|
|
} /* cs_reset */
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_resolve_file
|
|
*
|
|
* Construct the full pathname to a file found in the cscope database.
|
|
* (Prepends ppath, if there is one and if it's not already prepended,
|
|
* otherwise just uses the name found.)
|
|
*
|
|
* We need to prepend the prefix because on some cscope's (e.g., the one that
|
|
* ships with Solaris 2.6), the output never has the prefix prepended.
|
|
* Contrast this with my development system (Digital Unix), which does.
|
|
*/
|
|
static char *cs_resolve_file(int i, char *name)
|
|
{
|
|
char *fullname;
|
|
char_u *csdir = NULL;
|
|
|
|
/*
|
|
* Ppath is freed when we destroy the cscope connection.
|
|
* Fullname is freed after cs_make_vim_style_matches, after it's been
|
|
* copied into the tag buffer used by Vim.
|
|
*/
|
|
size_t len = strlen(name) + 2;
|
|
if (csinfo[i].ppath != NULL)
|
|
len += strlen(csinfo[i].ppath);
|
|
else if (p_csre && csinfo[i].fname != NULL) {
|
|
/* If 'cscoperelative' is set and ppath is not set, use cscope.out
|
|
* path in path resolution. */
|
|
csdir = xmalloc(MAXPATHL);
|
|
STRLCPY(csdir, csinfo[i].fname,
|
|
path_tail((char_u *)csinfo[i].fname)
|
|
- (char_u *)csinfo[i].fname + 1);
|
|
len += STRLEN(csdir);
|
|
}
|
|
|
|
/* Note/example: this won't work if the cscope output already starts
|
|
* "../.." and the prefix path is also "../..". if something like this
|
|
* happens, you are screwed up and need to fix how you're using cscope. */
|
|
if (csinfo[i].ppath != NULL
|
|
&& (strncmp(name, csinfo[i].ppath, strlen(csinfo[i].ppath)) != 0)
|
|
&& (name[0] != '/')
|
|
) {
|
|
fullname = xmalloc(len);
|
|
(void)sprintf(fullname, "%s/%s", csinfo[i].ppath, name);
|
|
} else if (csdir != NULL && csinfo[i].fname != NULL && *csdir != NUL) {
|
|
/* Check for csdir to be non empty to avoid empty path concatenated to
|
|
* cscope output. */
|
|
fullname = (char *)concat_fnames(csdir, (char_u *)name, TRUE);
|
|
} else {
|
|
fullname = xstrdup(name);
|
|
}
|
|
|
|
free(csdir);
|
|
return fullname;
|
|
}
|
|
|
|
|
|
/*
|
|
* PRIVATE: cs_show
|
|
*
|
|
* show all cscope connections
|
|
*/
|
|
static int cs_show(exarg_T *eap)
|
|
{
|
|
short i;
|
|
if (cs_cnt_connections() == 0)
|
|
MSG_PUTS(_("no cscope connections\n"));
|
|
else {
|
|
MSG_PUTS_ATTR(
|
|
_(" # pid database name prepend path\n"),
|
|
hl_attr(HLF_T));
|
|
for (i = 0; i < csinfo_size; i++) {
|
|
if (csinfo[i].fname == NULL)
|
|
continue;
|
|
|
|
if (csinfo[i].ppath != NULL)
|
|
(void)smsg((char_u *)"%2d %-5ld %-34s %-32s",
|
|
i, (long)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath);
|
|
else
|
|
(void)smsg((char_u *)"%2d %-5ld %-34s <none>",
|
|
i, (long)csinfo[i].pid, csinfo[i].fname);
|
|
}
|
|
}
|
|
|
|
wait_return(TRUE);
|
|
return CSCOPE_SUCCESS;
|
|
} /* cs_show */
|
|
|
|
|
|
/*
|
|
* PUBLIC: cs_end
|
|
*
|
|
* Only called when VIM exits to quit any cscope sessions.
|
|
*/
|
|
void cs_end(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < csinfo_size; i++)
|
|
cs_release_csp(i, TRUE);
|
|
free(csinfo);
|
|
csinfo_size = 0;
|
|
}
|
|
|
|
/* the end */
|