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 | |
46 | static csinfo_T * csinfo = NULL; |
47 | static size_t csinfo_size = 0; // number of items allocated in csinfo[] |
48 | |
49 | static int eap_arg_len; // length of eap->arg, set in cs_lookup_cmd() |
50 | static 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 | |
67 | static void cs_usage_msg(csid_e x) |
68 | { |
69 | (void)EMSG2(_("E560: Usage: cs[cope] %s" ), cs_cmds[(int)x].usage); |
70 | } |
71 | |
72 | |
73 | static 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 | */ |
84 | char_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 | */ |
144 | void 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. |
175 | static void |
176 | do_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" |
206 | void 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. |
212 | void ex_scscope(exarg_T *eap) |
213 | { |
214 | do_cscope_general(eap, TRUE); |
215 | } |
216 | |
217 | /// Implementation of ":cstag" |
218 | void 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 |
280 | int 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. |
293 | void cs_free_tags(void) |
294 | { |
295 | cs_manage_matches(NULL, NULL, 0, Free); |
296 | } |
297 | |
298 | /// Called from do_tag(). |
299 | void 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 | */ |
331 | int 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. |
381 | static 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 | |
395 | static 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. |
409 | static int |
410 | cs_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) { |
438 | staterr: |
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 | |
510 | add_err: |
511 | xfree(fname2); |
512 | xfree(fname); |
513 | xfree(ppath); |
514 | return CSCOPE_FAILURE; |
515 | } /* cs_add_common */ |
516 | |
517 | |
518 | static int cs_check_for_connections(void) |
519 | { |
520 | return cs_cnt_connections() > 0; |
521 | } /* cs_check_for_connections */ |
522 | |
523 | static 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. |
529 | static 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 | |
540 | static 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. |
549 | static 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. |
609 | static 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? |
666 | static 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" )); |
681 | err_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" )); |
726 | err_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. |
857 | static 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(). |
891 | static 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. |
1064 | static 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 | |
1100 | static 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. |
1112 | static 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. |
1172 | static 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. |
1197 | static 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. |
1254 | static 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. |
1284 | static 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 |
1331 | static 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. |
1388 | static 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 | |
1395 | retry: |
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. |
1445 | static 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. |
1488 | static 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 */ |
1546 | static 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. |
1575 | static 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 * = 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 ">> "). |
1681 | static 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 | */ |
1769 | static 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. |
1778 | static 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. |
1890 | static 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. |
1946 | static 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. |
1992 | static 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. |
2020 | void 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 | |