diff options
Diffstat (limited to 'src/if_cscope.c')
-rw-r--r-- | src/if_cscope.c | 2270 |
1 files changed, 2270 insertions, 0 deletions
diff --git a/src/if_cscope.c b/src/if_cscope.c new file mode 100644 index 0000000000..eec3de8de6 --- /dev/null +++ b/src/if_cscope.c @@ -0,0 +1,2270 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * CSCOPE support for Vim added by Andy Kahn <kahn@zk3.dec.com> + * Ported to Win32 by Sergey Khorev <khorev@softlab.ru> + * + * 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 "vim.h" + +#if defined(FEAT_CSCOPE) || defined(PROTO) + +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#if defined(UNIX) +# include <sys/wait.h> +#else + /* not UNIX, must be WIN32 */ +# include <io.h> +# include <fcntl.h> +# include <process.h> +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +# define pipe(fds) _pipe(fds, 256, O_TEXT|O_NOINHERIT) +#endif +#include "if_cscope.h" + +static void cs_usage_msg __ARGS((csid_e x)); +static int cs_add __ARGS((exarg_T *eap)); +static void cs_stat_emsg __ARGS((char *fname)); +static int cs_add_common __ARGS((char *, char *, char *)); +static int cs_check_for_connections __ARGS((void)); +static int cs_check_for_tags __ARGS((void)); +static int cs_cnt_connections __ARGS((void)); +static void cs_reading_emsg __ARGS((int idx)); +static int cs_cnt_matches __ARGS((int idx)); +static char * cs_create_cmd __ARGS((char *csoption, char *pattern)); +static int cs_create_connection __ARGS((int i)); +static void do_cscope_general __ARGS((exarg_T *eap, int make_split)); +static void cs_file_results __ARGS((FILE *, int *)); +static void cs_fill_results __ARGS((char *, int , int *, char ***, + char ***, int *)); +static int cs_find __ARGS((exarg_T *eap)); +static int cs_find_common __ARGS((char *opt, char *pat, int, int )); +static int cs_help __ARGS((exarg_T *eap)); +static void cs_init __ARGS((void)); +static void clear_csinfo __ARGS((int i)); +static int cs_insert_filelist __ARGS((char *, char *, char *, + struct stat *)); +static int cs_kill __ARGS((exarg_T *eap)); +static void cs_kill_execute __ARGS((int, char *)); +static cscmd_T * cs_lookup_cmd __ARGS((exarg_T *eap)); +static char * cs_make_vim_style_matches __ARGS((char *, char *, + char *, char *)); +static char * cs_manage_matches __ARGS((char **, char **, int, mcmd_e)); +static char * cs_parse_results __ARGS((int cnumber, char *buf, int bufsize, char **context, char **linenumber, char **search)); +static char * cs_pathcomponents __ARGS((char *path)); +static void cs_print_tags_priv __ARGS((char **, char **, int)); +static int cs_read_prompt __ARGS((int )); +static void cs_release_csp __ARGS((int, int freefnpp)); +static int cs_reset __ARGS((exarg_T *eap)); +static char * cs_resolve_file __ARGS((int, char *)); +static int cs_show __ARGS((exarg_T *eap)); + + +static csinfo_T csinfo[CSCOPE_MAX_CONNECTIONS]; +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_USAGE, 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 } +}; + + static void +cs_usage_msg(x) + csid_e x; +{ + (void)EMSG2(_("E560: Usage: cs[cope] %s"), cs_cmds[(int)x].usage); +} + +/* + * PRIVATE: do_cscope_general + * + * find the command, print help if invalid, and the then call the + * corresponding command function, + * called from do_cscope and do_scscope + */ + static void +do_cscope_general(eap, make_split) + exarg_T *eap; + int make_split; /* whether to split window */ +{ + cscmd_T *cmdp; + + cs_init(); + if ((cmdp = cs_lookup_cmd(eap)) == NULL) + { + cs_help(eap); + return; + } + +#ifdef FEAT_WINDOWS + 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; + } +#endif + + cmdp->func(eap); + +#ifdef FEAT_WINDOWS + postponed_split_flags = 0; +#endif +} + +/* + * PUBLIC: do_cscope + */ + void +do_cscope(eap) + exarg_T *eap; +{ + do_cscope_general(eap, FALSE); +} + +/* + * PUBLIC: do_scscope + * + * same as do_cscope, but splits window, too. + */ + void +do_scscope(eap) + exarg_T *eap; +{ + do_cscope_general(eap, TRUE); +} + +/* + * PUBLIC: do_cstag + * + */ + void +do_cstag(eap) + exarg_T *eap; +{ + int ret = FALSE; + + cs_init(); + + if (eap->arg == NULL || strlen((const char *)(eap->arg)) == 0) + { + (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); + 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); + if (ret == FALSE) + cs_free_tags(); + } + } + } + else if (cs_check_for_connections()) + { + ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE); + if (ret == FALSE) + cs_free_tags(); + } + break; + default : + break; + } + + if (!ret) + { + (void)EMSG(_("E257: cstag: tag not found")); +#if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) + g_do_tagpreview = 0; +#endif + } + +} /* 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(buf, size) + char_u *buf; + int size; +{ + char *p; + + if ((p = cs_manage_matches(NULL, NULL, -1, Get)) == NULL) + return TRUE; + + if ((int)strlen(p) > size) + { + strncpy((char *)buf, p, size - 1); + buf[size] = '\0'; + } + else + (void)strcpy((char *)buf, p); + + return FALSE; +} /* cs_fgets */ + + +/* + * PUBLIC: cs_free_tags + * + * called only from do_tag(), when popping the tag stack + */ + void +cs_free_tags() +{ + cs_manage_matches(NULL, NULL, -1, Free); +} + + +/* + * PUBLIC: cs_print_tags + * + * called from do_tag() + */ + void +cs_print_tags() +{ + 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! + */ +#if defined(FEAT_EVAL) || defined(PROTO) + int +cs_connection(num, dbpath, ppath) + int num; + char_u *dbpath; + char_u *ppath; +{ + int i; + + if (num < 0 || num > 4 || (num > 0 && !dbpath)) + return FALSE; + + for (i = 0; i < CSCOPE_MAX_CONNECTIONS; 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 */ +#endif + + +/* + * PRIVATE functions + ****************************************************************************/ + +/* + * PRIVATE: cs_add + * + * add cscope database or a directory name (to look for cscope.out) + * the the cscope connection list + * + * MAXPATHL 256 + */ +/* ARGSUSED */ + static int +cs_add(eap) + 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(fname) + char *fname; +{ + char *stat_emsg = _("E563: stat(%s) error: %d"); + char *buf = (char *)alloc((unsigned)strlen(stat_emsg) + MAXPATHL + 10); + + if (buf != NULL) + { + (void)sprintf(buf, stat_emsg, fname, errno); + (void)EMSG(buf); + vim_free(buf); + } + else + (void)EMSG(_("E563: stat error")); +} + + +/* + * 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(arg1, arg2, flags) + char *arg1; /* filename - may contain environment variables */ + char *arg2; /* prepend path - may contain environment variables */ + char *flags; +{ + struct stat statbuf; + int ret; + char *fname = NULL; + char *fname2 = NULL; + char *ppath = NULL; + int i; + + /* get the filename (arg1), expand it, and try to stat it */ + if ((fname = (char *)alloc(MAXPATHL+1)) == NULL) + goto add_err; + + expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL); + ret = stat(fname, &statbuf); + if (ret < 0) + { +staterr: + if (p_csverbose) + cs_stat_emsg(fname); + goto add_err; + } + + /* get the prepend path (arg2), expand it, and try to stat it */ + if (arg2 != NULL) + { + struct stat statbuf2; + + if ((ppath = (char *)alloc(MAXPATHL+1)) == NULL) + goto add_err; + + expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL); + ret = stat(ppath, &statbuf2); + if (ret < 0) + goto staterr; + } + + /* if filename is a directory, append the cscope database name to it */ + if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + { + fname2 = (char *)alloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2); + if (fname2 == NULL) + goto add_err; + + while (fname[strlen(fname)-1] == '/' +#ifdef WIN32 + || fname[strlen(fname)-1] == '\\' +#endif + ) + { + fname[strlen(fname)-1] = '\0'; + if (strlen(fname) == 0) + break; + } + if (fname[0] == '\0') + (void)sprintf(fname2, "/%s", CSCOPE_DBFILE); + else + (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE); + + ret = stat(fname2, &statbuf); + if (ret < 0) + { + if (p_csverbose) + cs_stat_emsg(fname2); + goto add_err; + } + + i = cs_insert_filelist(fname2, ppath, flags, &statbuf); + } +#if defined(UNIX) + else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) +#else + /* substitute define S_ISREG from os_unix.h */ + else if (((statbuf.st_mode) & S_IFMT) == S_IFREG) +#endif + { + i = cs_insert_filelist(fname, ppath, flags, &statbuf); + } + 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); + } + } + + vim_free(fname); + vim_free(fname2); + vim_free(ppath); + return CSCOPE_SUCCESS; + +add_err: + vim_free(fname2); + vim_free(fname); + vim_free(ppath); + return CSCOPE_FAILURE; +} /* cs_add_common */ + + + static int +cs_check_for_connections() +{ + return (cs_cnt_connections() > 0); +} /* cs_check_for_connections */ + + + static int +cs_check_for_tags() +{ + return (p_tags[0] != NUL && curbuf->b_p_tags != NUL); +} /* cs_check_for_tags */ + + +/* + * PRIVATE: cs_cnt_connections + * + * count the number of cscope connections + */ + static int +cs_cnt_connections() +{ + short i; + short cnt = 0; + + for (i = 0; i < CSCOPE_MAX_CONNECTIONS; i++) + { + if (csinfo[i].fname != NULL) + cnt++; + } + return cnt; +} /* cs_cnt_connections */ + + static void +cs_reading_emsg(idx) + int idx; /* connection index */ +{ + EMSGN(_("E262: error reading cscope connection %ld"), idx); +} + +#define CSREAD_BUFSIZE 2048 +/* + * PRIVATE: cs_cnt_matches + * + * count the number of matches for a given cscope connection. + */ + static int +cs_cnt_matches(idx) + int idx; +{ + char *stok; + char *buf; + int nlines; + + buf = (char *)alloc(CSREAD_BUFSIZE); + if (buf == NULL) + return 0; + for (;;) + { + if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp)) + { + if (feof(csinfo[idx].fr_fp)) + errno = EIO; + + cs_reading_emsg(idx); + + vim_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. + */ + if ((stok = strtok(buf, (const char *)" ")) == NULL) + continue; + if (strcmp((const char *)stok, "cscope:")) + 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; + } + + vim_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(csoption, pattern) + char *csoption; + char *pattern; +{ + char *cmd; + short search; + + 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; + } + + if ((cmd = (char *)alloc(strlen(pattern) + 2)) == NULL) + return NULL; + + (void)sprintf(cmd, "%d%s", search, pattern); + + 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(i) + int i; +{ + int to_cs[2], from_cs[2], len; + char *prog, *cmd, *ppath = NULL; +#ifndef UNIX + int in_save, out_save, err_save; + int ph; +# ifdef FEAT_GUI + HWND activewnd = NULL; + HWND consolewnd = NULL; +# endif +#endif + + /* + * 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; + } + +#if defined(UNIX) + switch (csinfo[i].pid = fork()) + { + case -1: + (void)EMSG(_("E622: Could not fork for cscope")); + goto err_closing; + case 0: /* child: run cscope. */ +#else + in_save = dup(STDIN_FILENO); + out_save = dup(STDOUT_FILENO); + err_save = dup(STDERR_FILENO); +#endif + 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 */ +#if defined(UNIX) + (void)close(to_cs[1]); + (void)close(from_cs[0]); +#else + /* On win32 we must close opposite ends because we are the parent */ + (void)close(to_cs[0]); + to_cs[0] = -1; + (void)close(from_cs[1]); + from_cs[1] = -1; +#endif + /* expand the cscope exec for env var's */ + if ((prog = (char *)alloc(MAXPATHL + 1)) == NULL) + { +#ifdef UNIX + return CSCOPE_FAILURE; +#else + goto err_closing; +#endif + } + expand_env((char_u *)p_csprg, (char_u *)prog, MAXPATHL); + + /* alloc space to hold the cscope command */ + len = strlen(prog) + strlen(csinfo[i].fname) + 32; + if (csinfo[i].ppath) + { + /* expand the prepend path for env var's */ + if ((ppath = (char *)alloc(MAXPATHL + 1)) == NULL) + { + vim_free(prog); +#ifdef UNIX + return CSCOPE_FAILURE; +#else + goto err_closing; +#endif + } + expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL); + + len += strlen(ppath); + } + + if (csinfo[i].flags) + len += strlen(csinfo[i].flags); + + if ((cmd = (char *)alloc(len)) == NULL) + { + vim_free(prog); + vim_free(ppath); +#ifdef UNIX + return CSCOPE_FAILURE; +#else + goto err_closing; +#endif + } + + /* 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 + (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 */ + vim_free(prog); +# endif + vim_free(ppath); + +#if defined(UNIX) + if (execl("/bin/sh", "sh", "-c", cmd, NULL) == -1) + PERROR(_("cs_create_connection exec failed")); + + exit(127); + /* NOTREACHED */ + default: /* parent. */ +#else +# ifdef FEAT_GUI + activewnd = GetForegroundWindow(); /* on win9x cscope steals focus */ + /* Dirty hack to hide annoying console window */ + if (AllocConsole()) + { + char *title; + title = (char *)alloc(1024); + if (title == NULL) + FreeConsole(); + else + { + GetConsoleTitle(title, 1024); /* save for future restore */ + SetConsoleTitle( + "GVIMCS{5499421B-CBEF-45b0-85EF-38167FDEA5C5}GVIMCS"); + Sleep(40); /* as stated in MS KB we must wait 40 ms */ + consolewnd = FindWindow(NULL, + "GVIMCS{5499421B-CBEF-45b0-85EF-38167FDEA5C5}GVIMCS"); + if (consolewnd != NULL) + ShowWindow(consolewnd, SW_HIDE); + SetConsoleTitle(title); + vim_free(title); + } + } +# endif + /* May be use &shell, &shellquote etc */ +# ifdef __BORLANDC__ + /* BCC 5.5 uses a different function name for spawnlp */ + ph = spawnlp(P_NOWAIT, prog, cmd, NULL); +# else + ph = _spawnlp(_P_NOWAIT, prog, cmd, NULL); +# endif + vim_free(prog); + vim_free(cmd); +# ifdef FEAT_GUI + /* Dirty hack part two */ + if (activewnd != NULL) + /* restoring focus */ + SetForegroundWindow(activewnd); + if (consolewnd != NULL) + FreeConsole(); + +# endif + if (ph == -1) + { + PERROR(_("cs_create_connection exec failed")); + (void)EMSG(_("E623: Could not spawn cscope process")); + goto err_closing; + } + /* else */ + csinfo[i].pid = 0; + csinfo[i].hProc = (HANDLE)ph; + +#endif /* !UNIX */ + /* + * 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")); + +#if defined(UNIX) + /* close unused */ + (void)close(to_cs[0]); + (void)close(from_cs[1]); + + break; + } +#else + /* restore stdhandles */ + dup2(in_save, STDIN_FILENO); + dup2(out_save, STDOUT_FILENO); + dup2(err_save, STDERR_FILENO); + close(in_save); + close(out_save); + close(err_save); +#endif + 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(eap) + exarg_T *eap; +{ + char *opt, *pat; + + 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 == NULL || (pat != NULL && pat[0] == '\0')) + { + cs_usage_msg(Find); + return FALSE; + } + + return cs_find_common(opt, pat, eap->forceit, TRUE); +} /* cs_find */ + + +/* + * PRIVATE: cs_find_common + * + * common code for cscope find, shared by cs_find() and do_cstag() + */ + static int +cs_find_common(opt, pat, forceit, verbose) + char *opt; + char *pat; + int forceit; + int verbose; +{ + int i; + char *cmd; + char **matches, **contexts; + int nummatches[CSCOPE_MAX_CONNECTIONS], totmatches, matched; +#ifdef FEAT_QUICKFIX + char cmdletter; + char *qfpos; +#endif + + /* create the actual command to send to cscope */ + cmd = cs_create_cmd(opt, pat); + if (cmd == NULL) + return FALSE; + + /* send query to all open connections, then count the total number + * of matches so we can alloc matchesp all in one swell foop + */ + for (i = 0; i < CSCOPE_MAX_CONNECTIONS; i++) + nummatches[i] = 0; + totmatches = 0; + for (i = 0; i < CSCOPE_MAX_CONNECTIONS; i++) + { + if (csinfo[i].fname == 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); + } + vim_free(cmd); + + if (totmatches == 0) + { + char *nf = _("E259: no matches found for cscope query %s of %s"); + char *buf; + + if (!verbose) + return FALSE; + + buf = (char *)alloc(strlen(opt) + strlen(pat) + strlen(nf)); + if (buf == NULL) + (void)EMSG(nf); + else + { + sprintf(buf, nf, opt, pat); + (void)EMSG(buf); + vim_free(buf); + } + return FALSE; + } + +#ifdef FEAT_QUICKFIX + /* 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"); + char *buf = (char *)alloc(strlen(nf)); + + /* strlen will be enough because we use chars */ + if (buf != NULL) + { + sprintf(buf, nf, *qfpos, *(qfpos-1)); + (void)EMSG(buf); + vim_free(buf); + } + return FALSE; + } + } + if (qfpos != NULL && *qfpos != '0' && totmatches > 1) + { + /* fill error list */ + FILE *f; + char_u *tmp = vim_tempname('c'); + + f = fopen((char *)tmp, "w"); + cs_file_results(f, nummatches); + fclose(f); + /* '-' starts a new error list */ + if (qf_init(tmp, (char_u *)"%f%*\\t%l%*\\t%m", *qfpos == '-') > 0) + { +# ifdef FEAT_WINDOWS + if (postponed_split != 0) + { + win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags); +# ifdef FEAT_SCROLLBIND + curwin->w_p_scb = FALSE; +# endif + postponed_split = 0; + } +# endif + qf_jump(0, 0, forceit); + } + mch_remove(tmp); + vim_free(tmp); + return TRUE; + } + else +#endif /* FEAT_QUICKFIX */ + { + /* read output */ + cs_fill_results((char *)pat, totmatches, nummatches, &matches, + &contexts, &matched); + if (matches == NULL) + return FALSE; + + (void)cs_manage_matches(matches, contexts, totmatches, Store); + + return do_tag((char_u *)pat, DT_CSCOPE, 0, forceit, verbose); + } + +} /* cs_find_common */ + +/* + * PRIVATE: cs_help + * + * print help + */ +/* ARGSUSED */ + static int +cs_help(eap) + exarg_T *eap; +{ + cscmd_T *cmdp = cs_cmds; + + (void)MSG_PUTS(_("cscope commands:\n")); + while (cmdp->name != NULL) + { + (void)smsg((char_u *)_("%-5s: %-30s (Usage: %s)"), + cmdp->name, _(cmdp->help), cmdp->usage); + if (strcmp(cmdp->name, "find") == 0) + MSG_PUTS(FIND_HELP); + cmdp++; + } + + wait_return(TRUE); + return 0; +} /* cs_help */ + + +/* + * PRIVATE: cs_init + * + * initialize cscope structure if not already + */ + static void +cs_init() +{ + short i; + static int init_already = FALSE; + + if (init_already) + return; + + for (i = 0; i < CSCOPE_MAX_CONNECTIONS; i++) + clear_csinfo(i); + + init_already = TRUE; +} /* cs_init */ + + static void +clear_csinfo(i) + int i; +{ + csinfo[i].fname = NULL; + csinfo[i].ppath = NULL; + csinfo[i].flags = NULL; +#if defined(UNIX) + csinfo[i].st_dev = (dev_t)0; + csinfo[i].st_ino = (ino_t)0; +#else + csinfo[i].nVolume = 0; + csinfo[i].nIndexHigh = 0; + csinfo[i].nIndexLow = 0; +#endif + csinfo[i].pid = -1; + csinfo[i].fr_fp = NULL; + csinfo[i].to_fp = NULL; +} + +#ifndef UNIX +static char *GetWin32Error __ARGS((void)); + + static char * +GetWin32Error() +{ + 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(fname, ppath, flags, sb) + char *fname; + char *ppath; + char *flags; + struct stat *sb; +{ + short i, j; +#ifndef UNIX + HANDLE hFile; + BY_HANDLE_FILE_INFORMATION bhfi; + + vim_memset(&bhfi, 0, sizeof(bhfi)); + /* On windows 9x GetFileInformationByHandle doesn't work, so skip it */ + if (!mch_windows95()) + { + hFile = CreateFile(fname, FILE_READ_ATTRIBUTES, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + if (p_csverbose) + { + char *cant_msg = _("E625: cannot open cscope database: %s"); + char *winmsg = GetWin32Error(); + + if (winmsg != NULL) + { + (void)EMSG2(cant_msg, winmsg); + LocalFree(winmsg); + } + else + /* subst filename if can't get error text */ + (void)EMSG2(cant_msg, fname); + } + return -1; + } + if (!GetFileInformationByHandle(hFile, &bhfi)) + { + CloseHandle(hFile); + if (p_csverbose) + (void)EMSG(_("E626: cannot get cscope database information")); + return -1; + } + CloseHandle(hFile); + } +#endif + + i = -1; /* can be set to the index of an empty item in csinfo */ + for (j = 0; j < CSCOPE_MAX_CONNECTIONS; j++) + { + if (csinfo[j].fname != NULL +#if defined(UNIX) + && csinfo[j].st_dev == sb->st_dev && csinfo[j].st_ino == sb->st_ino +#else + /* compare pathnames first */ + && ((fullpathcmp(csinfo[j].fname, fname, FALSE) & FPC_SAME) + /* if not Windows 9x, test index file atributes too */ + || (!mch_windows95() + && csinfo[j].nVolume == bhfi.dwVolumeSerialNumber + && csinfo[j].nIndexHigh == bhfi.nFileIndexHigh + && csinfo[j].nIndexLow == bhfi.nFileIndexLow)) +#endif + ) + { + 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) + { + if (p_csverbose) + (void)EMSG(_("E569: maximum number of cscope connections reached")); + return -1; + } + + if ((csinfo[i].fname = (char *)alloc(strlen(fname)+1)) == NULL) + return -1; + + (void)strcpy(csinfo[i].fname, (const char *)fname); + + if (ppath != NULL) + { + if ((csinfo[i].ppath = (char *)alloc(strlen(ppath) + 1)) == NULL) + { + vim_free(csinfo[i].fname); + csinfo[i].fname = NULL; + return -1; + } + (void)strcpy(csin |