1/* $NetBSD: ex_cscope.c,v 1.7 2011/03/21 14:53:03 tnozaki Exp $ */ 2 3/*- 4 * Copyright (c) 1994, 1996 5 * Rob Mayoff. All rights reserved. 6 * Copyright (c) 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12#include "config.h" 13 14#ifndef lint 15static const char sccsid[] = "Id: ex_cscope.c,v 10.21 2003/11/05 17:11:54 skimo Exp (Berkeley) Date: 2003/11/05 17:11:54"; 16#endif /* not lint */ 17 18#include <sys/param.h> 19#include <sys/types.h> /* XXX: param.h may not have included types.h */ 20#include <sys/queue.h> 21#include <sys/stat.h> 22#include <sys/time.h> 23#include <sys/wait.h> 24 25#include <bitstring.h> 26#include <ctype.h> 27#include <errno.h> 28#include <fcntl.h> 29#include <limits.h> 30#include <stddef.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <termios.h> 35#include <unistd.h> 36 37#include "../common/common.h" 38#include "pathnames.h" 39#include "tag.h" 40 41#define CSCOPE_DBFILE "cscope.out" 42#define CSCOPE_PATHS "cscope.tpath" 43 44/* 45 * 0name find all uses of name 46 * 1name find definition of name 47 * 2name find all function calls made from name 48 * 3name find callers of name 49 * 4string find text string (cscope 12.9) 50 * 4name find assignments to name (cscope 13.3) 51 * 5pattern change pattern -- NOT USED 52 * 6pattern find pattern 53 * 7name find files with name as substring 54 * 8name find files #including name 55 */ 56#define FINDHELP "\ 57find c|d|e|f|g|i|s|t buffer|pattern\n\ 58 c: find callers of name\n\ 59 d: find all function calls made from name\n\ 60 e: find pattern\n\ 61 f: find files with name as substring\n\ 62 g: find definition of name\n\ 63 i: find files #including name\n\ 64 s: find all uses of name\n\ 65 t: find assignments to name" 66 67static int cscope_add __P((SCR *, EXCMD *, const CHAR_T *)); 68static int cscope_find __P((SCR *, EXCMD*, const CHAR_T *)); 69static int cscope_help __P((SCR *, EXCMD *, const CHAR_T *)); 70static int cscope_kill __P((SCR *, EXCMD *, const CHAR_T *)); 71static int cscope_reset __P((SCR *, EXCMD *, const CHAR_T *)); 72 73typedef struct _cc { 74 const char *name; 75 int (*function) __P((SCR *, EXCMD *, const CHAR_T *)); 76 const char *help_msg; 77 const char *usage_msg; 78} CC; 79 80static CC const cscope_cmds[] = { 81 { "add", cscope_add, 82 "Add a new cscope database", "add file | directory" }, 83 { "find", cscope_find, 84 "Query the databases for a pattern", FINDHELP }, 85 { "help", cscope_help, 86 "Show help for cscope commands", "help [command]" }, 87 { "kill", cscope_kill, 88 "Kill a cscope connection", "kill number" }, 89 { "reset", cscope_reset, 90 "Discard all current cscope connections", "reset" }, 91 { NULL, NULL, NULL, NULL } 92}; 93 94static TAGQ *create_cs_cmd __P((SCR *, const char *, size_t *)); 95static int csc_help __P((SCR *, const char *)); 96static void csc_file __P((SCR *, 97 CSC *, char *, char **, size_t *, int *)); 98static int get_paths __P((SCR *, CSC *)); 99static CC const *lookup_ccmd __P((const char *)); 100static int parse __P((SCR *, CSC *, TAGQ *, int *)); 101static int read_prompt __P((SCR *, CSC *)); 102static int run_cscope __P((SCR *, CSC *, const char *)); 103static int start_cscopes __P((SCR *, EXCMD *)); 104static int terminate __P((SCR *, CSC *, int)); 105 106/* 107 * ex_cscope -- 108 * Perform an ex cscope. 109 * 110 * PUBLIC: int ex_cscope __P((SCR *, EXCMD *)); 111 */ 112int 113ex_cscope(SCR *sp, EXCMD *cmdp) 114{ 115 CC const *ccp; 116 EX_PRIVATE *exp; 117 int i; 118 CHAR_T *cmd; 119 CHAR_T *p; 120 const char *np; 121 size_t nlen; 122 123 /* Initialize the default cscope directories. */ 124 exp = EXP(sp); 125 if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp)) 126 return (1); 127 F_SET(exp, EXP_CSCINIT); 128 129 /* Skip leading whitespace. */ 130 for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p) 131 if (!ISBLANK((UCHAR_T)*p)) 132 break; 133 if (i == 0) 134 goto usage; 135 136 /* Skip the command to any arguments. */ 137 for (cmd = p; i > 0; --i, ++p) 138 if (ISBLANK((UCHAR_T)*p)) 139 break; 140 if (*p != '\0') { 141 *p++ = '\0'; 142 for (; *p && ISBLANK((UCHAR_T)*p); ++p); 143 } 144 145 INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); 146 if ((ccp = lookup_ccmd(np)) == NULL) { 147usage: msgq(sp, M_ERR, "309|Use \"cscope help\" for help"); 148 return (1); 149 } 150 151 /* Call the underlying function. */ 152 return (ccp->function(sp, cmdp, p)); 153} 154 155/* 156 * start_cscopes -- 157 * Initialize the cscope package. 158 */ 159static int 160start_cscopes(SCR *sp, EXCMD *cmdp) 161{ 162 size_t blen, len; 163 char *bp, *cscopes, *p, *t; 164 const CHAR_T *wp; 165 size_t wlen; 166 167 /* 168 * EXTENSION #1: 169 * 170 * If the CSCOPE_DIRS environment variable is set, we treat it as a 171 * list of cscope directories that we're using, similar to the tags 172 * edit option. 173 * 174 * XXX 175 * This should probably be an edit option, although that implies that 176 * we start/stop cscope processes periodically, instead of once when 177 * the editor starts. 178 */ 179 if ((cscopes = getenv("CSCOPE_DIRS")) == NULL) 180 return (0); 181 len = strlen(cscopes); 182 GET_SPACE_RETC(sp, bp, blen, len); 183 memcpy(bp, cscopes, len + 1); 184 185 for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;) 186 if (*p != '\0') { 187 CHAR2INT(sp, p, strlen(p) + 1, wp, wlen); 188 (void)cscope_add(sp, cmdp, wp); 189 } 190 191 FREE_SPACE(sp, bp, blen); 192 return (0); 193} 194 195/* 196 * cscope_add -- 197 * The cscope add command. 198 */ 199static int 200cscope_add(SCR *sp, EXCMD *cmdp, const CHAR_T *dname) 201{ 202 struct stat sb; 203 EX_PRIVATE *exp; 204 CSC *csc; 205 size_t len; 206 int cur_argc; 207 const char *dbname; 208 char path[MAXPATHLEN]; 209 const char *np; 210 char *npp; 211 size_t nlen; 212 213 exp = EXP(sp); 214 215 /* 216 * 0 additional args: usage. 217 * 1 additional args: matched a file. 218 * >1 additional args: object, too many args. 219 */ 220 cur_argc = cmdp->argc; 221 if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) { 222 return (1); 223 } 224 if (cmdp->argc == cur_argc) { 225 (void)csc_help(sp, "add"); 226 return (1); 227 } 228 if (cmdp->argc == cur_argc + 1) 229 dname = cmdp->argv[cur_argc]->bp; 230 else { 231 ex_emsg(sp, np, EXM_FILECOUNT); 232 return (1); 233 } 234 235 INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen); 236 237 /* 238 * The user can specify a specific file (so they can have multiple 239 * Cscope databases in a single directory) or a directory. If the 240 * file doesn't exist, we're done. If it's a directory, append the 241 * standard database file name and try again. Store the directory 242 * name regardless so that we can use it as a base for searches. 243 */ 244 if (stat(np, &sb)) { 245 msgq(sp, M_SYSERR, "%s", np); 246 return (1); 247 } 248 if (S_ISDIR(sb.st_mode)) { 249 (void)snprintf(path, sizeof(path), 250 "%s/%s", np, CSCOPE_DBFILE); 251 if (stat(path, &sb)) { 252 msgq(sp, M_SYSERR, "%s", path); 253 return (1); 254 } 255 dbname = CSCOPE_DBFILE; 256 } else if ((npp = strrchr(np, '/')) != NULL) { 257 *npp = '\0'; 258 dbname = npp + 1; 259 } else { 260 dbname = np; 261 np = "."; 262 } 263 264 /* Allocate a cscope connection structure and initialize its fields. */ 265 len = strlen(np); 266 CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len); 267 csc->dname = csc->buf; 268 csc->dlen = len; 269 memcpy(csc->dname, np, len); 270 csc->mtime = sb.st_mtime; 271 272 /* Get the search paths for the cscope. */ 273 if (get_paths(sp, csc)) 274 goto err; 275 276 /* Start the cscope process. */ 277 if (run_cscope(sp, csc, dbname)) 278 goto err; 279 280 /* 281 * Add the cscope connection to the screen's list. From now on, 282 * on error, we have to call terminate, which expects the csc to 283 * be on the chain. 284 */ 285 LIST_INSERT_HEAD(&exp->cscq, csc, q); 286 287 /* Read the initial prompt from the cscope to make sure it's okay. */ 288 return read_prompt(sp, csc); 289 290err: free(csc); 291 return (1); 292} 293 294/* 295 * get_paths -- 296 * Get the directories to search for the files associated with this 297 * cscope database. 298 */ 299static int 300get_paths(SCR *sp, CSC *csc) 301{ 302 struct stat sb; 303 int fd, nentries; 304 size_t len; 305 char *p, **pathp, buf[MAXPATHLEN * 2]; 306 307 /* 308 * EXTENSION #2: 309 * 310 * If there's a cscope directory with a file named CSCOPE_PATHS, it 311 * contains a colon-separated list of paths in which to search for 312 * files returned by cscope. 313 * 314 * XXX 315 * These paths are absolute paths, and not relative to the cscope 316 * directory. To fix this, rewrite the each path using the cscope 317 * directory as a prefix. 318 */ 319 (void)snprintf(buf, sizeof(buf), "%s/%s", csc->dname, CSCOPE_PATHS); 320 if (stat(buf, &sb) == 0) { 321 /* Read in the CSCOPE_PATHS file. */ 322 len = sb.st_size; 323 MALLOC_RET(sp, csc->pbuf, char *, len + 1); 324 if ((fd = open(buf, O_RDONLY, 0)) < 0 || 325 (size_t)read(fd, csc->pbuf, len) != len) { 326 msgq_str(sp, M_SYSERR, buf, "%s"); 327 if (fd >= 0) 328 (void)close(fd); 329 return (1); 330 } 331 (void)close(fd); 332 csc->pbuf[len] = '\0'; 333 334 /* Count up the entries. */ 335 for (nentries = 0, p = csc->pbuf; *p != '\0'; ++p) 336 if (p[0] == ':' && p[1] != '\0') 337 ++nentries; 338 339 /* Build an array of pointers to the paths. */ 340 CALLOC_GOTO(sp, 341 csc->paths, char **, nentries + 1, sizeof(char **)); 342 for (pathp = csc->paths, p = strtok(csc->pbuf, ":"); 343 p != NULL; p = strtok(NULL, ":")) 344 *pathp++ = p; 345 return (0); 346 } 347 348 /* 349 * If the CSCOPE_PATHS file doesn't exist, we look for files 350 * relative to the cscope directory. 351 */ 352 if ((csc->pbuf = strdup(csc->dname)) == NULL) { 353 msgq(sp, M_SYSERR, NULL); 354 return (1); 355 } 356 CALLOC_GOTO(sp, csc->paths, char **, 2, sizeof(char *)); 357 csc->paths[0] = csc->pbuf; 358 return (0); 359 360alloc_err: 361 if (csc->pbuf != NULL) { 362 free(csc->pbuf); 363 csc->pbuf = NULL; 364 } 365 return (1); 366} 367 368/* 369 * run_cscope -- 370 * Fork off the cscope process. 371 */ 372static int 373run_cscope(SCR *sp, CSC *csc, const char *dbname) 374{ 375 int to_cs[2], from_cs[2]; 376 char cmd[MAXPATHLEN * 2]; 377 378 /* 379 * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from 380 * from_cs[0] and writes to to_cs[1]. 381 */ 382 to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1; 383 if (pipe(to_cs) < 0 || pipe(from_cs) < 0) { 384 msgq(sp, M_SYSERR, "pipe"); 385 goto err; 386 } 387 switch (csc->pid = vfork()) { 388 case -1: 389 msgq(sp, M_SYSERR, "vfork"); 390err: if (to_cs[0] != -1) 391 (void)close(to_cs[0]); 392 if (to_cs[1] != -1) 393 (void)close(to_cs[1]); 394 if (from_cs[0] != -1) 395 (void)close(from_cs[0]); 396 if (from_cs[1] != -1) 397 (void)close(from_cs[1]); 398 return (1); 399 case 0: /* child: run cscope. */ 400 (void)dup2(to_cs[0], STDIN_FILENO); 401 (void)dup2(from_cs[1], STDOUT_FILENO); 402 (void)dup2(from_cs[1], STDERR_FILENO); 403 404 /* Close unused file descriptors. */ 405 (void)close(to_cs[1]); 406 (void)close(from_cs[0]); 407 408 /* Run the cscope command. */ 409#define CSCOPE_CMD_FMT "cd '%s' && exec cscope -dl -f %s" 410 (void)snprintf(cmd, sizeof(cmd), 411 CSCOPE_CMD_FMT, csc->dname, dbname); 412 (void)execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); 413 msgq_str(sp, M_SYSERR, cmd, "execl: %s"); 414 _exit (127); 415 /* NOTREACHED */ 416 default: /* parent. */ 417 /* Close unused file descriptors. */ 418 (void)close(to_cs[0]); 419 (void)close(from_cs[1]); 420 421 /* 422 * Save the file descriptors for later duplication, and 423 * reopen as streams. 424 */ 425 csc->to_fd = to_cs[1]; 426 csc->to_fp = fdopen(to_cs[1], "w"); 427 csc->from_fd = from_cs[0]; 428 csc->from_fp = fdopen(from_cs[0], "r"); 429 break; 430 } 431 return (0); 432} 433 434/* 435 * cscope_find -- 436 * The cscope find command. 437 */ 438static int 439cscope_find(SCR *sp, EXCMD *cmdp, const CHAR_T *pattern) 440{ 441 CSC *csc, *csc_next; 442 EX_PRIVATE *exp; 443 FREF *frp; 444 TAGQ *rtqp, *tqp; 445 TAG *rtp; 446 db_recno_t lno; 447 size_t cno, search; 448 int force, istmp, matches; 449 const char *np = NULL; 450 size_t nlen; 451 452 exp = EXP(sp); 453 454 /* Check for connections. */ 455 if (exp->cscq.lh_first == NULL) { 456 msgq(sp, M_ERR, "310|No cscope connections running"); 457 return (1); 458 } 459 460 /* 461 * Allocate all necessary memory before doing anything hard. If the 462 * tags stack is empty, we'll need the `local context' TAGQ structure 463 * later. 464 */ 465 rtp = NULL; 466 rtqp = NULL; 467 if (exp->tq.cqh_first == (void *)&exp->tq) { 468 /* Initialize the `local context' tag queue structure. */ 469 CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ)); 470 CIRCLEQ_INIT(&rtqp->tagq); 471 472 /* Initialize and link in its tag structure. */ 473 CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG)); 474 CIRCLEQ_INSERT_HEAD(&rtqp->tagq, rtp, q); 475 rtqp->current = rtp; 476 } 477 478 /* Create the cscope command. */ 479 INT2CHAR(sp, pattern, STRLEN(pattern) + 1, np, nlen); 480 np = strdup(np); 481 if ((tqp = create_cs_cmd(sp, np, &search)) == NULL) 482 goto err; 483 484 /* 485 * Stick the current context in a convenient place, we'll lose it 486 * when we switch files. 487 */ 488 frp = sp->frp; 489 lno = sp->lno; 490 cno = sp->cno; 491 istmp = F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN); 492 493 /* Search all open connections for a match. */ 494 matches = 0; 495 for (csc = exp->cscq.lh_first; csc != NULL; csc = csc_next) { 496 /* Copy csc->q.lh_next here in case csc is killed. */ 497 csc_next = csc->q.le_next; 498 499 /* 500 * Send the command to the cscope program. (We skip the 501 * first two bytes of the command, because we stored the 502 * search cscope command character and a leading space 503 * there.) 504 */ 505 (void)fprintf(csc->to_fp, "%zu%s\n", search, tqp->tag + 2); 506 (void)fflush(csc->to_fp); 507 508 /* Read the output. */ 509 if (parse(sp, csc, tqp, &matches)) { 510 if (rtqp != NULL) 511 free(rtqp); 512 tagq_free(sp, tqp); 513 return (1); 514 } 515 } 516 517 if (matches == 0) { 518 msgq(sp, M_INFO, "278|No matches for query"); 519 return (0); 520 } 521 522 tqp->current = tqp->tagq.cqh_first; 523 524 /* Try to switch to the first tag. */ 525 force = FL_ISSET(cmdp->iflags, E_C_FORCE); 526 if (F_ISSET(cmdp, E_NEWSCREEN)) { 527 if (ex_tag_Nswitch(sp, tqp->current, force)) 528 goto err; 529 530 /* Everything else gets done in the new screen. */ 531 sp = sp->nextdisp; 532 exp = EXP(sp); 533 } else 534 if (ex_tag_nswitch(sp, tqp->current, force)) 535 goto err; 536 537 /* 538 * If this is the first tag, put a `current location' queue entry 539 * in place, so we can pop all the way back to the current mark. 540 * Note, it doesn't point to much of anything, it's a placeholder. 541 */ 542 if (exp->tq.cqh_first == (void *)&exp->tq) { 543 CIRCLEQ_INSERT_HEAD(&exp->tq, rtqp, q); 544 } else 545 rtqp = exp->tq.cqh_first; 546 547 /* Link the current TAGQ structure into place. */ 548 CIRCLEQ_INSERT_HEAD(&exp->tq, tqp, q); 549 550 (void)cscope_search(sp, tqp, tqp->current); 551 552 /* 553 * Move the current context from the temporary save area into the 554 * right structure. 555 * 556 * If we were in a temporary file, we don't have a context to which 557 * we can return, so just make it be the same as what we're moving 558 * to. It will be a little odd that ^T doesn't change anything, but 559 * I don't think it's a big deal. 560 */ 561 if (istmp) { 562 rtqp->current->frp = sp->frp; 563 rtqp->current->lno = sp->lno; 564 rtqp->current->cno = sp->cno; 565 } else { 566 rtqp->current->frp = frp; 567 rtqp->current->lno = lno; 568 rtqp->current->cno = cno; 569 } 570 571 return (0); 572 573err: 574alloc_err: 575 if (rtqp != NULL) 576 free(rtqp); 577 if (rtp != NULL) 578 free(rtp); 579 if (np != NULL) 580 free(__UNCONST(np)); 581 return (1); 582} 583 584/* 585 * create_cs_cmd -- 586 * Build a cscope command, creating and initializing the base TAGQ. 587 */ 588static TAGQ * 589create_cs_cmd(SCR *sp, const char *pattern, size_t *searchp) 590{ 591 CB *cbp; 592 TAGQ *tqp; 593 size_t tlen; 594 const char *p; 595 596 /* 597 * Cscope supports a "change pattern" command which we never use, 598 * cscope command 5. Set CSCOPE_QUERIES[5] to " " since the user 599 * can't pass " " as the first character of pattern. That way the 600 * user can't ask for pattern 5 so we don't need any special-case 601 * code. 602 */ 603#define CSCOPE_QUERIES "sgdct efi" 604 605 if (pattern == NULL) 606 goto usage; 607 608 /* Skip leading blanks, check for command character. */ 609 for (; isblank((unsigned char)pattern[0]); ++pattern); 610 if (pattern[0] == '\0' || !isblank((unsigned char)pattern[1])) 611 goto usage; 612 for (*searchp = 0, p = CSCOPE_QUERIES; 613 *p != '\0' && *p != pattern[0]; ++*searchp, ++p); 614 if (*p == '\0') { 615 msgq(sp, M_ERR, 616 "311|%s: unknown search type: use one of %s", 617 KEY_NAME(sp, pattern[0]), CSCOPE_QUERIES); 618 return (NULL); 619 } 620 621 /* Skip <blank> characters to the pattern. */ 622 for (p = pattern + 1; *p != '\0' && isblank((unsigned char)*p); ++p); 623 if (*p == '\0') { 624usage: (void)csc_help(sp, "find"); 625 return (NULL); 626 } 627 628 /* The user can specify the contents of a buffer as the pattern. */ 629 cbp = NULL; 630 if (p[0] == '"' && p[1] != '\0' && p[2] == '\0') 631 CBNAME(sp, cbp, p[1]); 632 if (cbp != NULL) { 633 INT2CHAR(sp, cbp->textq.cqh_first->lb, 634 cbp->textq.cqh_first->len, p, tlen); 635 } else 636 tlen = strlen(p); 637 638 /* Allocate and initialize the TAGQ structure. */ 639 CALLOC(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + tlen + 3); 640 if (tqp == NULL) 641 return (NULL); 642 CIRCLEQ_INIT(&tqp->tagq); 643 tqp->tag = tqp->buf; 644 tqp->tag[0] = pattern[0]; 645 tqp->tag[1] = ' '; 646 tqp->tlen = tlen + 2; 647 memcpy(tqp->tag + 2, p, tlen); 648 tqp->tag[tlen + 2] = '\0'; 649 F_SET(tqp, TAG_CSCOPE); 650 651 return (tqp); 652} 653 654/* 655 * parse -- 656 * Parse the cscope output. 657 */ 658static int 659parse(SCR *sp, CSC *csc, TAGQ *tqp, int *matchesp) 660{ 661 TAG *tp; 662 db_recno_t slno = 0; 663 size_t dlen, nlen = 0, slen = 0; 664 int ch, i, isolder = 0, nlines; 665 char *dname = NULL, *name = NULL, *search, *p, *t, dummy[2], buf[2048]; 666 667 for (;;) { 668 if (!fgets(buf, sizeof(buf), csc->from_fp)) 669 goto io_err; 670 671 /* 672 * If the database is out of date, or there's some other 673 * problem, cscope will output error messages before the 674 * number-of-lines output. Display/discard any output 675 * that doesn't match what we want. 676 */ 677#define CSCOPE_NLINES_FMT "cscope: %d lines%1[\n]" 678 if (sscanf(buf, CSCOPE_NLINES_FMT, &nlines, dummy) == 2) 679 break; 680 if ((p = strchr(buf, '\n')) != NULL) 681 *p = '\0'; 682 msgq(sp, M_ERR, "%s: \"%s\"", csc->dname, buf); 683 } 684 685 while (nlines--) { 686 if (fgets(buf, sizeof(buf), csc->from_fp) == NULL) 687 goto io_err; 688 689 /* If the line's too long for the buffer, discard it. */ 690 if ((p = strchr(buf, '\n')) == NULL) { 691 while ((ch = getc(csc->from_fp)) != EOF && ch != '\n'); 692 continue; 693 } 694 *p = '\0'; 695 696 /* 697 * The cscope output is in the following format: 698 * 699 * <filename> <context> <line number> <pattern> 700 * 701 * Figure out how long everything is so we can allocate in one 702 * swell foop, but discard anything that looks wrong. 703 */ 704 for (p = buf, i = 0; 705 i < 3 && (t = strsep(&p, "\t ")) != NULL; ++i) 706 switch (i) { 707 case 0: /* Filename. */ 708 name = t; 709 nlen = strlen(name); 710 break; 711 case 1: /* Context. */ 712 break; 713 case 2: /* Line number. */ 714 slno = (db_recno_t)atol(t); 715 break; 716 } 717 if (i != 3 || p == NULL || t == NULL) 718 continue; 719 720 /* The rest of the string is the search pattern. */ 721 search = p; 722 slen = strlen(p); 723 724 /* Resolve the file name. */ 725 csc_file(sp, csc, name, &dname, &dlen, &isolder); 726 727 /* 728 * If the file is older than the cscope database, that is, 729 * the database was built since the file was last modified, 730 * or there wasn't a search string, use the line number. 731 */ 732 if (isolder || strcmp(search, "<unknown>") == 0) { 733 search = NULL; 734 slen = 0; 735 } 736 737 /* 738 * Allocate and initialize a tag structure plus the variable 739 * length cscope information that follows it. 740 */ 741 CALLOC_RET(sp, tp, 742 TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + slen + 1); 743 tp->fname = (char *)tp->buf; 744 if (dlen != 0) { 745 memcpy(tp->fname, dname, dlen); 746 tp->fname[dlen] = '/'; 747 ++dlen; 748 } 749 memcpy(tp->fname + dlen, name, nlen + 1); 750 tp->fnlen = dlen + nlen; 751 tp->slno = slno; 752 if (slen != 0) { 753 tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1); 754 MEMCPYW(tp->search, search, (tp->slen = slen) + 1); 755 } 756 CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q); 757 758 ++*matchesp; 759 } 760 761 return read_prompt(sp, csc); 762 763io_err: if (feof(csc->from_fp)) 764 errno = EIO; 765 msgq_str(sp, M_SYSERR, "%s", csc->dname); 766 terminate(sp, csc, 0); 767 return (1); 768} 769 770/* 771 * csc_file -- 772 * Search for the right path to this file. 773 */ 774static void 775csc_file(SCR *sp, CSC *csc, char *name, char **dirp, size_t *dlenp, int *isolderp) 776{ 777 struct stat sb; 778 char **pp, buf[MAXPATHLEN]; 779 780 /* 781 * Check for the file in all of the listed paths. If we don't 782 * find it, we simply return it unchanged. We have to do this 783 * now, even though it's expensive, because if the user changes 784 * directories, we can't change our minds as to where the file 785 * lives. 786 */ 787 for (pp = csc->paths; *pp != NULL; ++pp) { 788 (void)snprintf(buf, sizeof(buf), "%s/%s", *pp, name); 789 if (stat(buf, &sb) == 0) { 790 *dirp = *pp; 791 *dlenp = strlen(*pp); 792 *isolderp = sb.st_mtime < csc->mtime; 793 return; 794 } 795 } 796 *dlenp = 0; 797} 798 799/* 800 * cscope_help -- 801 * The cscope help command. 802 */ 803static int 804cscope_help(SCR *sp, EXCMD *cmdp, const CHAR_T *subcmd) 805{ 806 const char *np; 807 size_t nlen; 808 809 INT2CHAR(sp, subcmd, STRLEN(subcmd) + 1, np, nlen); 810 return (csc_help(sp, np)); 811} 812 813/* 814 * csc_help -- 815 * Display help/usage messages. 816 */ 817static int 818csc_help(SCR *sp, const char *cmd) 819{ 820 CC const *ccp; 821 822 if (cmd != NULL && *cmd != '\0') { 823 if ((ccp = lookup_ccmd(cmd)) == NULL) { 824 ex_printf(sp, 825 "%s doesn't match any cscope command\n", cmd); 826 return (1); 827 } else { 828 ex_printf(sp, 829 "Command: %s (%s)\n", ccp->name, ccp->help_msg); 830 ex_printf(sp, " Usage: %s\n", ccp->usage_msg); 831 return (0); 832 } 833 } 834 835 ex_printf(sp, "cscope commands:\n"); 836 for (ccp = cscope_cmds; ccp->name != NULL; ++ccp) 837 ex_printf(sp, " %*s: %s\n", 5, ccp->name, ccp->help_msg); 838 return (0); 839} 840 841/* 842 * cscope_kill -- 843 * The cscope kill command. 844 */ 845static int 846cscope_kill(SCR *sp, EXCMD *cmdp, const CHAR_T *cn) 847{ 848 const char *np; 849 size_t nlen; 850 851 INT2CHAR(sp, cn, STRLEN(cn) + 1, np, nlen); 852 return (terminate(sp, NULL, atoi(np))); 853} 854 855/* 856 * terminate -- 857 * Detach from a cscope process. 858 */ 859static int 860terminate(SCR *sp, CSC *csc, int n) 861{ 862 EX_PRIVATE *exp; 863 int i, pstat; 864 865 exp = EXP(sp); 866 867 /* 868 * We either get a csc structure or a number. If not provided a 869 * csc structure, find the right one. 870 */ 871 if (csc == NULL) { 872 if (n < 1) 873 goto badno; 874 for (i = 1, csc = exp->cscq.lh_first; 875 csc != NULL; csc = csc->q.le_next, i++) 876 if (i == n) 877 break; 878 if (csc == NULL) { 879badno: msgq(sp, M_ERR, "312|%d: no such cscope session", n); 880 return (1); 881 } 882 } 883 884 /* 885 * XXX 886 * Theoretically, we have the only file descriptors to the process, 887 * so closing them should let it exit gracefully, deleting temporary 888 * files, etc. The original vi cscope integration sent the cscope 889 * connection a SIGTERM signal, so I'm not sure if closing the file 890 * descriptors is sufficient. 891 */ 892 if (csc->from_fp != NULL) 893 (void)fclose(csc->from_fp); 894 if (csc->to_fp != NULL) 895 (void)fclose(csc->to_fp); 896 (void)waitpid(csc->pid, &pstat, 0); 897 898 /* Discard cscope connection information. */ 899 LIST_REMOVE(csc, q); 900 if (csc->pbuf != NULL) 901 free(csc->pbuf); 902 if (csc->paths != NULL) 903 free(csc->paths); 904 free(csc); 905 return (0); 906} 907 908/* 909 * cscope_reset -- 910 * The cscope reset command. 911 */ 912static int 913cscope_reset(SCR *sp, EXCMD *cmdp, const CHAR_T *notusedp) 914{ 915 EX_PRIVATE *exp; 916 917 for (exp = EXP(sp); exp->cscq.lh_first != NULL;) { 918 static CHAR_T one[] = {'1', 0}; 919 if (cscope_kill(sp, cmdp, one)) 920 return (1); 921 } 922 return (0); 923} 924 925/* 926 * cscope_display -- 927 * Display current connections. 928 * 929 * PUBLIC: int cscope_display __P((SCR *)); 930 */ 931int 932cscope_display(SCR *sp) 933{ 934 EX_PRIVATE *exp; 935 CSC *csc; 936 int i; 937 938 exp = EXP(sp); 939 if (exp->cscq.lh_first == NULL) { 940 ex_printf(sp, "No cscope connections.\n"); 941 return (0); 942 } 943 for (i = 1, 944 csc = exp->cscq.lh_first; csc != NULL; ++i, csc = csc->q.le_next) 945 ex_printf(sp, 946 "%2d %s (process %lu)\n", i, csc->dname, (u_long)csc->pid); 947 return (0); 948} 949 950/* 951 * cscope_search -- 952 * Search a file for a cscope entry. 953 * 954 * PUBLIC: int cscope_search __P((SCR *, TAGQ *, TAG *)); 955 */ 956int 957cscope_search(SCR *sp, TAGQ *tqp, TAG *tp) 958{ 959 MARK m; 960 961 /* If we don't have a search pattern, use the line number. */ 962 if (tp->search == NULL) { 963 if (!db_exist(sp, tp->slno)) { 964 tag_msg(sp, TAG_BADLNO, tqp->tag); 965 return (1); 966 } 967 m.lno = tp->slno; 968 } else { 969 /* 970 * Search for the tag; cheap fallback for C functions 971 * if the name is the same but the arguments have changed. 972 */ 973 m.lno = 1; 974 m.cno = 0; 975 if (f_search(sp, &m, &m, 976 tp->search, tp->slen, NULL, SEARCH_CSCOPE | SEARCH_FIRST)) { 977 tag_msg(sp, TAG_SEARCH, tqp->tag); 978 return (1); 979 } 980 981 /* 982 * !!! 983 * Historically, tags set the search direction if it wasn't 984 * already set. 985 */ 986 if (sp->searchdir == NOTSET) 987 sp->searchdir = FORWARD; 988 } 989 990 /* 991 * !!! 992 * Tags move to the first non-blank, NOT the search pattern start. 993 */ 994 sp->lno = m.lno; 995 sp->cno = 0; 996 (void)nonblank(sp, sp->lno, &sp->cno); 997 return (0); 998} 999 1000 1001/* 1002 * lookup_ccmd -- 1003 * Return a pointer to the command structure. 1004 */ 1005static CC const * 1006lookup_ccmd(const char *name) 1007{ 1008 CC const *ccp; 1009 size_t len; 1010 1011 len = strlen(name); 1012 for (ccp = cscope_cmds; ccp->name != NULL; ++ccp) 1013 if (strncmp(name, ccp->name, len) == 0) 1014 return (ccp); 1015 return (NULL); 1016} 1017 1018/* 1019 * read_prompt -- 1020 * Read a prompt from cscope. 1021 */ 1022static int 1023read_prompt(SCR *sp, CSC *csc) 1024{ 1025 int ch; 1026 1027#define CSCOPE_PROMPT ">> " 1028 for (;;) { 1029 while ((ch = 1030 getc(csc->from_fp)) != EOF && ch != CSCOPE_PROMPT[0]); 1031 if (ch == EOF) { 1032 terminate(sp, csc, 0); 1033 return (1); 1034 } 1035 if (getc(csc->from_fp) != CSCOPE_PROMPT[1]) 1036 continue; 1037 if (getc(csc->from_fp) != CSCOPE_PROMPT[2]) 1038 continue; 1039 break; 1040 } 1041 return (0); 1042} 1043