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