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