diff options
| author | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-12 02:25:17 +0200 |
|---|---|---|
| committer | Eliseo Martínez <eliseomarmol@gmail.com> | 2014-05-15 20:46:01 +0200 |
| commit | da51dc9cf202772f60bd2da975dbef257bd9237c (patch) | |
| tree | 5c16b93238a153f55634e9323077f30c8133970c /src/nvim/if_cscope.c | |
| parent | ffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff) | |
| download | rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2 rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip | |
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/.
- src/nvim/ becomes the new root dir for nvim executable sources.
- src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/nvim/if_cscope.c')
| -rw-r--r-- | src/nvim/if_cscope.c | 2167 |
1 files changed, 2167 insertions, 0 deletions
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c new file mode 100644 index 0000000000..b000b03444 --- /dev/null +++ b/src/nvim/if_cscope.c @@ -0,0 +1,2167 @@ +/* + * 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 "vim.h" +#include "if_cscope.h" +#include "charset.h" +#include "eval.h" +#include "fileio.h" +#include "message.h" +#include "misc1.h" +#include "misc2.h" +#include "memory.h" +#include "os/time.h" +#include "path.h" +#include "quickfix.h" +#include "tag.h" +#include "ui.h" +#include "window.h" +#include "os/os.h" + +#include <sys/types.h> +#include <sys/stat.h> +#if defined(UNIX) +# include <sys/wait.h> +#endif +#include "if_cscope_defs.h" + +static void cs_usage_msg(csid_e x); +static int cs_add(exarg_T *eap); +static void cs_stat_emsg(char *fname); +static int cs_add_common(char *, char *, char *); +static int cs_check_for_connections(void); +static int cs_check_for_tags(void); +static int cs_cnt_connections(void); +static void cs_reading_emsg(int idx); +static int cs_cnt_matches(int idx); +static char * cs_create_cmd(char *csoption, char *pattern); +static int cs_create_connection(int i); +static void do_cscope_general(exarg_T *eap, int make_split); +static void cs_file_results(FILE *, int *); +static void cs_fill_results(char *, int, int *, char ***, + char ***, int *); +static int cs_find(exarg_T *eap); +static int cs_find_common(char *opt, char *pat, int, int, int, + char_u *cmdline); +static int cs_help(exarg_T *eap); +static void clear_csinfo(int i); +static int cs_insert_filelist(char *, char *, char *, FileInfo *file_info); +static int cs_kill(exarg_T *eap); +static void cs_kill_execute(int, char *); +static cscmd_T * cs_lookup_cmd(exarg_T *eap); +static char * cs_make_vim_style_matches(char *, char *, + char *, char *); +static char * cs_manage_matches(char **, char **, int, mcmd_e); +static char * cs_parse_results(int cnumber, char *buf, + int bufsize, char **context, + char **linenumber, + char **search); +static char * cs_pathcomponents(char *path); +static void cs_print_tags_priv(char **, char **, int); +static int cs_read_prompt(int); +static void cs_release_csp(int, int freefnpp); +static int cs_reset(exarg_T *eap); +static char * cs_resolve_file(int, char *); +static int cs_show(exarg_T *eap); + + +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; + vim_strncpy(buf, (char_u *)p, size - 1); + + 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 = (char *)alloc((unsigned)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 = (char *)alloc(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_get_file_info(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 = (char *)alloc(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 *)alloc((unsigned)(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_get_file_info(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; + char *buf; + int nlines; + + buf = (char *)alloc(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 = (char *)alloc((unsigned)(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 = (char *)alloc(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 = (char *)alloc(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 = (char *)alloc(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 = (char *)alloc((unsigned)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 = (int *)alloc(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 = (char *)alloc((unsigned)(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('c'); + 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 |