1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
3  * CSCOPE support for Vim added by Andy Kahn <kahn@zk3.dec.com>
4  * Ported to Win32 by Sergey Khorev <sergey.khorev@gmail.com>
5  *
6  * The basic idea/structure of cscope for Vim was borrowed from Nvi.  There
7  * might be a few lines of code that look similar to what Nvi has.
8  *
9  * See README.txt for an overview of the Vim source code.
10  */
11 
12 #include "vim.h"
13 
14 #if defined(FEAT_CSCOPE) || defined(PROTO)
15 
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #if defined(UNIX)
19 # include <sys/wait.h>
20 #endif
21 #include "if_cscope.h"
22 
23 static int	    cs_add(exarg_T *eap);
24 static int	    cs_add_common(char *, char *, char *);
25 static int	    cs_check_for_connections(void);
26 static int	    cs_check_for_tags(void);
27 static int	    cs_cnt_connections(void);
28 static int	    cs_create_connection(int i);
29 #ifdef FEAT_QUICKFIX
30 static void	    cs_file_results(FILE *, int *);
31 #endif
32 static void	    cs_fill_results(char *, int , int *, char ***,
33 			char ***, int *);
34 static int	    cs_find(exarg_T *eap);
35 static int	    cs_find_common(char *opt, char *pat, int, int, int, char_u *cmdline);
36 static int	    cs_help(exarg_T *eap);
37 static int	    cs_insert_filelist(char *, char *, char *,
38 			stat_T *);
39 static int	    cs_kill(exarg_T *eap);
40 static void	    cs_kill_execute(int, char *);
41 static cscmd_T *    cs_lookup_cmd(exarg_T *eap);
42 static char *	    cs_make_vim_style_matches(char *, char *,
43 			char *, char *);
44 static char *	    cs_manage_matches(char **, char **, int, mcmd_e);
45 static void	    cs_print_tags_priv(char **, char **, int);
46 static int	    cs_read_prompt(int);
47 static void	    cs_release_csp(int, int freefnpp);
48 static int	    cs_reset(exarg_T *eap);
49 static char *	    cs_resolve_file(int, char *);
50 static int	    cs_show(exarg_T *eap);
51 
52 
53 static csinfo_T *   csinfo = NULL;
54 static int	    csinfo_size = 0;	// number of items allocated in
55 					// csinfo[]
56 
57 static int	    eap_arg_len;    // length of eap->arg, set in
58 				    // cs_lookup_cmd()
59 static cscmd_T	    cs_cmds[] =
60 {
61     { "add",	cs_add,
62 		N_("Add a new database"),     "add file|dir [pre-path] [flags]", 0 },
63     { "find",	cs_find,
64 		N_("Query for a pattern"),    "find a|c|d|e|f|g|i|s|t name", 1 },
65     { "help",	cs_help,
66 		N_("Show this message"),      "help", 0 },
67     { "kill",	cs_kill,
68 		N_("Kill a connection"),      "kill #", 0 },
69     { "reset",	cs_reset,
70 		N_("Reinit all connections"), "reset", 0 },
71     { "show",	cs_show,
72 		N_("Show connections"),       "show", 0 },
73     { NULL, NULL, NULL, NULL, 0 }
74 };
75 
76     static void
cs_usage_msg(csid_e x)77 cs_usage_msg(csid_e x)
78 {
79     (void)semsg(_("E560: Usage: cs[cope] %s"), cs_cmds[(int)x].usage);
80 }
81 
82 static enum
83 {
84     EXP_CSCOPE_SUBCMD,	// expand ":cscope" sub-commands
85     EXP_SCSCOPE_SUBCMD,	// expand ":scscope" sub-commands
86     EXP_CSCOPE_FIND,	// expand ":cscope find" arguments
87     EXP_CSCOPE_KILL	// expand ":cscope kill" arguments
88 } expand_what;
89 
90 /*
91  * Function given to ExpandGeneric() to obtain the cscope command
92  * expansion.
93  */
94     char_u *
get_cscope_name(expand_T * xp UNUSED,int idx)95 get_cscope_name(expand_T *xp UNUSED, int idx)
96 {
97     int		current_idx;
98     int		i;
99 
100     switch (expand_what)
101     {
102     case EXP_CSCOPE_SUBCMD:
103 	// Complete with sub-commands of ":cscope":
104 	// add, find, help, kill, reset, show
105 	return (char_u *)cs_cmds[idx].name;
106     case EXP_SCSCOPE_SUBCMD:
107 	// Complete with sub-commands of ":scscope": same sub-commands as
108 	// ":cscope" but skip commands which don't support split windows
109 	for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++)
110 	    if (cs_cmds[i].cansplit)
111 		if (current_idx++ == idx)
112 		    break;
113 	return (char_u *)cs_cmds[i].name;
114     case EXP_CSCOPE_FIND:
115 	{
116 	    const char *query_type[] =
117 	    {
118 		"a", "c", "d", "e", "f", "g", "i", "s", "t", NULL
119 	    };
120 
121 	    // Complete with query type of ":cscope find {query_type}".
122 	    // {query_type} can be letters (c, d, ... a) or numbers (0, 1,
123 	    // ..., 9) but only complete with letters, since numbers are
124 	    // redundant.
125 	    return (char_u *)query_type[idx];
126 	}
127     case EXP_CSCOPE_KILL:
128 	{
129 	    static char	connection[5];
130 
131 	    // ":cscope kill" accepts connection numbers or partial names of
132 	    // the pathname of the cscope database as argument.  Only complete
133 	    // with connection numbers. -1 can also be used to kill all
134 	    // connections.
135 	    for (i = 0, current_idx = 0; i < csinfo_size; i++)
136 	    {
137 		if (csinfo[i].fname == NULL)
138 		    continue;
139 		if (current_idx++ == idx)
140 		{
141 		    vim_snprintf(connection, sizeof(connection), "%d", i);
142 		    return (char_u *)connection;
143 		}
144 	    }
145 	    return (current_idx == idx && idx > 0) ? (char_u *)"-1" : NULL;
146 	}
147     default:
148 	return NULL;
149     }
150 }
151 
152 /*
153  * Handle command line completion for :cscope command.
154  */
155     void
set_context_in_cscope_cmd(expand_T * xp,char_u * arg,cmdidx_T cmdidx)156 set_context_in_cscope_cmd(
157     expand_T	*xp,
158     char_u	*arg,
159     cmdidx_T	cmdidx)
160 {
161     char_u	*p;
162 
163     // Default: expand subcommands
164     xp->xp_context = EXPAND_CSCOPE;
165     xp->xp_pattern = arg;
166     expand_what = (cmdidx == CMD_scscope)
167 			? EXP_SCSCOPE_SUBCMD : EXP_CSCOPE_SUBCMD;
168 
169     // (part of) subcommand already typed
170     if (*arg != NUL)
171     {
172 	p = skiptowhite(arg);
173 	if (*p != NUL)		    // past first word
174 	{
175 	    xp->xp_pattern = skipwhite(p);
176 	    if (*skiptowhite(xp->xp_pattern) != NUL)
177 		xp->xp_context = EXPAND_NOTHING;
178 	    else if (STRNICMP(arg, "add", p - arg) == 0)
179 		xp->xp_context = EXPAND_FILES;
180 	    else if (STRNICMP(arg, "kill", p - arg) == 0)
181 		expand_what = EXP_CSCOPE_KILL;
182 	    else if (STRNICMP(arg, "find", p - arg) == 0)
183 		expand_what = EXP_CSCOPE_FIND;
184 	    else
185 		xp->xp_context = EXPAND_NOTHING;
186 	}
187     }
188 }
189 
190 /*
191  * Find the command, print help if invalid, and then call the corresponding
192  * command function.
193  */
194     static void
do_cscope_general(exarg_T * eap,int make_split UNUSED)195 do_cscope_general(
196     exarg_T	*eap,
197     int		make_split UNUSED) // whether to split window
198 {
199     cscmd_T *cmdp;
200 
201     if ((cmdp = cs_lookup_cmd(eap)) == NULL)
202     {
203 	cs_help(eap);
204 	return;
205     }
206 
207     if (make_split)
208     {
209 	if (!cmdp->cansplit)
210 	{
211 	    (void)msg_puts(_("This cscope command does not support splitting the window.\n"));
212 	    return;
213 	}
214 	postponed_split = -1;
215 	postponed_split_flags = cmdmod.cmod_split;
216 	postponed_split_tab = cmdmod.cmod_tab;
217     }
218 
219     cmdp->func(eap);
220 
221     postponed_split_flags = 0;
222     postponed_split_tab = 0;
223 }
224 
225 /*
226  * Implementation of ":cscope" and ":lcscope"
227  */
228     void
ex_cscope(exarg_T * eap)229 ex_cscope(exarg_T *eap)
230 {
231     do_cscope_general(eap, FALSE);
232 }
233 
234 /*
235  * Implementation of ":scscope". Same as ex_cscope(), but splits window, too.
236  */
237     void
ex_scscope(exarg_T * eap)238 ex_scscope(exarg_T *eap)
239 {
240     do_cscope_general(eap, TRUE);
241 }
242 
243 /*
244  * Implementation of ":cstag"
245  */
246     void
ex_cstag(exarg_T * eap)247 ex_cstag(exarg_T *eap)
248 {
249     int ret = FALSE;
250 
251     if (*eap->arg == NUL)
252     {
253 	(void)emsg(_("E562: Usage: cstag <ident>"));
254 	return;
255     }
256 
257     switch (p_csto)
258     {
259     case 0 :
260 	if (cs_check_for_connections())
261 	{
262 	    ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
263 						       FALSE, *eap->cmdlinep);
264 	    if (ret == FALSE)
265 	    {
266 		cs_free_tags();
267 		if (msg_col)
268 		    msg_putchar('\n');
269 
270 		if (cs_check_for_tags())
271 		    ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
272 	    }
273 	}
274 	else if (cs_check_for_tags())
275 	{
276 	    ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
277 	}
278 	break;
279     case 1 :
280 	if (cs_check_for_tags())
281 	{
282 	    ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE);
283 	    if (ret == FALSE)
284 	    {
285 		if (msg_col)
286 		    msg_putchar('\n');
287 
288 		if (cs_check_for_connections())
289 		{
290 		    ret = cs_find_common("g", (char *)(eap->arg), eap->forceit,
291 						FALSE, FALSE, *eap->cmdlinep);
292 		    if (ret == FALSE)
293 			cs_free_tags();
294 		}
295 	    }
296 	}
297 	else if (cs_check_for_connections())
298 	{
299 	    ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE,
300 						       FALSE, *eap->cmdlinep);
301 	    if (ret == FALSE)
302 		cs_free_tags();
303 	}
304 	break;
305     default :
306 	break;
307     }
308 
309     if (!ret)
310     {
311 	(void)emsg(_("E257: cstag: tag not found"));
312 #if defined(FEAT_QUICKFIX)
313 	g_do_tagpreview = 0;
314 #endif
315     }
316 
317 }
318 
319 
320 /*
321  * This simulates a vim_fgets(), but for cscope, returns the next line
322  * from the cscope output.  should only be called from find_tags()
323  *
324  * returns TRUE if eof, FALSE otherwise
325  */
326     int
cs_fgets(char_u * buf,int size)327 cs_fgets(char_u *buf, int size)
328 {
329     char *p;
330 
331     if ((p = cs_manage_matches(NULL, NULL, -1, Get)) == NULL)
332 	return TRUE;
333     vim_strncpy(buf, (char_u *)p, size - 1);
334 
335     return FALSE;
336 }
337 
338 
339 /*
340  * Called only from do_tag(), when popping the tag stack.
341  */
342     void
cs_free_tags(void)343 cs_free_tags(void)
344 {
345     cs_manage_matches(NULL, NULL, -1, Free);
346 }
347 
348 
349 /*
350  * Called from do_tag().
351  */
352     void
cs_print_tags(void)353 cs_print_tags(void)
354 {
355     cs_manage_matches(NULL, NULL, -1, Print);
356 }
357 
358 
359 /*
360  * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
361  *
362  *		Checks for the existence of a |cscope| connection.  If no
363  *		parameters are specified, then the function returns:
364  *
365  *		0, if cscope was not available (not compiled in), or if there
366  *		are no cscope connections; or
367  *		1, if there is at least one cscope connection.
368  *
369  *		If parameters are specified, then the value of {num}
370  *		determines how existence of a cscope connection is checked:
371  *
372  *		{num}	Description of existence check
373  *		-----	------------------------------
374  *		0	Same as no parameters (e.g., "cscope_connection()").
375  *		1	Ignore {prepend}, and use partial string matches for
376  *			{dbpath}.
377  *		2	Ignore {prepend}, and use exact string matches for
378  *			{dbpath}.
379  *		3	Use {prepend}, use partial string matches for both
380  *			{dbpath} and {prepend}.
381  *		4	Use {prepend}, use exact string matches for both
382  *			{dbpath} and {prepend}.
383  *
384  *		Note: All string comparisons are case sensitive!
385  */
386 #if defined(FEAT_EVAL) || defined(PROTO)
387     static int
cs_connection(int num,char_u * dbpath,char_u * ppath)388 cs_connection(int num, char_u *dbpath, char_u *ppath)
389 {
390     int i;
391 
392     if (num < 0 || num > 4 || (num > 0 && !dbpath))
393 	return FALSE;
394 
395     for (i = 0; i < csinfo_size; i++)
396     {
397 	if (!csinfo[i].fname)
398 	    continue;
399 
400 	if (num == 0)
401 	    return TRUE;
402 
403 	switch (num)
404 	{
405 	case 1:
406 	    if (strstr(csinfo[i].fname, (char *)dbpath))
407 		return TRUE;
408 	    break;
409 	case 2:
410 	    if (strcmp(csinfo[i].fname, (char *)dbpath) == 0)
411 		return TRUE;
412 	    break;
413 	case 3:
414 	    if (strstr(csinfo[i].fname, (char *)dbpath)
415 		    && ((!ppath && !csinfo[i].ppath)
416 			|| (ppath
417 			    && csinfo[i].ppath
418 			    && strstr(csinfo[i].ppath, (char *)ppath))))
419 		return TRUE;
420 	    break;
421 	case 4:
422 	    if ((strcmp(csinfo[i].fname, (char *)dbpath) == 0)
423 		    && ((!ppath && !csinfo[i].ppath)
424 			|| (ppath
425 			    && csinfo[i].ppath
426 			    && (strcmp(csinfo[i].ppath, (char *)ppath) == 0))))
427 		return TRUE;
428 	    break;
429 	}
430     }
431 
432     return FALSE;
433 }
434 
435 #endif
436 
437 
438 /*
439  * PRIVATE functions
440  ****************************************************************************/
441 
442 /*
443  * Add cscope database or a directory name (to look for cscope.out)
444  * to the cscope connection list.
445  */
446     static int
cs_add(exarg_T * eap UNUSED)447 cs_add(exarg_T *eap UNUSED)
448 {
449     char *fname, *ppath, *flags = NULL;
450 
451     if ((fname = strtok((char *)NULL, (const char *)" ")) == NULL)
452     {
453 	cs_usage_msg(Add);
454 	return CSCOPE_FAILURE;
455     }
456     if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL)
457 	flags = strtok((char *)NULL, (const char *)" ");
458 
459     return cs_add_common(fname, ppath, flags);
460 }
461 
462     static void
cs_stat_emsg(char * fname)463 cs_stat_emsg(char *fname)
464 {
465     int err = errno;
466     (void)semsg(_("E563: stat(%s) error: %d"), fname, err);
467 }
468 
469 
470 /*
471  * The common routine to add a new cscope connection.  Called by
472  * cs_add() and cs_reset().  I really don't like to do this, but this
473  * routine uses a number of goto statements.
474  */
475     static int
cs_add_common(char * arg1,char * arg2,char * flags)476 cs_add_common(
477     char *arg1,	    // filename - may contain environment variables
478     char *arg2,	    // prepend path - may contain environment variables
479     char *flags)
480 {
481     stat_T	statbuf;
482     int		ret;
483     char	*fname = NULL;
484     char	*fname2 = NULL;
485     char	*ppath = NULL;
486     int		i;
487     int		len;
488     int		usedlen = 0;
489     char_u	*fbuf = NULL;
490 
491     // get the filename (arg1), expand it, and try to stat it
492     if ((fname = alloc(MAXPATHL + 1)) == NULL)
493 	goto add_err;
494 
495     expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
496     len = (int)STRLEN(fname);
497     fbuf = (char_u *)fname;
498     (void)modify_fname((char_u *)":p", FALSE, &usedlen,
499 					      (char_u **)&fname, &fbuf, &len);
500     if (fname == NULL)
501 	goto add_err;
502     fname = (char *)vim_strnsave((char_u *)fname, len);
503     vim_free(fbuf);
504 
505     ret = mch_stat(fname, &statbuf);
506     if (ret < 0)
507     {
508 staterr:
509 	if (p_csverbose)
510 	    cs_stat_emsg(fname);
511 	goto add_err;
512     }
513 
514     // get the prepend path (arg2), expand it, and try to stat it
515     if (arg2 != NULL)
516     {
517 	stat_T	    statbuf2;
518 
519 	if ((ppath = alloc(MAXPATHL + 1)) == NULL)
520 	    goto add_err;
521 
522 	expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL);
523 	ret = mch_stat(ppath, &statbuf2);
524 	if (ret < 0)
525 	    goto staterr;
526     }
527 
528     // if filename is a directory, append the cscope database name to it
529     if (S_ISDIR(statbuf.st_mode))
530     {
531 	fname2 = alloc(strlen(CSCOPE_DBFILE) + strlen(fname) + 2);
532 	if (fname2 == NULL)
533 	    goto add_err;
534 
535 	while (fname[strlen(fname)-1] == '/'
536 #ifdef MSWIN
537 		|| fname[strlen(fname)-1] == '\\'
538 #endif
539 		)
540 	{
541 	    fname[strlen(fname)-1] = '\0';
542 	    if (fname[0] == '\0')
543 		break;
544 	}
545 	if (fname[0] == '\0')
546 	    (void)sprintf(fname2, "/%s", CSCOPE_DBFILE);
547 	else
548 	    (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE);
549 
550 	ret = mch_stat(fname2, &statbuf);
551 	if (ret < 0)
552 	{
553 	    if (p_csverbose)
554 		cs_stat_emsg(fname2);
555 	    goto add_err;
556 	}
557 
558 	i = cs_insert_filelist(fname2, ppath, flags, &statbuf);
559     }
560     else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode))
561     {
562 	i = cs_insert_filelist(fname, ppath, flags, &statbuf);
563     }
564     else
565     {
566 	if (p_csverbose)
567 	    (void)semsg(
568 		_("E564: %s is not a directory or a valid cscope database"),
569 		fname);
570 	goto add_err;
571     }
572 
573     if (i != -1)
574     {
575 	if (cs_create_connection(i) == CSCOPE_FAILURE
576 		|| cs_read_prompt(i) == CSCOPE_FAILURE)
577 	{
578 	    cs_release_csp(i, TRUE);
579 	    goto add_err;
580 	}
581 
582 	if (p_csverbose)
583 	{
584 	    msg_clr_eos();
585 	    (void)smsg_attr(HL_ATTR(HLF_R),
586 			    _("Added cscope database %s"),
587 			    csinfo[i].fname);
588 	}
589     }
590 
591     vim_free(fname);
592     vim_free(fname2);
593     vim_free(ppath);
594     return CSCOPE_SUCCESS;
595 
596 add_err:
597     vim_free(fname2);
598     vim_free(fname);
599     vim_free(ppath);
600     return CSCOPE_FAILURE;
601 }
602 
603 
604     static int
cs_check_for_connections(void)605 cs_check_for_connections(void)
606 {
607     return (cs_cnt_connections() > 0);
608 }
609 
610 
611     static int
cs_check_for_tags(void)612 cs_check_for_tags(void)
613 {
614     return (p_tags[0] != NUL && curbuf->b_p_tags != NULL);
615 }
616 
617 
618 /*
619  * Count the number of cscope connections.
620  */
621     static int
cs_cnt_connections(void)622 cs_cnt_connections(void)
623 {
624     short i;
625     short cnt = 0;
626 
627     for (i = 0; i < csinfo_size; i++)
628     {
629 	if (csinfo[i].fname != NULL)
630 	    cnt++;
631     }
632     return cnt;
633 }
634 
635     static void
cs_reading_emsg(int idx)636 cs_reading_emsg(
637     int idx)	// connection index
638 {
639     semsg(_("E262: error reading cscope connection %d"), idx);
640 }
641 
642 #define	CSREAD_BUFSIZE	2048
643 /*
644  * Count the number of matches for a given cscope connection.
645  */
646     static int
cs_cnt_matches(int idx)647 cs_cnt_matches(int idx)
648 {
649     char *stok;
650     char *buf;
651     int nlines = 0;
652 
653     buf = alloc(CSREAD_BUFSIZE);
654     if (buf == NULL)
655 	return 0;
656     for (;;)
657     {
658 	if (!fgets(buf, CSREAD_BUFSIZE, csinfo[idx].fr_fp))
659 	{
660 	    if (feof(csinfo[idx].fr_fp))
661 		errno = EIO;
662 
663 	    cs_reading_emsg(idx);
664 
665 	    vim_free(buf);
666 	    return -1;
667 	}
668 
669 	/*
670 	 * If the database is out of date, or there's some other problem,
671 	 * cscope will output error messages before the number-of-lines output.
672 	 * Display/discard any output that doesn't match what we want.
673 	 * Accept "\S*cscope: X lines", also matches "mlcscope".
674 	 * Bail out for the "Unable to search" error.
675 	 */
676 	if (strstr((const char *)buf, "Unable to search database") != NULL)
677 	    break;
678 	if ((stok = strtok(buf, (const char *)" ")) == NULL)
679 	    continue;
680 	if (strstr((const char *)stok, "cscope:") == NULL)
681 	    continue;
682 
683 	if ((stok = strtok(NULL, (const char *)" ")) == NULL)
684 	    continue;
685 	nlines = atoi(stok);
686 	if (nlines < 0)
687 	{
688 	    nlines = 0;
689 	    break;
690 	}
691 
692 	if ((stok = strtok(NULL, (const char *)" ")) == NULL)
693 	    continue;
694 	if (strncmp((const char *)stok, "lines", 5))
695 	    continue;
696 
697 	break;
698     }
699 
700     vim_free(buf);
701     return nlines;
702 }
703 
704 
705 /*
706  * Creates the actual cscope command query from what the user entered.
707  */
708     static char *
cs_create_cmd(char * csoption,char * pattern)709 cs_create_cmd(char *csoption, char *pattern)
710 {
711     char *cmd;
712     short search;
713     char *pat;
714 
715     switch (csoption[0])
716     {
717     case '0' : case 's' :
718 	search = 0;
719 	break;
720     case '1' : case 'g' :
721 	search = 1;
722 	break;
723     case '2' : case 'd' :
724 	search = 2;
725 	break;
726     case '3' : case 'c' :
727 	search = 3;
728 	break;
729     case '4' : case 't' :
730 	search = 4;
731 	break;
732     case '6' : case 'e' :
733 	search = 6;
734 	break;
735     case '7' : case 'f' :
736 	search = 7;
737 	break;
738     case '8' : case 'i' :
739 	search = 8;
740 	break;
741     case '9' : case 'a' :
742 	search = 9;
743 	break;
744     default :
745 	(void)emsg(_("E561: unknown cscope search type"));
746 	cs_usage_msg(Find);
747 	return NULL;
748     }
749 
750     // Skip white space before the patter, except for text and pattern search,
751     // they may want to use the leading white space.
752     pat = pattern;
753     if (search != 4 && search != 6)
754 	while VIM_ISWHITE(*pat)
755 	    ++pat;
756 
757     if ((cmd = alloc(strlen(pat) + 2)) == NULL)
758 	return NULL;
759 
760     (void)sprintf(cmd, "%d%s", search, pat);
761 
762     return cmd;
763 }
764 
765 
766 /*
767  * This piece of code was taken/adapted from nvi.  do we need to add
768  * the BSD license notice?
769  */
770     static int
cs_create_connection(int i)771 cs_create_connection(int i)
772 {
773 #ifdef UNIX
774     int		to_cs[2], from_cs[2];
775 #endif
776     int		len;
777     char	*prog, *cmd, *ppath = NULL;
778 #ifdef MSWIN
779     int		fd;
780     SECURITY_ATTRIBUTES sa;
781     PROCESS_INFORMATION pi;
782     STARTUPINFO si;
783     BOOL	pipe_stdin = FALSE, pipe_stdout = FALSE;
784     HANDLE	stdin_rd, stdout_rd;
785     HANDLE	stdout_wr, stdin_wr;
786     BOOL	created;
787 # if (defined(_MSC_VER) && (_MSC_VER >= 1300)) || defined(__MINGW32__)
788 #  define OPEN_OH_ARGTYPE intptr_t
789 # else
790 #  define OPEN_OH_ARGTYPE long
791 # endif
792 #endif
793 
794 #if defined(UNIX)
795     /*
796      * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from
797      * from_cs[0] and writes to to_cs[1].
798      */
799     to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1;
800     if (pipe(to_cs) < 0 || pipe(from_cs) < 0)
801     {
802 	(void)emsg(_("E566: Could not create cscope pipes"));
803 err_closing:
804 	if (to_cs[0] != -1)
805 	    (void)close(to_cs[0]);
806 	if (to_cs[1] != -1)
807 	    (void)close(to_cs[1]);
808 	if (from_cs[0] != -1)
809 	    (void)close(from_cs[0]);
810 	if (from_cs[1] != -1)
811 	    (void)close(from_cs[1]);
812 	return CSCOPE_FAILURE;
813     }
814 
815     switch (csinfo[i].pid = fork())
816     {
817     case -1:
818 	(void)emsg(_("E622: Could not fork for cscope"));
819 	goto err_closing;
820     case 0:				// child: run cscope.
821 	if (dup2(to_cs[0], STDIN_FILENO) == -1)
822 	    PERROR("cs_create_connection 1");
823 	if (dup2(from_cs[1], STDOUT_FILENO) == -1)
824 	    PERROR("cs_create_connection 2");
825 	if (dup2(from_cs[1], STDERR_FILENO) == -1)
826 	    PERROR("cs_create_connection 3");
827 
828 	// close unused
829 	(void)close(to_cs[1]);
830 	(void)close(from_cs[0]);
831 #else
832 	// MSWIN
833 	// Create pipes to communicate with cscope
834 	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
835 	sa.bInheritHandle = TRUE;
836 	sa.lpSecurityDescriptor = NULL;
837 
838 	if (!(pipe_stdin = CreatePipe(&stdin_rd, &stdin_wr, &sa, 0))
839 		|| !(pipe_stdout = CreatePipe(&stdout_rd, &stdout_wr, &sa, 0)))
840 	{
841 	    (void)emsg(_("E566: Could not create cscope pipes"));
842 err_closing:
843 	    if (pipe_stdin)
844 	    {
845 		CloseHandle(stdin_rd);
846 		CloseHandle(stdin_wr);
847 	    }
848 	    if (pipe_stdout)
849 	    {
850 		CloseHandle(stdout_rd);
851 		CloseHandle(stdout_wr);
852 	    }
853 	    return CSCOPE_FAILURE;
854 	}
855 #endif
856 	// expand the cscope exec for env var's
857 	if ((prog = alloc(MAXPATHL + 1)) == NULL)
858 	{
859 #ifdef UNIX
860 	    return CSCOPE_FAILURE;
861 #else
862 	    // MSWIN
863 	    goto err_closing;
864 #endif
865 	}
866 	expand_env((char_u *)p_csprg, (char_u *)prog, MAXPATHL);
867 
868 	// alloc space to hold the cscope command
869 	len = (int)(strlen(prog) + strlen(csinfo[i].fname) + 32);
870 	if (csinfo[i].ppath)
871 	{
872 	    // expand the prepend path for env var's
873 	    if ((ppath = alloc(MAXPATHL + 1)) == NULL)
874 	    {
875 		vim_free(prog);
876 #ifdef UNIX
877 		return CSCOPE_FAILURE;
878 #else
879 		// MSWIN
880 		goto err_closing;
881 #endif
882 	    }
883 	    expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
884 
885 	    len += (int)strlen(ppath);
886 	}
887 
888 	if (csinfo[i].flags)
889 	    len += (int)strlen(csinfo[i].flags);
890 
891 	if ((cmd = alloc(len)) == NULL)
892 	{
893 	    vim_free(prog);
894 	    vim_free(ppath);
895 #ifdef UNIX
896 	    return CSCOPE_FAILURE;
897 #else
898 	    // MSWIN
899 	    goto err_closing;
900 #endif
901 	}
902 
903 	// run the cscope command; is there execl for non-unix systems?
904 #if defined(UNIX)
905 	(void)sprintf(cmd, "exec %s -dl -f %s", prog, csinfo[i].fname);
906 #else
907 	// MSWIN
908 	(void)sprintf(cmd, "%s -dl -f %s", prog, csinfo[i].fname);
909 #endif
910 	if (csinfo[i].ppath != NULL)
911 	{
912 	    (void)strcat(cmd, " -P");
913 	    (void)strcat(cmd, csinfo[i].ppath);
914 	}
915 	if (csinfo[i].flags != NULL)
916 	{
917 	    (void)strcat(cmd, " ");
918 	    (void)strcat(cmd, csinfo[i].flags);
919 	}
920 # ifdef UNIX
921 	// on Win32 we still need prog
922 	vim_free(prog);
923 # endif
924 	vim_free(ppath);
925 
926 #if defined(UNIX)
927 # if defined(HAVE_SETSID) || defined(HAVE_SETPGID)
928 	// Change our process group to avoid cscope receiving SIGWINCH.
929 #  if defined(HAVE_SETSID)
930 	(void)setsid();
931 #  else
932 	if (setpgid(0, 0) == -1)
933 	    PERROR(_("cs_create_connection setpgid failed"));
934 #  endif
935 # endif
936 	if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1)
937 	    PERROR(_("cs_create_connection exec failed"));
938 
939 	exit(127);
940 	// NOTREACHED
941     default:	// parent.
942 	/*
943 	 * Save the file descriptors for later duplication, and
944 	 * reopen as streams.
945 	 */
946 	if ((csinfo[i].to_fp = fdopen(to_cs[1], "w")) == NULL)
947 	    PERROR(_("cs_create_connection: fdopen for to_fp failed"));
948 	if ((csinfo[i].fr_fp = fdopen(from_cs[0], "r")) == NULL)
949 	    PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
950 
951 	// close unused
952 	(void)close(to_cs[0]);
953 	(void)close(from_cs[1]);
954 
955 	break;
956     }
957 
958 #else
959     // MSWIN
960     // Create a new process to run cscope and use pipes to talk with it
961     GetStartupInfo(&si);
962     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
963     si.wShowWindow = SW_HIDE;  // Hide child application window
964     si.hStdOutput = stdout_wr;
965     si.hStdError  = stdout_wr;
966     si.hStdInput  = stdin_rd;
967     created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
968 							NULL, NULL, &si, &pi);
969     vim_free(prog);
970     vim_free(cmd);
971 
972     if (!created)
973     {
974 	PERROR(_("cs_create_connection exec failed"));
975 	(void)emsg(_("E623: Could not spawn cscope process"));
976 	goto err_closing;
977     }
978     // else
979     csinfo[i].pid = pi.dwProcessId;
980     csinfo[i].hProc = pi.hProcess;
981     CloseHandle(pi.hThread);
982 
983     // TODO - tidy up after failure to create files on pipe handles.
984     if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdin_wr,
985 						      _O_TEXT|_O_APPEND)) < 0)
986 	    || ((csinfo[i].to_fp = _fdopen(fd, "w")) == NULL))
987 	PERROR(_("cs_create_connection: fdopen for to_fp failed"));
988     if (((fd = _open_osfhandle((OPEN_OH_ARGTYPE)stdout_rd,
989 						      _O_TEXT|_O_RDONLY)) < 0)
990 	    || ((csinfo[i].fr_fp = _fdopen(fd, "r")) == NULL))
991 	PERROR(_("cs_create_connection: fdopen for fr_fp failed"));
992 
993     // Close handles for file descriptors inherited by the cscope process
994     CloseHandle(stdin_rd);
995     CloseHandle(stdout_wr);
996 
997 #endif // !UNIX
998 
999     return CSCOPE_SUCCESS;
1000 }
1001 
1002 
1003 /*
1004  * Query cscope using command line interface.  Parse the output and use tselect
1005  * to allow choices.  Like Nvi, creates a pipe to send to/from query/cscope.
1006  *
1007  * returns TRUE if we jump to a tag or abort, FALSE if not.
1008  */
1009     static int
cs_find(exarg_T * eap)1010 cs_find(exarg_T *eap)
1011 {
1012     char *opt, *pat;
1013     int i;
1014 
1015     if (cs_check_for_connections() == FALSE)
1016     {
1017 	(void)emsg(_("E567: no cscope connections"));
1018 	return FALSE;
1019     }
1020 
1021     if ((opt = strtok((char *)NULL, (const char *)" ")) == NULL)
1022     {
1023 	cs_usage_msg(Find);
1024 	return FALSE;
1025     }
1026 
1027     pat = opt + strlen(opt) + 1;
1028     if (pat >= (char *)eap->arg + eap_arg_len)
1029     {
1030 	cs_usage_msg(Find);
1031 	return FALSE;
1032     }
1033 
1034     /*
1035      * Let's replace the NULs written by strtok() with spaces - we need the
1036      * spaces to correctly display the quickfix/location list window's title.
1037      */
1038     for (i = 0; i < eap_arg_len; ++i)
1039 	if (NUL == eap->arg[i])
1040 	    eap->arg[i] = ' ';
1041 
1042     return cs_find_common(opt, pat, eap->forceit, TRUE,
1043 				  eap->cmdidx == CMD_lcscope, *eap->cmdlinep);
1044 }
1045 
1046 
1047 /*
1048  * Common code for cscope find, shared by cs_find() and ex_cstag().
1049  */
1050     static int
cs_find_common(char * opt,char * pat,int forceit,int verbose,int use_ll UNUSED,char_u * cmdline UNUSED)1051 cs_find_common(
1052     char *opt,
1053     char *pat,
1054     int forceit,
1055     int verbose,
1056     int	use_ll UNUSED,
1057     char_u *cmdline UNUSED)
1058 {
1059     int i;
1060     char *cmd;
1061     int *nummatches;
1062     int totmatches;
1063 #ifdef FEAT_QUICKFIX
1064     char cmdletter;
1065     char *qfpos;
1066 
1067     // get cmd letter
1068     switch (opt[0])
1069     {
1070     case '0' :
1071 	cmdletter = 's';
1072 	break;
1073     case '1' :
1074 	cmdletter = 'g';
1075 	break;
1076     case '2' :
1077 	cmdletter = 'd';
1078 	break;
1079     case '3' :
1080 	cmdletter = 'c';
1081 	break;
1082     case '4' :
1083 	cmdletter = 't';
1084 	break;
1085     case '6' :
1086 	cmdletter = 'e';
1087 	break;
1088     case '7' :
1089 	cmdletter = 'f';
1090 	break;
1091     case '8' :
1092 	cmdletter = 'i';
1093 	break;
1094     case '9' :
1095 	cmdletter = 'a';
1096 	break;
1097     default :
1098 	cmdletter = opt[0];
1099     }
1100 
1101     qfpos = (char *)vim_strchr(p_csqf, cmdletter);
1102     if (qfpos != NULL)
1103     {
1104 	qfpos++;
1105 	// next symbol must be + or -
1106 	if (strchr(CSQF_FLAGS, *qfpos) == NULL)
1107 	{
1108 	    (void)semsg(_("E469: invalid cscopequickfix flag %c for %c"), *qfpos, *(qfpos - 1));
1109 	    return FALSE;
1110 	}
1111 
1112 	if (*qfpos != '0'
1113 		&& apply_autocmds(EVENT_QUICKFIXCMDPRE, (char_u *)"cscope",
1114 					       curbuf->b_fname, TRUE, curbuf))
1115 	{
1116 # ifdef FEAT_EVAL
1117 	    if (aborting())
1118 		return FALSE;
1119 # endif
1120 	}
1121     }
1122 #endif
1123 
1124     // create the actual command to send to cscope
1125     cmd = cs_create_cmd(opt, pat);
1126     if (cmd == NULL)
1127 	return FALSE;
1128 
1129     nummatches = ALLOC_MULT(int, csinfo_size);
1130     if (nummatches == NULL)
1131     {
1132 	vim_free(cmd);
1133 	return FALSE;
1134     }
1135 
1136     // Send query to all open connections, then count the total number
1137     // of matches so we can alloc all in one swell foop.
1138     for (i = 0; i < csinfo_size; i++)
1139 	nummatches[i] = 0;
1140     totmatches = 0;
1141     for (i = 0; i < csinfo_size; i++)
1142     {
1143 	if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL)
1144 	    continue;
1145 
1146 	// send cmd to cscope
1147 	(void)fprintf(csinfo[i].to_fp, "%s\n", cmd);
1148 	(void)fflush(csinfo[i].to_fp);
1149 
1150 	nummatches[i] = cs_cnt_matches(i);
1151 
1152 	if (nummatches[i] > -1)
1153 	    totmatches += nummatches[i];
1154 
1155 	if (nummatches[i] == 0)
1156 	    (void)cs_read_prompt(i);
1157     }
1158     vim_free(cmd);
1159 
1160     if (totmatches == 0)
1161     {
1162 	if (verbose)
1163 	    (void)semsg(_("E259: no matches found for cscope query %s of %s"), opt, pat);
1164 	vim_free(nummatches);
1165 	return FALSE;
1166     }
1167 
1168 #ifdef FEAT_QUICKFIX
1169     if (qfpos != NULL && *qfpos != '0' && totmatches > 0)
1170     {
1171 	// fill error list
1172 	FILE	    *f;
1173 	char_u	    *tmp = vim_tempname('c', TRUE);
1174 	qf_info_T   *qi = NULL;
1175 	win_T	    *wp = NULL;
1176 
1177 	f = mch_fopen((char *)tmp, "w");
1178 	if (f == NULL)
1179 	    semsg(_(e_notopen), tmp);
1180 	else
1181 	{
1182 	    cs_file_results(f, nummatches);
1183 	    fclose(f);
1184 	    if (use_ll)	    // Use location list
1185 		wp = curwin;
1186 	    // '-' starts a new error list
1187 	    if (qf_init(wp, tmp, (char_u *)"%f%*\\t%l%*\\t%m",
1188 					  *qfpos == '-', cmdline, NULL) > 0)
1189 	    {
1190 		if (postponed_split != 0)
1191 		{
1192 		    (void)win_split(postponed_split > 0 ? postponed_split : 0,
1193 						       postponed_split_flags);
1194 		    RESET_BINDING(curwin);
1195 		    postponed_split = 0;
1196 		}
1197 
1198 		apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)"cscope",
1199 					       curbuf->b_fname, TRUE, curbuf);
1200 		if (use_ll)
1201 		    /*
1202 		     * In the location list window, use the displayed location
1203 		     * list. Otherwise, use the location list for the window.
1204 		     */
1205 		    qi = (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL)
1206 			?  wp->w_llist_ref : wp->w_llist;
1207 		qf_jump(qi, 0, 0, forceit);
1208 	    }
1209 	}
1210 	mch_remove(tmp);
1211 	vim_free(tmp);
1212 	vim_free(nummatches);
1213 	return TRUE;
1214     }
1215     else
1216 #endif // FEAT_QUICKFIX
1217     {
1218 	char **matches = NULL, **contexts = NULL;
1219 	int matched = 0;
1220 
1221 	// read output
1222 	cs_fill_results(pat, totmatches, nummatches, &matches,
1223 							 &contexts, &matched);
1224 	vim_free(nummatches);
1225 	if (matches == NULL)
1226 	    return FALSE;
1227 
1228 	(void)cs_manage_matches(matches, contexts, matched, Store);
1229 
1230 	return do_tag((char_u *)pat, DT_CSCOPE, 0, forceit, verbose);
1231     }
1232 
1233 }
1234 
1235 /*
1236  * Print help.
1237  */
1238     static int
cs_help(exarg_T * eap UNUSED)1239 cs_help(exarg_T *eap UNUSED)
1240 {
1241     cscmd_T *cmdp = cs_cmds;
1242 
1243     (void)msg_puts(_("cscope commands:\n"));
1244     while (cmdp->name != NULL)
1245     {
1246 	char *help = _(cmdp->help);
1247 	int  space_cnt = 30 - vim_strsize((char_u *)help);
1248 
1249 	// Use %*s rather than %30s to ensure proper alignment in utf-8
1250 	if (space_cnt < 0)
1251 	    space_cnt = 0;
1252 	(void)smsg(_("%-5s: %s%*s (Usage: %s)"),
1253 				      cmdp->name,
1254 				      help, space_cnt, " ",
1255 				      cmdp->usage);
1256 	if (strcmp(cmdp->name, "find") == 0)
1257 	    msg_puts(_("\n"
1258 		       "       a: Find assignments to this symbol\n"
1259 		       "       c: Find functions calling this function\n"
1260 		       "       d: Find functions called by this function\n"
1261 		       "       e: Find this egrep pattern\n"
1262 		       "       f: Find this file\n"
1263 		       "       g: Find this definition\n"
1264 		       "       i: Find files #including this file\n"
1265 		       "       s: Find this C symbol\n"
1266 		       "       t: Find this text string\n"));
1267 
1268 	cmdp++;
1269     }
1270 
1271     wait_return(TRUE);
1272     return 0;
1273 }
1274 
1275 
1276     static void
clear_csinfo(int i)1277 clear_csinfo(int i)
1278 {
1279     csinfo[i].fname  = NULL;
1280     csinfo[i].ppath  = NULL;
1281     csinfo[i].flags  = NULL;
1282 #if defined(UNIX)
1283     csinfo[i].st_dev = (dev_t)0;
1284     csinfo[i].st_ino = (ino_t)0;
1285 #else
1286     csinfo[i].nVolume = 0;
1287     csinfo[i].nIndexHigh = 0;
1288     csinfo[i].nIndexLow = 0;
1289 #endif
1290     csinfo[i].pid    = 0;
1291     csinfo[i].fr_fp  = NULL;
1292     csinfo[i].to_fp  = NULL;
1293 #if defined(MSWIN)
1294     csinfo[i].hProc = NULL;
1295 #endif
1296 }
1297 
1298 /*
1299  * Insert a new cscope database filename into the filelist.
1300  */
1301     static int
cs_insert_filelist(char * fname,char * ppath,char * flags,stat_T * sb UNUSED)1302 cs_insert_filelist(
1303     char *fname,
1304     char *ppath,
1305     char *flags,
1306     stat_T *sb UNUSED)
1307 {
1308     short	i, j;
1309 #ifndef UNIX
1310     BY_HANDLE_FILE_INFORMATION bhfi;
1311 
1312     switch (win32_fileinfo((char_u *)fname, &bhfi))
1313     {
1314 	case FILEINFO_ENC_FAIL:		// enc_to_utf16() failed
1315 	case FILEINFO_READ_FAIL:	// CreateFile() failed
1316 	    if (p_csverbose)
1317 	    {
1318 		char *cant_msg = _("E625: cannot open cscope database: %s");
1319 		char *winmsg = GetWin32Error();
1320 
1321 		if (winmsg != NULL)
1322 		{
1323 		    (void)semsg(cant_msg, winmsg);
1324 		    LocalFree(winmsg);
1325 		}
1326 		else
1327 		    // subst filename if can't get error text
1328 		    (void)semsg(cant_msg, fname);
1329 	    }
1330 	    return -1;
1331 
1332 	case FILEINFO_INFO_FAIL:    // GetFileInformationByHandle() failed
1333 	    if (p_csverbose)
1334 		(void)emsg(_("E626: cannot get cscope database information"));
1335 	    return -1;
1336     }
1337 #endif
1338 
1339     i = -1; // can be set to the index of an empty item in csinfo
1340     for (j = 0; j < csinfo_size; j++)
1341     {
1342 	if (csinfo[j].fname != NULL
1343 #if defined(UNIX)
1344 	    && csinfo[j].st_dev == sb->st_dev && csinfo[j].st_ino == sb->st_ino
1345 #else
1346 	    // compare pathnames first
1347 	    && ((fullpathcmp((char_u *)csinfo[j].fname,
1348 			(char_u *)fname, FALSE, TRUE) & FPC_SAME)
1349 		// test index file attributes too
1350 		|| (csinfo[j].nVolume == bhfi.dwVolumeSerialNumber
1351 		    && csinfo[j].nIndexHigh == bhfi.nFileIndexHigh
1352 		    && csinfo[j].nIndexLow == bhfi.nFileIndexLow))
1353 #endif
1354 	    )
1355 	{
1356 	    if (p_csverbose)
1357 		(void)emsg(_("E568: duplicate cscope database not added"));
1358 	    return -1;
1359 	}
1360 
1361 	if (csinfo[j].fname == NULL && i == -1)
1362 	    i = j; // remember first empty entry
1363     }
1364 
1365     if (i == -1)
1366     {
1367 	i = csinfo_size;
1368 	if (csinfo_size == 0)
1369 	{
1370 	    // First time allocation: allocate only 1 connection. It should
1371 	    // be enough for most users.  If more is needed, csinfo will be
1372 	    // reallocated.
1373 	    csinfo_size = 1;
1374 	    csinfo = ALLOC_CLEAR_ONE(csinfo_T);
1375 	}
1376 	else
1377 	{
1378 	    csinfo_T *t_csinfo = csinfo;
1379 
1380 	    // Reallocate space for more connections.
1381 	    csinfo_size *= 2;
1382 	    csinfo = vim_realloc(csinfo, sizeof(csinfo_T)*csinfo_size);
1383 	    if (csinfo == NULL)
1384 	    {
1385 		vim_free(t_csinfo);
1386 		csinfo_size = 0;
1387 	    }
1388 	}
1389 	if (csinfo == NULL)
1390 	    return -1;
1391 	for (j = csinfo_size/2; j < csinfo_size; j++)
1392 	    clear_csinfo(j);
1393     }
1394 
1395     if ((csinfo[i].fname = alloc(strlen(fname)+1)) == NULL)
1396 	return -1;
1397 
1398     (void)strcpy(csinfo[i].fname, (const char *)fname);
1399 
1400     if (ppath != NULL)
1401     {
1402 	if ((csinfo[i].ppath = alloc(strlen(ppath) + 1)) == NULL)
1403 	{
1404 	    VIM_CLEAR(csinfo[i].fname);
1405 	    return -1;
1406 	}
1407 	(void)strcpy(csinfo[i].ppath, (const char *)ppath);
1408     } else
1409 	csinfo[i].ppath = NULL;
1410 
1411     if (flags != NULL)
1412     {
1413 	if ((csinfo[i].flags = alloc(strlen(flags) + 1)) == NULL)
1414 	{
1415 	    VIM_CLEAR(csinfo[i].fname);
1416 	    VIM_CLEAR(csinfo[i].ppath);
1417 	    return -1;
1418 	}
1419 	(void)strcpy(csinfo[i].flags, (const char *)flags);
1420     } else
1421 	csinfo[i].flags = NULL;
1422 
1423 #if defined(UNIX)
1424     csinfo[i].st_dev = sb->st_dev;
1425     csinfo[i].st_ino = sb->st_ino;
1426 
1427 #else
1428     csinfo[i].nVolume = bhfi.dwVolumeSerialNumber;
1429     csinfo[i].nIndexLow = bhfi.nFileIndexLow;
1430     csinfo[i].nIndexHigh = bhfi.nFileIndexHigh;
1431 #endif
1432     return i;
1433 }
1434 
1435 
1436 /*
1437  * Find cscope command in command table.
1438  */
1439     static cscmd_T *
cs_lookup_cmd(exarg_T * eap)1440 cs_lookup_cmd(exarg_T *eap)
1441 {
1442     cscmd_T *cmdp;
1443     char *stok;
1444     size_t len;
1445 
1446     if (eap->arg == NULL)
1447 	return NULL;
1448 
1449     // Store length of eap->arg before it gets modified by strtok().
1450     eap_arg_len = (int)STRLEN(eap->arg);
1451 
1452     if ((stok = strtok((char *)(eap->arg), (const char *)" ")) == NULL)
1453 	return NULL;
1454 
1455     len = strlen(stok);
1456     for (cmdp = cs_cmds; cmdp->name != NULL; ++cmdp)
1457     {
1458 	if (strncmp((const char *)(stok), cmdp->name, len) == 0)
1459 	    return (cmdp);
1460     }
1461     return NULL;
1462 }
1463 
1464 
1465 /*
1466  * Nuke em.
1467  */
1468     static int
cs_kill(exarg_T * eap UNUSED)1469 cs_kill(exarg_T *eap UNUSED)
1470 {
1471     char *stok;
1472     short i;
1473 
1474     if ((stok = strtok((char *)NULL, (const char *)" ")) == NULL)
1475     {
1476 	cs_usage_msg(Kill);
1477 	return CSCOPE_FAILURE;
1478     }
1479 
1480     // only single digit positive and negative integers are allowed
1481     if ((strlen(stok) < 2 && VIM_ISDIGIT((int)(stok[0])))
1482 	    || (strlen(stok) < 3 && stok[0] == '-'
1483 					      && VIM_ISDIGIT((int)(stok[1]))))
1484 	i = atoi(stok);
1485     else
1486     {
1487 	// It must be part of a name.  We will try to find a match
1488 	// within all the names in the csinfo data structure
1489 	for (i = 0; i < csinfo_size; i++)
1490 	{
1491 	    if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok))
1492 		break;
1493 	}
1494     }
1495 
1496     if ((i != -1) && (i >= csinfo_size || i < -1 || csinfo[i].fname == NULL))
1497     {
1498 	if (p_csverbose)
1499 	    (void)semsg(_("E261: cscope connection %s not found"), stok);
1500     }
1501     else
1502     {
1503 	if (i == -1)
1504 	{
1505 	    for (i = 0; i < csinfo_size; i++)
1506 	    {
1507 		if (csinfo[i].fname)
1508 		    cs_kill_execute(i, csinfo[i].fname);
1509 	    }
1510 	}
1511 	else
1512 	    cs_kill_execute(i, stok);
1513     }
1514 
1515     return 0;
1516 }
1517 
1518 
1519 /*
1520  * Actually kills a specific cscope connection.
1521  */
1522     static void
cs_kill_execute(int i,char * cname)1523 cs_kill_execute(
1524     int i,	    // cscope table index
1525     char *cname)    // cscope database name
1526 {
1527     if (p_csverbose)
1528     {
1529 	msg_clr_eos();
1530 	(void)smsg_attr(HL_ATTR(HLF_R) | MSG_HIST,
1531 			_("cscope connection %s closed"), cname);
1532     }
1533     cs_release_csp(i, TRUE);
1534 }
1535 
1536 
1537 /*
1538  * Convert the cscope output into a ctags style entry (as might be found
1539  * in a ctags tags file).  there's one catch though: cscope doesn't tell you
1540  * the type of the tag you are looking for.  for example, in Darren Hiebert's
1541  * ctags (the one that comes with vim), #define's use a line number to find the
1542  * tag in a file while function definitions use a regexp search pattern.
1543  *
1544  * I'm going to always use the line number because cscope does something
1545  * quirky (and probably other things i don't know about):
1546  *
1547  *     if you have "#  define" in your source file, which is
1548  *     perfectly legal, cscope thinks you have "#define".  this
1549  *     will result in a failed regexp search. :(
1550  *
1551  * Besides, even if this particular case didn't happen, the search pattern
1552  * would still have to be modified to escape all the special regular expression
1553  * characters to comply with ctags formatting.
1554  */
1555     static char *
cs_make_vim_style_matches(char * fname,char * slno,char * search,char * tagstr)1556 cs_make_vim_style_matches(
1557     char *fname,
1558     char *slno,
1559     char *search,
1560     char *tagstr)
1561 {
1562     // vim style is ctags:
1563     //
1564     //	    <tagstr>\t<filename>\t<linenum_or_search>"\t<extra>
1565     //
1566     // but as mentioned above, we'll always use the line number and
1567     // put the search pattern (if one exists) as "extra"
1568     //
1569     // buf is used as part of vim's method of handling tags, and
1570     // (i think) vim frees it when you pop your tags and get replaced
1571     // by new ones on the tag stack.
1572     char *buf;
1573     int amt;
1574 
1575     if (search != NULL)
1576     {
1577 	amt = (int)(strlen(fname) + strlen(slno) + strlen(tagstr) + strlen(search)+6);
1578 	if ((buf = alloc(amt)) == NULL)
1579 	    return NULL;
1580 
1581 	(void)sprintf(buf, "%s\t%s\t%s;\"\t%s", tagstr, fname, slno, search);
1582     }
1583     else
1584     {
1585 	amt = (int)(strlen(fname) + strlen(slno) + strlen(tagstr) + 5);
1586 	if ((buf = alloc(amt)) == NULL)
1587 	    return NULL;
1588 
1589 	(void)sprintf(buf, "%s\t%s\t%s;\"", tagstr, fname, slno);
1590     }
1591 
1592     return buf;
1593 }
1594 
1595 
1596 /*
1597  * This is kind of hokey, but i don't see an easy way round this.
1598  *
1599  * Store: keep a ptr to the (malloc'd) memory of matches originally
1600  * generated from cs_find().  the matches are originally lines directly
1601  * from cscope output, but transformed to look like something out of a
1602  * ctags.  see cs_make_vim_style_matches for more details.
1603  *
1604  * Get: used only from cs_fgets(), this simulates a vim_fgets() to return
1605  * the next line from the cscope output.  it basically keeps track of which
1606  * lines have been "used" and returns the next one.
1607  *
1608  * Free: frees up everything and resets
1609  *
1610  * Print: prints the tags
1611  */
1612     static char *
cs_manage_matches(char ** matches,char ** contexts,int totmatches,mcmd_e cmd)1613 cs_manage_matches(
1614     char **matches,
1615     char **contexts,
1616     int totmatches,
1617     mcmd_e cmd)
1618 {
1619     static char **mp = NULL;
1620     static char **cp = NULL;
1621     static int cnt = -1;
1622     static int next = -1;
1623     char *p = NULL;
1624 
1625     switch (cmd)
1626     {
1627     case Store:
1628 	assert(matches != NULL);
1629 	assert(totmatches > 0);
1630 	if (mp != NULL || cp != NULL)
1631 	    (void)cs_manage_matches(NULL, NULL, -1, Free);
1632 	mp = matches;
1633 	cp = contexts;
1634 	cnt = totmatches;
1635 	next = 0;
1636 	break;
1637     case Get:
1638 	if (next >= cnt)
1639 	    return NULL;
1640 
1641 	p = mp[next];
1642 	next++;
1643 	break;
1644     case Free:
1645 	if (mp != NULL)
1646 	{
1647 	    if (cnt > 0)
1648 		while (cnt--)
1649 		{
1650 		    vim_free(mp[cnt]);
1651 		    if (cp != NULL)
1652 			vim_free(cp[cnt]);
1653 		}
1654 	    vim_free(mp);
1655 	    vim_free(cp);
1656 	}
1657 	mp = NULL;
1658 	cp = NULL;
1659 	cnt = 0;
1660 	next = 0;
1661 	break;
1662     case Print:
1663 	cs_print_tags_priv(mp, cp, cnt);
1664 	break;
1665     default:	// should not reach here
1666 	iemsg(_("E570: fatal error in cs_manage_matches"));
1667 	return NULL;
1668     }
1669 
1670     return p;
1671 }
1672 
1673 
1674 /*
1675  * Parse cscope output.
1676  */
1677     static char *
cs_parse_results(int cnumber,char * buf,int bufsize,char ** context,char ** linenumber,char ** search)1678 cs_parse_results(
1679     int cnumber,
1680     char *buf,
1681     int bufsize,
1682     char **context,
1683     char **linenumber,
1684     char **search)
1685 {
1686     int ch;
1687     char *p;
1688     char *name;
1689 
1690     if (fgets(buf, bufsize, csinfo[cnumber].fr_fp) == NULL)
1691     {
1692 	if (feof(csinfo[cnumber].fr_fp))
1693 	    errno = EIO;
1694 
1695 	cs_reading_emsg(cnumber);
1696 
1697 	return NULL;
1698     }
1699 
1700     // If the line's too long for the buffer, discard it.
1701     if ((p = strchr(buf, '\n')) == NULL)
1702     {
1703 	while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n')
1704 	    ;
1705 	return NULL;
1706     }
1707     *p = '\0';
1708 
1709     /*
1710      * cscope output is in the following format:
1711      *
1712      *	<filename> <context> <line number> <pattern>
1713      */
1714     if ((name = strtok(buf, (const char *)" ")) == NULL)
1715 	return NULL;
1716     if ((*context = strtok(NULL, (const char *)" ")) == NULL)
1717 	return NULL;
1718     if ((*linenumber = strtok(NULL, (const char *)" ")) == NULL)
1719 	return NULL;
1720     *search = *linenumber + strlen(*linenumber) + 1;	// +1 to skip \0
1721 
1722     // --- nvi ---
1723     // If the file is older than the cscope database, that is,
1724     // the database was built since the file was last modified,
1725     // or there wasn't a search string, use the line number.
1726     if (strcmp(*search, "<unknown>") == 0)
1727 	*search = NULL;
1728 
1729     name = cs_resolve_file(cnumber, name);
1730     return name;
1731 }
1732 
1733 #ifdef FEAT_QUICKFIX
1734 /*
1735  * Write cscope find results to file.
1736  */
1737     static void
cs_file_results(FILE * f,int * nummatches_a)1738 cs_file_results(FILE *f, int *nummatches_a)
1739 {
1740     int i, j;
1741     char *buf;
1742     char *search, *slno;
1743     char *fullname;
1744     char *cntx;
1745     char *context;
1746 
1747     buf = alloc(CSREAD_BUFSIZE);
1748     if (buf == NULL)
1749 	return;
1750 
1751     for (i = 0; i < csinfo_size; i++)
1752     {
1753 	if (nummatches_a[i] < 1)
1754 	    continue;
1755 
1756 	for (j = 0; j < nummatches_a[i]; j++)
1757 	{
1758 	   if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
1759 			   &slno, &search)) == NULL)
1760 	       continue;
1761 
1762 	   context = alloc(strlen(cntx)+5);
1763 	   if (context == NULL)
1764 	   {
1765 	       vim_free(fullname);
1766 	       continue;
1767 	   }
1768 
1769 	   if (strcmp(cntx, "<global>")==0)
1770 	       strcpy(context, "<<global>>");
1771 	   else
1772 	       sprintf(context, "<<%s>>", cntx);
1773 
1774 	   if (search == NULL)
1775 	       fprintf(f, "%s\t%s\t%s\n", fullname, slno, context);
1776 	   else
1777 	       fprintf(f, "%s\t%s\t%s %s\n", fullname, slno, context, search);
1778 
1779 	   vim_free(context);
1780 	   vim_free(fullname);
1781 	} // for all matches
1782 
1783 	(void)cs_read_prompt(i);
1784 
1785     } // for all cscope connections
1786     vim_free(buf);
1787 }
1788 #endif
1789 
1790 /*
1791  * Get parsed cscope output and calls cs_make_vim_style_matches to convert
1792  * into ctags format.
1793  * When there are no matches sets "*matches_p" to NULL.
1794  */
1795     static void
cs_fill_results(char * tagstr,int totmatches,int * nummatches_a,char *** matches_p,char *** cntxts_p,int * matched)1796 cs_fill_results(
1797     char *tagstr,
1798     int totmatches,
1799     int *nummatches_a,
1800     char ***matches_p,
1801     char ***cntxts_p,
1802     int *matched)
1803 {
1804     int i, j;
1805     char *buf;
1806     char *search, *slno;
1807     int totsofar = 0;
1808     char **matches = NULL;
1809     char **cntxts = NULL;
1810     char *fullname;
1811     char *cntx;
1812 
1813     assert(totmatches > 0);
1814 
1815     buf = alloc(CSREAD_BUFSIZE);
1816     if (buf == NULL)
1817 	return;
1818 
1819     if ((matches = ALLOC_MULT(char *, totmatches)) == NULL)
1820 	goto parse_out;
1821     if ((cntxts = ALLOC_MULT(char *, totmatches)) == NULL)
1822 	goto parse_out;
1823 
1824     for (i = 0; i < csinfo_size; i++)
1825     {
1826 	if (nummatches_a[i] < 1)
1827 	    continue;
1828 
1829 	for (j = 0; j < nummatches_a[i]; j++)
1830 	{
1831 	   if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx,
1832 			   &slno, &search)) == NULL)
1833 		continue;
1834 
1835 	    matches[totsofar] = cs_make_vim_style_matches(fullname, slno,
1836 							  search, tagstr);
1837 
1838 	    vim_free(fullname);
1839 
1840 	    if (strcmp(cntx, "<global>") == 0)
1841 		cntxts[totsofar] = NULL;
1842 	    else
1843 		// note: if vim_strsave returns NULL, then the context
1844 		// will be "<global>", which is misleading.
1845 		cntxts[totsofar] = (char *)vim_strsave((char_u *)cntx);
1846 
1847 	    if (matches[totsofar] != NULL)
1848 		totsofar++;
1849 
1850 	} // for all matches
1851 
1852 	(void)cs_read_prompt(i);
1853 
1854     } // for all cscope connections
1855 
1856 parse_out:
1857     if (totsofar == 0)
1858     {
1859 	// No matches, free the arrays and return NULL in "*matches_p".
1860 	VIM_CLEAR(matches);
1861 	VIM_CLEAR(cntxts);
1862     }
1863     *matched = totsofar;
1864     *matches_p = matches;
1865     *cntxts_p = cntxts;
1866 
1867     vim_free(buf);
1868 }
1869 
1870 
1871 /*
1872  * get the requested path components
1873  */
1874     static char *
cs_pathcomponents(char * path)1875 cs_pathcomponents(char *path)
1876 {
1877     int		i;
1878     char	*s;
1879 
1880     if (p_cspc == 0)
1881 	return path;
1882 
1883     s = path + strlen(path) - 1;
1884     for (i = 0; i < p_cspc; ++i)
1885 	while (s > path && *--s != '/'
1886 #ifdef MSWIN
1887 		&& *--s != '\\'
1888 #endif
1889 		)
1890 	    ;
1891     if ((s > path && *s == '/')
1892 #ifdef MSWIN
1893 	|| (s > path && *s == '\\')
1894 #endif
1895 	    )
1896 	++s;
1897     return s;
1898 }
1899 
1900 /*
1901  * Called from cs_manage_matches().
1902  */
1903     static void
cs_print_tags_priv(char ** matches,char ** cntxts,int num_matches)1904 cs_print_tags_priv(char **matches, char **cntxts, int num_matches)
1905 {
1906     char	*buf = NULL;
1907     char	*t_buf;
1908     int		bufsize = 0; // Track available bufsize
1909     int		newsize = 0;
1910     char	*ptag;
1911     char	*fname, *lno, *extra, *tbuf;
1912     int		i, idx, num;
1913     char	*globalcntx = "GLOBAL";
1914     char	*cntxformat = " <<%s>>";
1915     char	*context;
1916     char	*cstag_msg = _("Cscope tag: %s");
1917     char	*csfmt_str = "%4d %6s  ";
1918 
1919     assert(num_matches > 0);
1920 
1921     if ((tbuf = alloc(strlen(matches[0]) + 1)) == NULL)
1922 	return;
1923 
1924     strcpy(tbuf, matches[0]);
1925     ptag = strtok(tbuf, "\t");
1926     if (ptag == NULL)
1927     {
1928 	vim_free(tbuf);
1929 	return;
1930     }
1931 
1932     newsize = (int)(strlen(cstag_msg) + strlen(ptag));
1933     buf = alloc(newsize);
1934     if (buf != NULL)
1935     {
1936 	bufsize = newsize;
1937 	(void)sprintf(buf, cstag_msg, ptag);
1938 	msg_puts_attr(buf, HL_ATTR(HLF_T));
1939     }
1940 
1941     vim_free(tbuf);
1942 
1943     msg_puts_attr(_("\n   #   line"), HL_ATTR(HLF_T));    // strlen is 7
1944     msg_advance(msg_col + 2);
1945     msg_puts_attr(_("filename / context / line\n"), HL_ATTR(HLF_T));
1946 
1947     num = 1;
1948     for (i = 0; i < num_matches; i++)
1949     {
1950 	idx = i;
1951 
1952 	// if we really wanted to, we could avoid this malloc and strcpy
1953 	// by parsing matches[i] on the fly and placing stuff into buf
1954 	// directly, but that's too much of a hassle
1955 	if ((tbuf = alloc(strlen(matches[idx]) + 1)) == NULL)
1956 	    continue;
1957 	(void)strcpy(tbuf, matches[idx]);
1958 
1959 	if (strtok(tbuf, (const char *)"\t") == NULL
1960 		|| (fname = strtok(NULL, (const char *)"\t")) == NULL
1961 		|| (lno = strtok(NULL, (const char *)"\t")) == NULL)
1962 	{
1963 	    vim_free(tbuf);
1964 	    continue;
1965 	}
1966 	extra = strtok(NULL, (const char *)"\t");
1967 
1968 	lno[strlen(lno)-2] = '\0';  // ignore ;" at the end
1969 
1970 	// hopefully 'num' (num of matches) will be less than 10^16
1971 	newsize = (int)(strlen(csfmt_str) + 16 + strlen(lno));
1972 	if (bufsize < newsize)
1973 	{
1974 	    t_buf = buf;
1975 	    buf = vim_realloc(buf, newsize);
1976 	    if (buf == NULL)
1977 	    {
1978 		bufsize = 0;
1979 		vim_free(t_buf);
1980 	    }
1981 	    else
1982 		bufsize = newsize;
1983 	}
1984 	if (buf != NULL)
1985 	{
1986 	    // csfmt_str = "%4d %6s  ";
1987 	    (void)sprintf(buf, csfmt_str, num, lno);
1988 	    msg_puts_attr(buf, HL_ATTR(HLF_CM));
1989 	}
1990 	msg_outtrans_long_attr((char_u *)cs_pathcomponents(fname),
1991 							      HL_ATTR(HLF_CM));
1992 
1993 	// compute the required space for the context
1994 	if (cntxts[idx] != NULL)
1995 	    context = cntxts[idx];
1996 	else
1997 	    context = globalcntx;
1998 	newsize = (int)(strlen(context) + strlen(cntxformat));
1999 
2000 	if (bufsize < newsize)
2001 	{
2002 	    t_buf = buf;
2003 	    buf = vim_realloc(buf, newsize);
2004 	    if (buf == NULL)
2005 	    {
2006 		bufsize = 0;
2007 		vim_free(t_buf);
2008 	    }
2009 	    else
2010 		bufsize = newsize;
2011 	}
2012 	if (buf != NULL)
2013 	{
2014 	    (void)sprintf(buf, cntxformat, context);
2015 
2016 	    // print the context only if it fits on the same line
2017 	    if (msg_col + (int)strlen(buf) >= (int)Columns)
2018 		msg_putchar('\n');
2019 	    msg_advance(12);
2020 	    msg_outtrans_long_attr((char_u *)buf, 0);
2021 	    msg_putchar('\n');
2022 	}
2023 	if (extra != NULL)
2024 	{
2025 	    msg_advance(13);
2026 	    msg_outtrans_long_attr((char_u *)extra, 0);
2027 	}
2028 
2029 	vim_free(tbuf); // only after printing extra due to strtok use
2030 
2031 	if (msg_col)
2032 	    msg_putchar('\n');
2033 
2034 	ui_breakcheck();
2035 	if (got_int)
2036 	{
2037 	    got_int = FALSE;	// don't print any more matches
2038 	    break;
2039 	}
2040 
2041 	num++;
2042     } // for all matches
2043 
2044     vim_free(buf);
2045 }
2046 
2047 
2048 /*
2049  * Read a cscope prompt (basically, skip over the ">> ").
2050  */
2051     static int
cs_read_prompt(int i)2052 cs_read_prompt(int i)
2053 {
2054     int		ch;
2055     char	*buf = NULL; // buffer for possible error message from cscope
2056     int		bufpos = 0;
2057     char	*cs_emsg;
2058     int		maxlen;
2059     static char *eprompt = "Press the RETURN key to continue:";
2060     int		epromptlen = (int)strlen(eprompt);
2061     int		n;
2062 
2063     cs_emsg = _("E609: Cscope error: %s");
2064     // compute maximum allowed len for Cscope error message
2065     maxlen = (int)(IOSIZE - strlen(cs_emsg));
2066 
2067     for (;;)
2068     {
2069 	while ((ch = getc(csinfo[i].fr_fp)) != EOF && ch != CSCOPE_PROMPT[0])
2070 	    // if there is room and char is printable
2071 	    if (bufpos < maxlen - 1 && vim_isprintc(ch))
2072 	    {
2073 		if (buf == NULL) // lazy buffer allocation
2074 		    buf = alloc(maxlen);
2075 		if (buf != NULL)
2076 		{
2077 		    // append character to the message
2078 		    buf[bufpos++] = ch;
2079 		    buf[bufpos] = NUL;
2080 		    if (bufpos >= epromptlen
2081 			    && strcmp(&buf[bufpos - epromptlen], eprompt) == 0)
2082 		    {
2083 			// remove eprompt from buf
2084 			buf[bufpos - epromptlen] = NUL;
2085 
2086 			// print message to user
2087 			(void)semsg(cs_emsg, buf);
2088 
2089 			// send RETURN to cscope
2090 			(void)putc('\n', csinfo[i].to_fp);
2091 			(void)fflush(csinfo[i].to_fp);
2092 
2093 			// clear buf
2094 			bufpos = 0;
2095 			buf[bufpos] = NUL;
2096 		    }
2097 		}
2098 	    }
2099 
2100 	for (n = 0; n < (int)strlen(CSCOPE_PROMPT); ++n)
2101 	{
2102 	    if (n > 0)
2103 		ch = getc(csinfo[i].fr_fp);
2104 	    if (ch == EOF)
2105 	    {
2106 		if (buf != NULL && buf[0] != NUL)
2107 		    (void)semsg(cs_emsg, buf);
2108 		else if (p_csverbose)
2109 		    cs_reading_emsg(i); // don't have additional information
2110 		cs_release_csp(i, TRUE);
2111 		vim_free(buf);
2112 		return CSCOPE_FAILURE;
2113 	    }
2114 
2115 	    if (ch != CSCOPE_PROMPT[n])
2116 	    {
2117 		ch = EOF;
2118 		break;
2119 	    }
2120 	}
2121 
2122 	if (ch == EOF)
2123 	    continue;	    // didn't find the prompt
2124 	break;		    // did find the prompt
2125     }
2126 
2127     vim_free(buf);
2128     return CSCOPE_SUCCESS;
2129 }
2130 
2131 #if defined(UNIX) && defined(SIGALRM)
2132 /*
2133  * Used to catch and ignore SIGALRM below.
2134  */
2135     static RETSIGTYPE
SIGDEFARG(sigarg)2136 sig_handler SIGDEFARG(sigarg)
2137 {
2138     // do nothing
2139     SIGRETURN;
2140 }
2141 #endif
2142 
2143 /*
2144  * Does the actual free'ing for the cs ptr with an optional flag of whether
2145  * or not to free the filename.  Called by cs_kill and cs_reset.
2146  */
2147     static void
cs_release_csp(int i,int freefnpp)2148 cs_release_csp(int i, int freefnpp)
2149 {
2150     /*
2151      * Trying to exit normally (not sure whether it is fit to UNIX cscope
2152      */
2153     if (csinfo[i].to_fp != NULL)
2154     {
2155 	(void)fputs("q\n", csinfo[i].to_fp);
2156 	(void)fflush(csinfo[i].to_fp);
2157     }
2158 #if defined(UNIX)
2159     {
2160 	int waitpid_errno;
2161 	int pstat;
2162 	pid_t pid;
2163 
2164 # if defined(HAVE_SIGACTION)
2165 	struct sigaction sa, old;
2166 
2167 	// Use sigaction() to limit the waiting time to two seconds.
2168 	sigemptyset(&sa.sa_mask);
2169 	sa.sa_handler = sig_handler;
2170 #  ifdef SA_NODEFER
2171 	sa.sa_flags = SA_NODEFER;
2172 #  else
2173 	sa.sa_flags = 0;
2174 #  endif
2175 	sigaction(SIGALRM, &sa, &old);
2176 	alarm(2); // 2 sec timeout
2177 
2178 	// Block until cscope exits or until timer expires
2179 	pid = waitpid(csinfo[i].pid, &pstat, 0);
2180 	waitpid_errno = errno;
2181 
2182 	// cancel pending alarm if still there and restore signal
2183 	alarm(0);
2184 	sigaction(SIGALRM, &old, NULL);
2185 # else
2186 	int waited;
2187 
2188 	// Can't use sigaction(), loop for two seconds.  First yield the CPU
2189 	// to give cscope a chance to exit quickly.
2190 	sleep(0);
2191 	for (waited = 0; waited < 40; ++waited)
2192 	{
2193 	    pid = waitpid(csinfo[i].pid, &pstat, WNOHANG);
2194 	    waitpid_errno = errno;
2195 	    if (pid != 0)
2196 		break;  // break unless the process is still running
2197 	    mch_delay(50L, 0); // sleep 50 ms
2198 	}
2199 # endif
2200 	/*
2201 	 * If the cscope process is still running: kill it.
2202 	 * Safety check: If the PID would be zero here, the entire X session
2203 	 * would be killed.  -1 and 1 are dangerous as well.
2204 	 */
2205 	if (pid < 0 && csinfo[i].pid > 1)
2206 	{
2207 # ifdef ECHILD
2208 	    int alive = TRUE;
2209 
2210 	    if (waitpid_errno == ECHILD)
2211 	    {
2212 		/*
2213 		 * When using 'vim -g', vim is forked and cscope process is
2214 		 * no longer a child process but a sibling.  So waitpid()
2215 		 * fails with errno being ECHILD (No child processes).
2216 		 * Don't send SIGKILL to cscope immediately but wait
2217 		 * (polling) for it to exit normally as result of sending
2218 		 * the "q" command, hence giving it a chance to clean up
2219 		 * its temporary files.
2220 		 */
2221 		int waited;
2222 
2223 		sleep(0);
2224 		for (waited = 0; waited < 40; ++waited)
2225 		{
2226 		    // Check whether cscope process is still alive
2227 		    if (kill(csinfo[i].pid, 0) != 0)
2228 		    {
2229 			alive = FALSE; // cscope process no longer exists
2230 			break;
2231 		    }
2232 		    mch_delay(50L, 0); // sleep 50 ms
2233 		}
2234 	    }
2235 	    if (alive)
2236 # endif
2237 	    {
2238 		kill(csinfo[i].pid, SIGKILL);
2239 		(void)waitpid(csinfo[i].pid, &pstat, 0);
2240 	    }
2241 	}
2242     }
2243 #else  // !UNIX
2244     if (csinfo[i].hProc != NULL)
2245     {
2246 	// Give cscope a chance to exit normally
2247 	if (WaitForSingleObject(csinfo[i].hProc, 1000) == WAIT_TIMEOUT)
2248 	    TerminateProcess(csinfo[i].hProc, 0);
2249 	CloseHandle(csinfo[i].hProc);
2250     }
2251 #endif
2252 
2253     if (csinfo[i].fr_fp != NULL)
2254 	(void)fclose(csinfo[i].fr_fp);
2255     if (csinfo[i].to_fp != NULL)
2256 	(void)fclose(csinfo[i].to_fp);
2257 
2258     if (freefnpp)
2259     {
2260 	vim_free(csinfo[i].fname);
2261 	vim_free(csinfo[i].ppath);
2262 	vim_free(csinfo[i].flags);
2263     }
2264 
2265     clear_csinfo(i);
2266 }
2267 
2268 
2269 /*
2270  * Calls cs_kill on all cscope connections then reinits.
2271  */
2272     static int
cs_reset(exarg_T * eap UNUSED)2273 cs_reset(exarg_T *eap UNUSED)
2274 {
2275     char	**dblist = NULL, **pplist = NULL, **fllist = NULL;
2276     int	i;
2277     char buf[20]; // for sprintf " (#%d)"
2278 
2279     if (csinfo_size == 0)
2280 	return CSCOPE_SUCCESS;
2281 
2282     // malloc our db and ppath list
2283     dblist = ALLOC_MULT(char *, csinfo_size);
2284     pplist = ALLOC_MULT(char *, csinfo_size);
2285     fllist = ALLOC_MULT(char *, csinfo_size);
2286     if (dblist == NULL || pplist == NULL || fllist == NULL)
2287     {
2288 	vim_free(dblist);
2289 	vim_free(pplist);
2290 	vim_free(fllist);
2291 	return CSCOPE_FAILURE;
2292     }
2293 
2294     for (i = 0; i < csinfo_size; i++)
2295     {
2296 	dblist[i] = csinfo[i].fname;
2297 	pplist[i] = csinfo[i].ppath;
2298 	fllist[i] = csinfo[i].flags;
2299 	if (csinfo[i].fname != NULL)
2300 	    cs_release_csp(i, FALSE);
2301     }
2302 
2303     // rebuild the cscope connection list
2304     for (i = 0; i < csinfo_size; i++)
2305     {
2306 	if (dblist[i] != NULL)
2307 	{
2308 	    cs_add_common(dblist[i], pplist[i], fllist[i]);
2309 	    if (p_csverbose)
2310 	    {
2311 		// don't use smsg_attr() because we want to display the
2312 		// connection number in the same line as
2313 		// "Added cscope database..."
2314 		sprintf(buf, " (#%d)", i);
2315 		msg_puts_attr(buf, HL_ATTR(HLF_R));
2316 	    }
2317 	}
2318 	vim_free(dblist[i]);
2319 	vim_free(pplist[i]);
2320 	vim_free(fllist[i]);
2321     }
2322     vim_free(dblist);
2323     vim_free(pplist);
2324     vim_free(fllist);
2325 
2326     if (p_csverbose)
2327 	msg_attr(_("All cscope databases reset"), HL_ATTR(HLF_R) | MSG_HIST);
2328     return CSCOPE_SUCCESS;
2329 }
2330 
2331 
2332 /*
2333  * Construct the full pathname to a file found in the cscope database.
2334  * (Prepends ppath, if there is one and if it's not already prepended,
2335  * otherwise just uses the name found.)
2336  *
2337  * We need to prepend the prefix because on some cscope's (e.g., the one that
2338  * ships with Solaris 2.6), the output never has the prefix prepended.
2339  * Contrast this with my development system (Digital Unix), which does.
2340  */
2341     static char *
cs_resolve_file(int i,char * name)2342 cs_resolve_file(int i, char *name)
2343 {
2344     char	*fullname;
2345     int		len;
2346     char_u	*csdir = NULL;
2347 
2348     /*
2349      * Ppath is freed when we destroy the cscope connection.
2350      * Fullname is freed after cs_make_vim_style_matches, after it's been
2351      * copied into the tag buffer used by Vim.
2352      */
2353     len = (int)(strlen(name) + 2);
2354     if (csinfo[i].ppath != NULL)
2355 	len += (int)strlen(csinfo[i].ppath);
2356     else if (p_csre && csinfo[i].fname != NULL)
2357     {
2358 	// If 'cscoperelative' is set and ppath is not set, use cscope.out
2359 	// path in path resolution.
2360 	csdir = alloc(MAXPATHL);
2361 	if (csdir != NULL)
2362 	{
2363 	    vim_strncpy(csdir, (char_u *)csinfo[i].fname,
2364 					  gettail((char_u *)csinfo[i].fname)
2365 						 - (char_u *)csinfo[i].fname);
2366 	    len += (int)STRLEN(csdir);
2367 	}
2368     }
2369 
2370     // Note/example: this won't work if the cscope output already starts
2371     // "../.." and the prefix path is also "../..".  if something like this
2372     // happens, you are screwed up and need to fix how you're using cscope.
2373     if (csinfo[i].ppath != NULL
2374 	    && (strncmp(name, csinfo[i].ppath, strlen(csinfo[i].ppath)) != 0)
2375 	    && (name[0] != '/')
2376 #ifdef MSWIN
2377 	    && name[0] != '\\' && name[1] != ':'
2378 #endif
2379        )
2380     {
2381 	if ((fullname = alloc(len)) != NULL)
2382 	    (void)sprintf(fullname, "%s/%s", csinfo[i].ppath, name);
2383     }
2384     else if (csdir != NULL && csinfo[i].fname != NULL && *csdir != NUL)
2385     {
2386 	// Check for csdir to be non empty to avoid empty path concatenated to
2387 	// cscope output.
2388 	fullname = (char *)concat_fnames(csdir, (char_u *)name, TRUE);
2389     }
2390     else
2391     {
2392 	fullname = (char *)vim_strsave((char_u *)name);
2393     }
2394 
2395     vim_free(csdir);
2396     return fullname;
2397 }
2398 
2399 
2400 /*
2401  * Show all cscope connections.
2402  */
2403     static int
cs_show(exarg_T * eap UNUSED)2404 cs_show(exarg_T *eap UNUSED)
2405 {
2406     short i;
2407     if (cs_cnt_connections() == 0)
2408 	msg_puts(_("no cscope connections\n"));
2409     else
2410     {
2411 	msg_puts_attr(
2412 	    _(" # pid    database name                       prepend path\n"),
2413 	    HL_ATTR(HLF_T));
2414 	for (i = 0; i < csinfo_size; i++)
2415 	{
2416 	    if (csinfo[i].fname == NULL)
2417 		continue;
2418 
2419 	    if (csinfo[i].ppath != NULL)
2420 		(void)smsg("%2d %-5ld  %-34s  %-32s",
2421 		    i, (long)csinfo[i].pid, csinfo[i].fname, csinfo[i].ppath);
2422 	    else
2423 		(void)smsg("%2d %-5ld  %-34s  <none>",
2424 			   i, (long)csinfo[i].pid, csinfo[i].fname);
2425 	}
2426     }
2427 
2428     wait_return(TRUE);
2429     return CSCOPE_SUCCESS;
2430 }
2431 
2432 
2433 /*
2434  * Only called when VIM exits to quit any cscope sessions.
2435  */
2436     void
cs_end(void)2437 cs_end(void)
2438 {
2439     int i;
2440 
2441     for (i = 0; i < csinfo_size; i++)
2442 	cs_release_csp(i, TRUE);
2443     vim_free(csinfo);
2444     csinfo_size = 0;
2445 }
2446 
2447 #endif	// FEAT_CSCOPE
2448 
2449 #if defined(FEAT_EVAL) || defined(PROTO)
2450 
2451 /*
2452  * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
2453  *
2454  * Checks the existence of a cscope connection.
2455  */
2456     void
f_cscope_connection(typval_T * argvars UNUSED,typval_T * rettv UNUSED)2457 f_cscope_connection(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
2458 {
2459 # ifdef FEAT_CSCOPE
2460     int		num = 0;
2461     char_u	*dbpath = NULL;
2462     char_u	*prepend = NULL;
2463     char_u	buf[NUMBUFLEN];
2464 
2465     if (in_vim9script()
2466 	    && (check_for_opt_number_arg(argvars, 0) == FAIL
2467 		|| (argvars[0].v_type != VAR_UNKNOWN
2468 		    && (check_for_opt_string_arg(argvars, 1) == FAIL
2469 			|| (argvars[1].v_type != VAR_UNKNOWN
2470 			    && check_for_opt_string_arg(argvars, 2) == FAIL)))))
2471 	return;
2472 
2473     if (argvars[0].v_type != VAR_UNKNOWN
2474 	    && argvars[1].v_type != VAR_UNKNOWN)
2475     {
2476 	num = (int)tv_get_number(&argvars[0]);
2477 	dbpath = tv_get_string(&argvars[1]);
2478 	if (argvars[2].v_type != VAR_UNKNOWN)
2479 	    prepend = tv_get_string_buf(&argvars[2], buf);
2480     }
2481 
2482     rettv->vval.v_number = cs_connection(num, dbpath, prepend);
2483 # endif
2484 }
2485 
2486 #endif // FEAT_EVAL
2487