ex_cscope.c revision 254225
119304Speter/*- 219304Speter * Copyright (c) 1994, 1996 319304Speter * Rob Mayoff. All rights reserved. 419304Speter * Copyright (c) 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: ex_cscope.c,v 10.25 2012/10/04 09:23:03 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 16254225Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/stat.h> 1919304Speter#include <sys/wait.h> 2019304Speter 2119304Speter#include <bitstring.h> 2219304Speter#include <ctype.h> 2319304Speter#include <errno.h> 2419304Speter#include <fcntl.h> 2519304Speter#include <limits.h> 26254225Speter#include <signal.h> 2719304Speter#include <stddef.h> 2819304Speter#include <stdio.h> 2919304Speter#include <stdlib.h> 3019304Speter#include <string.h> 3119304Speter#include <termios.h> 3219304Speter#include <unistd.h> 3319304Speter 3419304Speter#include "../common/common.h" 3519304Speter#include "pathnames.h" 3619304Speter#include "tag.h" 3719304Speter 3819304Speter#define CSCOPE_DBFILE "cscope.out" 3919304Speter#define CSCOPE_PATHS "cscope.tpath" 4019304Speter 4119304Speter/* 4219304Speter * 0name find all uses of name 4319304Speter * 1name find definition of name 4419304Speter * 2name find all function calls made from name 4519304Speter * 3name find callers of name 4619304Speter * 4string find text string (cscope 12.9) 4719304Speter * 4name find assignments to name (cscope 13.3) 4819304Speter * 5pattern change pattern -- NOT USED 4919304Speter * 6pattern find pattern 5019304Speter * 7name find files with name as substring 5119304Speter * 8name find files #including name 5219304Speter */ 5319304Speter#define FINDHELP "\ 5419304Speterfind c|d|e|f|g|i|s|t buffer|pattern\n\ 5519304Speter c: find callers of name\n\ 5619304Speter d: find all function calls made from name\n\ 5719304Speter e: find pattern\n\ 5819304Speter f: find files with name as substring\n\ 5919304Speter g: find definition of name\n\ 6019304Speter i: find files #including name\n\ 6119304Speter s: find all uses of name\n\ 6219304Speter t: find assignments to name" 6319304Speter 64254225Speterstatic int cscope_add __P((SCR *, EXCMD *, CHAR_T *)); 65254225Speterstatic int cscope_find __P((SCR *, EXCMD*, CHAR_T *)); 66254225Speterstatic int cscope_help __P((SCR *, EXCMD *, CHAR_T *)); 67254225Speterstatic int cscope_kill __P((SCR *, EXCMD *, CHAR_T *)); 68254225Speterstatic int cscope_reset __P((SCR *, EXCMD *, CHAR_T *)); 6919304Speter 7019304Spetertypedef struct _cc { 7119304Speter char *name; 72254225Speter int (*function) __P((SCR *, EXCMD *, CHAR_T *)); 7319304Speter char *help_msg; 7419304Speter char *usage_msg; 7519304Speter} CC; 7619304Speter 7719304Speterstatic CC const cscope_cmds[] = { 7819304Speter { "add", cscope_add, 7919304Speter "Add a new cscope database", "add file | directory" }, 8019304Speter { "find", cscope_find, 8119304Speter "Query the databases for a pattern", FINDHELP }, 8219304Speter { "help", cscope_help, 8319304Speter "Show help for cscope commands", "help [command]" }, 8419304Speter { "kill", cscope_kill, 8519304Speter "Kill a cscope connection", "kill number" }, 8619304Speter { "reset", cscope_reset, 8719304Speter "Discard all current cscope connections", "reset" }, 8819304Speter { NULL } 8919304Speter}; 9019304Speter 9119304Speterstatic TAGQ *create_cs_cmd __P((SCR *, char *, size_t *)); 9219304Speterstatic int csc_help __P((SCR *, char *)); 9319304Speterstatic void csc_file __P((SCR *, 9419304Speter CSC *, char *, char **, size_t *, int *)); 9519304Speterstatic int get_paths __P((SCR *, CSC *)); 9619304Speterstatic CC const *lookup_ccmd __P((char *)); 9719304Speterstatic int parse __P((SCR *, CSC *, TAGQ *, int *)); 9819304Speterstatic int read_prompt __P((SCR *, CSC *)); 9919304Speterstatic int run_cscope __P((SCR *, CSC *, char *)); 10019304Speterstatic int start_cscopes __P((SCR *, EXCMD *)); 10119304Speterstatic int terminate __P((SCR *, CSC *, int)); 10219304Speter 10319304Speter/* 10419304Speter * ex_cscope -- 10519304Speter * Perform an ex cscope. 10619304Speter * 10719304Speter * PUBLIC: int ex_cscope __P((SCR *, EXCMD *)); 10819304Speter */ 10919304Speterint 110254225Speterex_cscope(SCR *sp, EXCMD *cmdp) 11119304Speter{ 11219304Speter CC const *ccp; 11319304Speter EX_PRIVATE *exp; 11419304Speter int i; 115254225Speter CHAR_T *cmd; 116254225Speter CHAR_T *p; 117254225Speter char *np; 118254225Speter size_t nlen; 11919304Speter 12019304Speter /* Initialize the default cscope directories. */ 12119304Speter exp = EXP(sp); 12219304Speter if (!F_ISSET(exp, EXP_CSCINIT) && start_cscopes(sp, cmdp)) 12319304Speter return (1); 12419304Speter F_SET(exp, EXP_CSCINIT); 12519304Speter 12619304Speter /* Skip leading whitespace. */ 12719304Speter for (p = cmdp->argv[0]->bp, i = cmdp->argv[0]->len; i > 0; --i, ++p) 12819304Speter if (!isspace(*p)) 12919304Speter break; 13019304Speter if (i == 0) 13119304Speter goto usage; 13219304Speter 13319304Speter /* Skip the command to any arguments. */ 13419304Speter for (cmd = p; i > 0; --i, ++p) 13519304Speter if (isspace(*p)) 13619304Speter break; 13719304Speter if (*p != '\0') { 13819304Speter *p++ = '\0'; 13919304Speter for (; *p && isspace(*p); ++p); 14019304Speter } 14119304Speter 142254225Speter INT2CHAR(sp, cmd, STRLEN(cmd) + 1, np, nlen); 143254225Speter if ((ccp = lookup_ccmd(np)) == NULL) { 14419304Speterusage: msgq(sp, M_ERR, "309|Use \"cscope help\" for help"); 14519304Speter return (1); 14619304Speter } 14719304Speter 14819304Speter /* Call the underlying function. */ 14919304Speter return (ccp->function(sp, cmdp, p)); 15019304Speter} 15119304Speter 15219304Speter/* 15319304Speter * start_cscopes -- 15419304Speter * Initialize the cscope package. 15519304Speter */ 15619304Speterstatic int 157254225Speterstart_cscopes(SCR *sp, EXCMD *cmdp) 15819304Speter{ 15919304Speter size_t blen, len; 16019304Speter char *bp, *cscopes, *p, *t; 161254225Speter CHAR_T *wp; 162254225Speter size_t wlen; 16319304Speter 16419304Speter /* 16519304Speter * EXTENSION #1: 16619304Speter * 16719304Speter * If the CSCOPE_DIRS environment variable is set, we treat it as a 16819304Speter * list of cscope directories that we're using, similar to the tags 16919304Speter * edit option. 17019304Speter * 17119304Speter * XXX 17219304Speter * This should probably be an edit option, although that implies that 17319304Speter * we start/stop cscope processes periodically, instead of once when 17419304Speter * the editor starts. 17519304Speter */ 17619304Speter if ((cscopes = getenv("CSCOPE_DIRS")) == NULL) 17719304Speter return (0); 17819304Speter len = strlen(cscopes); 179254225Speter GET_SPACE_RETC(sp, bp, blen, len); 18019304Speter memcpy(bp, cscopes, len + 1); 18119304Speter 18219304Speter for (cscopes = t = bp; (p = strsep(&t, "\t :")) != NULL;) 183254225Speter if (*p != '\0') { 184254225Speter CHAR2INT(sp, p, strlen(p) + 1, wp, wlen); 185254225Speter (void)cscope_add(sp, cmdp, wp); 186254225Speter } 18719304Speter 18819304Speter FREE_SPACE(sp, bp, blen); 18919304Speter return (0); 19019304Speter} 19119304Speter 19219304Speter/* 19319304Speter * cscope_add -- 19419304Speter * The cscope add command. 19519304Speter */ 19619304Speterstatic int 197254225Spetercscope_add(SCR *sp, EXCMD *cmdp, CHAR_T *dname) 19819304Speter{ 19919304Speter struct stat sb; 20019304Speter EX_PRIVATE *exp; 20119304Speter CSC *csc; 20219304Speter size_t len; 20319304Speter int cur_argc; 204254225Speter char *dbname, *path; 205254225Speter char *np = NULL; 206254225Speter size_t nlen; 20719304Speter 20819304Speter exp = EXP(sp); 20919304Speter 21019304Speter /* 21119304Speter * 0 additional args: usage. 21219304Speter * 1 additional args: matched a file. 21319304Speter * >1 additional args: object, too many args. 21419304Speter */ 21519304Speter cur_argc = cmdp->argc; 216254225Speter if (argv_exp2(sp, cmdp, dname, STRLEN(dname))) { 21719304Speter return (1); 218254225Speter } 21919304Speter if (cmdp->argc == cur_argc) { 22019304Speter (void)csc_help(sp, "add"); 22119304Speter return (1); 22219304Speter } 22319304Speter if (cmdp->argc == cur_argc + 1) 22419304Speter dname = cmdp->argv[cur_argc]->bp; 22519304Speter else { 226254225Speter ex_emsg(sp, np, EXM_FILECOUNT); 22719304Speter return (1); 22819304Speter } 22919304Speter 230254225Speter INT2CHAR(sp, dname, STRLEN(dname)+1, np, nlen); 231254225Speter 23219304Speter /* 23319304Speter * The user can specify a specific file (so they can have multiple 23419304Speter * Cscope databases in a single directory) or a directory. If the 23519304Speter * file doesn't exist, we're done. If it's a directory, append the 23619304Speter * standard database file name and try again. Store the directory 23719304Speter * name regardless so that we can use it as a base for searches. 23819304Speter */ 239254225Speter if (stat(np, &sb)) { 240254225Speter msgq(sp, M_SYSERR, "%s", np); 24119304Speter return (1); 24219304Speter } 24319304Speter if (S_ISDIR(sb.st_mode)) { 244254225Speter if ((path = join(np, CSCOPE_DBFILE)) == NULL) { 245254225Speter msgq(sp, M_SYSERR, NULL); 246254225Speter return (1); 247254225Speter } 24819304Speter if (stat(path, &sb)) { 249254225Speter msgq(sp, M_SYSERR, "%s", path); 250254225Speter free(path); 25119304Speter return (1); 25219304Speter } 253254225Speter free(path); 25419304Speter dbname = CSCOPE_DBFILE; 255254225Speter } else if ((dbname = strrchr(np, '/')) != NULL) 25619304Speter *dbname++ = '\0'; 257254225Speter else { 258254225Speter dbname = np; 259254225Speter np = "."; 260254225Speter } 26119304Speter 26219304Speter /* Allocate a cscope connection structure and initialize its fields. */ 263254225Speter len = strlen(np); 26419304Speter CALLOC_RET(sp, csc, CSC *, 1, sizeof(CSC) + len); 26519304Speter csc->dname = csc->buf; 26619304Speter csc->dlen = len; 267254225Speter memcpy(csc->dname, np, len); 268254225Speter csc->mtim = sb.st_mtimespec; 26919304Speter 27019304Speter /* Get the search paths for the cscope. */ 27119304Speter if (get_paths(sp, csc)) 27219304Speter goto err; 27319304Speter 27419304Speter /* Start the cscope process. */ 27519304Speter if (run_cscope(sp, csc, dbname)) 27619304Speter goto err; 27719304Speter 27819304Speter /* 27919304Speter * Add the cscope connection to the screen's list. From now on, 28019304Speter * on error, we have to call terminate, which expects the csc to 28119304Speter * be on the chain. 28219304Speter */ 283254225Speter SLIST_INSERT_HEAD(exp->cscq, csc, q); 28419304Speter 28519304Speter /* Read the initial prompt from the cscope to make sure it's okay. */ 286254225Speter return read_prompt(sp, csc); 28719304Speter 28819304Spetererr: free(csc); 28919304Speter return (1); 29019304Speter} 29119304Speter 29219304Speter/* 29319304Speter * get_paths -- 29419304Speter * Get the directories to search for the files associated with this 29519304Speter * cscope database. 29619304Speter */ 29719304Speterstatic int 298254225Speterget_paths(SCR *sp, CSC *csc) 29919304Speter{ 30019304Speter struct stat sb; 30119304Speter int fd, nentries; 30219304Speter size_t len; 303254225Speter char *p, **pathp, *buf; 30419304Speter 30519304Speter /* 30619304Speter * EXTENSION #2: 30719304Speter * 30819304Speter * If there's a cscope directory with a file named CSCOPE_PATHS, it 30919304Speter * contains a colon-separated list of paths in which to search for 31019304Speter * files returned by cscope. 31119304Speter * 31219304Speter * XXX 31319304Speter * These paths are absolute paths, and not relative to the cscope 31419304Speter * directory. To fix this, rewrite the each path using the cscope 31519304Speter * directory as a prefix. 31619304Speter */ 317254225Speter if ((buf = join(csc->dname, CSCOPE_PATHS)) == NULL) { 318254225Speter msgq(sp, M_SYSERR, NULL); 319254225Speter return (1); 320254225Speter } 32119304Speter if (stat(buf, &sb) == 0) { 32219304Speter /* Read in the CSCOPE_PATHS file. */ 32319304Speter len = sb.st_size; 32419304Speter MALLOC_RET(sp, csc->pbuf, char *, len + 1); 32519304Speter if ((fd = open(buf, O_RDONLY, 0)) < 0 || 32619304Speter read(fd, csc->pbuf, len) != len) { 32719304Speter msgq_str(sp, M_SYSERR, buf, "%s"); 32819304Speter if (fd >= 0) 32919304Speter (void)close(fd); 330254225Speter free(buf); 33119304Speter return (1); 33219304Speter } 33319304Speter (void)close(fd); 334254225Speter free(buf); 33519304Speter csc->pbuf[len] = '\0'; 33619304Speter 33719304Speter /* Count up the entries. */ 33819304Speter for (nentries = 0, p = csc->pbuf; *p != '\0'; ++p) 33919304Speter if (p[0] == ':' && p[1] != '\0') 34019304Speter ++nentries; 34119304Speter 34219304Speter /* Build an array of pointers to the paths. */ 34319304Speter CALLOC_GOTO(sp, 34419304Speter csc->paths, char **, nentries + 1, sizeof(char **)); 34519304Speter for (pathp = csc->paths, p = strtok(csc->pbuf, ":"); 34619304Speter p != NULL; p = strtok(NULL, ":")) 34719304Speter *pathp++ = p; 34819304Speter return (0); 34919304Speter } 350254225Speter free(buf); 35119304Speter 35219304Speter /* 35319304Speter * If the CSCOPE_PATHS file doesn't exist, we look for files 35419304Speter * relative to the cscope directory. 35519304Speter */ 35619304Speter if ((csc->pbuf = strdup(csc->dname)) == NULL) { 35719304Speter msgq(sp, M_SYSERR, NULL); 35819304Speter return (1); 35919304Speter } 36019304Speter CALLOC_GOTO(sp, csc->paths, char **, 2, sizeof(char *)); 36119304Speter csc->paths[0] = csc->pbuf; 36219304Speter return (0); 36319304Speter 36419304Speteralloc_err: 36519304Speter if (csc->pbuf != NULL) { 36619304Speter free(csc->pbuf); 36719304Speter csc->pbuf = NULL; 36819304Speter } 36919304Speter return (1); 37019304Speter} 37119304Speter 37219304Speter/* 37319304Speter * run_cscope -- 37419304Speter * Fork off the cscope process. 37519304Speter */ 37619304Speterstatic int 377254225Speterrun_cscope(SCR *sp, CSC *csc, char *dbname) 37819304Speter{ 37919304Speter int to_cs[2], from_cs[2]; 380254225Speter char *cmd; 38119304Speter 38219304Speter /* 38319304Speter * Cscope reads from to_cs[0] and writes to from_cs[1]; vi reads from 38419304Speter * from_cs[0] and writes to to_cs[1]. 38519304Speter */ 386254225Speter to_cs[0] = to_cs[1] = from_cs[0] = from_cs[1] = -1; 38719304Speter if (pipe(to_cs) < 0 || pipe(from_cs) < 0) { 38819304Speter msgq(sp, M_SYSERR, "pipe"); 38919304Speter goto err; 39019304Speter } 39119304Speter switch (csc->pid = vfork()) { 392254225Speter char *dn, *dbn; 39319304Speter case -1: 39419304Speter msgq(sp, M_SYSERR, "vfork"); 39519304Spetererr: if (to_cs[0] != -1) 39619304Speter (void)close(to_cs[0]); 39719304Speter if (to_cs[1] != -1) 39819304Speter (void)close(to_cs[1]); 39919304Speter if (from_cs[0] != -1) 40019304Speter (void)close(from_cs[0]); 40119304Speter if (from_cs[1] != -1) 40219304Speter (void)close(from_cs[1]); 40319304Speter return (1); 40419304Speter case 0: /* child: run cscope. */ 40519304Speter (void)dup2(to_cs[0], STDIN_FILENO); 40619304Speter (void)dup2(from_cs[1], STDOUT_FILENO); 40719304Speter (void)dup2(from_cs[1], STDERR_FILENO); 40819304Speter 40919304Speter /* Close unused file descriptors. */ 41019304Speter (void)close(to_cs[1]); 41119304Speter (void)close(from_cs[0]); 41219304Speter 41319304Speter /* Run the cscope command. */ 414254225Speter#define CSCOPE_CMD_FMT "cd %s && exec cscope -dl -f %s" 415254225Speter if ((dn = quote(csc->dname)) == NULL) 416254225Speter goto nomem; 417254225Speter if ((dbn = quote(dbname)) == NULL) { 418254225Speter free(dn); 419254225Speter goto nomem; 420254225Speter } 421254225Speter (void)asprintf(&cmd, CSCOPE_CMD_FMT, dn, dbn); 422254225Speter free(dbn); 423254225Speter free(dn); 424254225Speter if (cmd == NULL) { 425254225Speternomem: msgq(sp, M_SYSERR, NULL); 426254225Speter _exit (1); 427254225Speter } 428254225Speter (void)execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); 42919304Speter msgq_str(sp, M_SYSERR, cmd, "execl: %s"); 430254225Speter free(cmd); 43119304Speter _exit (127); 43219304Speter /* NOTREACHED */ 43319304Speter default: /* parent. */ 43419304Speter /* Close unused file descriptors. */ 43519304Speter (void)close(to_cs[0]); 43619304Speter (void)close(from_cs[1]); 43719304Speter 43819304Speter /* 43919304Speter * Save the file descriptors for later duplication, and 44019304Speter * reopen as streams. 44119304Speter */ 44219304Speter csc->to_fd = to_cs[1]; 44319304Speter csc->to_fp = fdopen(to_cs[1], "w"); 44419304Speter csc->from_fd = from_cs[0]; 44519304Speter csc->from_fp = fdopen(from_cs[0], "r"); 44619304Speter break; 44719304Speter } 44819304Speter return (0); 44919304Speter} 45019304Speter 45119304Speter/* 45219304Speter * cscope_find -- 45319304Speter * The cscope find command. 45419304Speter */ 45519304Speterstatic int 456254225Spetercscope_find(SCR *sp, EXCMD *cmdp, CHAR_T *pattern) 45719304Speter{ 45819304Speter CSC *csc, *csc_next; 45919304Speter EX_PRIVATE *exp; 46019304Speter FREF *frp; 46119304Speter TAGQ *rtqp, *tqp; 46219304Speter TAG *rtp; 46319304Speter recno_t lno; 46419304Speter size_t cno, search; 46519304Speter int force, istmp, matches; 466254225Speter char *np = NULL; 467254225Speter size_t nlen; 46819304Speter 46919304Speter exp = EXP(sp); 47019304Speter 47119304Speter /* Check for connections. */ 472254225Speter if (SLIST_EMPTY(exp->cscq)) { 47319304Speter msgq(sp, M_ERR, "310|No cscope connections running"); 47419304Speter return (1); 47519304Speter } 47619304Speter 47719304Speter /* 47819304Speter * Allocate all necessary memory before doing anything hard. If the 47919304Speter * tags stack is empty, we'll need the `local context' TAGQ structure 48019304Speter * later. 48119304Speter */ 48219304Speter rtp = NULL; 48319304Speter rtqp = NULL; 484254225Speter if (TAILQ_EMPTY(exp->tq)) { 48519304Speter /* Initialize the `local context' tag queue structure. */ 48619304Speter CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ)); 487254225Speter TAILQ_INIT(rtqp->tagq); 48819304Speter 48919304Speter /* Initialize and link in its tag structure. */ 49019304Speter CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG)); 491254225Speter TAILQ_INSERT_HEAD(rtqp->tagq, rtp, q); 492254225Speter rtqp->current = rtp; 49319304Speter } 49419304Speter 49519304Speter /* Create the cscope command. */ 496254225Speter INT2CHAR(sp, pattern, STRLEN(pattern) + 1, np, nlen); 497254225Speter np = strdup(np); 498254225Speter if ((tqp = create_cs_cmd(sp, np, &search)) == NULL) 49919304Speter goto err; 500254225Speter if (np != NULL) 501254225Speter free(np); 50219304Speter 50319304Speter /* 50419304Speter * Stick the current context in a convenient place, we'll lose it 50519304Speter * when we switch files. 50619304Speter */ 50719304Speter frp = sp->frp; 50819304Speter lno = sp->lno; 50919304Speter cno = sp->cno; 51019304Speter istmp = F_ISSET(sp->frp, FR_TMPFILE) && !F_ISSET(cmdp, E_NEWSCREEN); 51119304Speter 51219304Speter /* Search all open connections for a match. */ 51319304Speter matches = 0; 514254225Speter /* Copy next connect here in case csc is killed. */ 515254225Speter SLIST_FOREACH_SAFE(csc, exp->cscq, q, csc_next) { 51619304Speter /* 51719304Speter * Send the command to the cscope program. (We skip the 51819304Speter * first two bytes of the command, because we stored the 51919304Speter * search cscope command character and a leading space 52019304Speter * there.) 52119304Speter */ 522254225Speter (void)fprintf(csc->to_fp, "%lu%s\n", search, tqp->tag + 2); 52319304Speter (void)fflush(csc->to_fp); 52419304Speter 52519304Speter /* Read the output. */ 526254225Speter if (parse(sp, csc, tqp, &matches)) 527254225Speter goto nomatch; 52819304Speter } 52919304Speter 53019304Speter if (matches == 0) { 53119304Speter msgq(sp, M_INFO, "278|No matches for query"); 532254225Speternomatch: if (rtp != NULL) 533254225Speter free(rtp); 534254225Speter if (rtqp != NULL) 535254225Speter free(rtqp); 536254225Speter tagq_free(sp, tqp); 537254225Speter return (1); 53819304Speter } 53919304Speter 54019304Speter /* Try to switch to the first tag. */ 54119304Speter force = FL_ISSET(cmdp->iflags, E_C_FORCE); 54219304Speter if (F_ISSET(cmdp, E_NEWSCREEN)) { 54319304Speter if (ex_tag_Nswitch(sp, tqp->current, force)) 54419304Speter goto err; 54519304Speter 54619304Speter /* Everything else gets done in the new screen. */ 54719304Speter sp = sp->nextdisp; 54819304Speter exp = EXP(sp); 54919304Speter } else 55019304Speter if (ex_tag_nswitch(sp, tqp->current, force)) 55119304Speter goto err; 55219304Speter 55319304Speter /* 55419304Speter * If this is the first tag, put a `current location' queue entry 55519304Speter * in place, so we can pop all the way back to the current mark. 55619304Speter * Note, it doesn't point to much of anything, it's a placeholder. 55719304Speter */ 558254225Speter if (TAILQ_EMPTY(exp->tq)) { 559254225Speter TAILQ_INSERT_HEAD(exp->tq, rtqp, q); 56019304Speter } else 561254225Speter rtqp = TAILQ_FIRST(exp->tq); 56219304Speter 56319304Speter /* Link the current TAGQ structure into place. */ 564254225Speter TAILQ_INSERT_HEAD(exp->tq, tqp, q); 56519304Speter 56619304Speter (void)cscope_search(sp, tqp, tqp->current); 56719304Speter 56819304Speter /* 56919304Speter * Move the current context from the temporary save area into the 57019304Speter * right structure. 57119304Speter * 57219304Speter * If we were in a temporary file, we don't have a context to which 57319304Speter * we can return, so just make it be the same as what we're moving 57419304Speter * to. It will be a little odd that ^T doesn't change anything, but 57519304Speter * I don't think it's a big deal. 57619304Speter */ 57719304Speter if (istmp) { 57819304Speter rtqp->current->frp = sp->frp; 57919304Speter rtqp->current->lno = sp->lno; 58019304Speter rtqp->current->cno = sp->cno; 58119304Speter } else { 58219304Speter rtqp->current->frp = frp; 58319304Speter rtqp->current->lno = lno; 58419304Speter rtqp->current->cno = cno; 58519304Speter } 58619304Speter 58719304Speter return (0); 58819304Speter 58919304Spetererr: 59019304Speteralloc_err: 59119304Speter if (rtqp != NULL) 59219304Speter free(rtqp); 59319304Speter if (rtp != NULL) 59419304Speter free(rtp); 595254225Speter if (np != NULL) 596254225Speter free(np); 59719304Speter return (1); 59819304Speter} 59919304Speter 60019304Speter/* 60119304Speter * create_cs_cmd -- 60219304Speter * Build a cscope command, creating and initializing the base TAGQ. 60319304Speter */ 60419304Speterstatic TAGQ * 605254225Spetercreate_cs_cmd(SCR *sp, char *pattern, size_t *searchp) 60619304Speter{ 60719304Speter CB *cbp; 60819304Speter TAGQ *tqp; 60919304Speter size_t tlen; 61019304Speter char *p; 61119304Speter 61219304Speter /* 61319304Speter * Cscope supports a "change pattern" command which we never use, 61419304Speter * cscope command 5. Set CSCOPE_QUERIES[5] to " " since the user 61519304Speter * can't pass " " as the first character of pattern. That way the 61619304Speter * user can't ask for pattern 5 so we don't need any special-case 61719304Speter * code. 61819304Speter */ 61919304Speter#define CSCOPE_QUERIES "sgdct efi" 62019304Speter 62119304Speter if (pattern == NULL) 62219304Speter goto usage; 62319304Speter 62419304Speter /* Skip leading blanks, check for command character. */ 625254225Speter for (; cmdskip(pattern[0]); ++pattern); 626254225Speter if (pattern[0] == '\0' || !cmdskip(pattern[1])) 62719304Speter goto usage; 62819304Speter for (*searchp = 0, p = CSCOPE_QUERIES; 62919304Speter *p != '\0' && *p != pattern[0]; ++*searchp, ++p); 63019304Speter if (*p == '\0') { 63119304Speter msgq(sp, M_ERR, 63219304Speter "311|%s: unknown search type: use one of %s", 63319304Speter KEY_NAME(sp, pattern[0]), CSCOPE_QUERIES); 63419304Speter return (NULL); 63519304Speter } 63619304Speter 63719304Speter /* Skip <blank> characters to the pattern. */ 638254225Speter for (p = pattern + 1; *p != '\0' && cmdskip(*p); ++p); 63919304Speter if (*p == '\0') { 64019304Speterusage: (void)csc_help(sp, "find"); 64119304Speter return (NULL); 64219304Speter } 64319304Speter 64419304Speter /* The user can specify the contents of a buffer as the pattern. */ 64519304Speter cbp = NULL; 64619304Speter if (p[0] == '"' && p[1] != '\0' && p[2] == '\0') 64719304Speter CBNAME(sp, cbp, p[1]); 64819304Speter if (cbp != NULL) { 649254225Speter INT2CHAR(sp, TAILQ_FIRST(cbp->textq)->lb, 650254225Speter TAILQ_FIRST(cbp->textq)->len, p, tlen); 65119304Speter } else 65219304Speter tlen = strlen(p); 65319304Speter 65419304Speter /* Allocate and initialize the TAGQ structure. */ 65519304Speter CALLOC(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + tlen + 3); 65619304Speter if (tqp == NULL) 65719304Speter return (NULL); 658254225Speter TAILQ_INIT(tqp->tagq); 65919304Speter tqp->tag = tqp->buf; 66019304Speter tqp->tag[0] = pattern[0]; 66119304Speter tqp->tag[1] = ' '; 66219304Speter tqp->tlen = tlen + 2; 66319304Speter memcpy(tqp->tag + 2, p, tlen); 66419304Speter tqp->tag[tlen + 2] = '\0'; 66519304Speter F_SET(tqp, TAG_CSCOPE); 66619304Speter 66719304Speter return (tqp); 66819304Speter} 66919304Speter 67019304Speter/* 67119304Speter * parse -- 67219304Speter * Parse the cscope output. 67319304Speter */ 67419304Speterstatic int 675254225Speterparse(SCR *sp, CSC *csc, TAGQ *tqp, int *matchesp) 67619304Speter{ 67719304Speter TAG *tp; 678254225Speter recno_t slno = 0; 679254225Speter size_t dlen, nlen = 0, slen = 0; 680254225Speter int ch, i, isolder = 0, nlines; 681254225Speter char *dname = NULL, *name = NULL, *search, *p, *t, dummy[2], buf[2048]; 682254225Speter CHAR_T *wp; 683254225Speter size_t wlen; 68419304Speter 68519304Speter for (;;) { 68619304Speter if (!fgets(buf, sizeof(buf), csc->from_fp)) 68719304Speter goto io_err; 68819304Speter 68919304Speter /* 69019304Speter * If the database is out of date, or there's some other 69119304Speter * problem, cscope will output error messages before the 69219304Speter * number-of-lines output. Display/discard any output 69319304Speter * that doesn't match what we want. 69419304Speter */ 69519304Speter#define CSCOPE_NLINES_FMT "cscope: %d lines%1[\n]" 69619304Speter if (sscanf(buf, CSCOPE_NLINES_FMT, &nlines, dummy) == 2) 69719304Speter break; 69819304Speter if ((p = strchr(buf, '\n')) != NULL) 69919304Speter *p = '\0'; 70019304Speter msgq(sp, M_ERR, "%s: \"%s\"", csc->dname, buf); 70119304Speter } 70219304Speter 70319304Speter while (nlines--) { 70419304Speter if (fgets(buf, sizeof(buf), csc->from_fp) == NULL) 70519304Speter goto io_err; 70619304Speter 70719304Speter /* If the line's too long for the buffer, discard it. */ 70819304Speter if ((p = strchr(buf, '\n')) == NULL) { 70919304Speter while ((ch = getc(csc->from_fp)) != EOF && ch != '\n'); 71019304Speter continue; 71119304Speter } 71219304Speter *p = '\0'; 71319304Speter 71419304Speter /* 71519304Speter * The cscope output is in the following format: 71619304Speter * 71719304Speter * <filename> <context> <line number> <pattern> 71819304Speter * 71919304Speter * Figure out how long everything is so we can allocate in one 72019304Speter * swell foop, but discard anything that looks wrong. 72119304Speter */ 72219304Speter for (p = buf, i = 0; 72319304Speter i < 3 && (t = strsep(&p, "\t ")) != NULL; ++i) 72419304Speter switch (i) { 72519304Speter case 0: /* Filename. */ 72619304Speter name = t; 72719304Speter nlen = strlen(name); 72819304Speter break; 72919304Speter case 1: /* Context. */ 73019304Speter break; 73119304Speter case 2: /* Line number. */ 73219304Speter slno = (recno_t)atol(t); 73319304Speter break; 73419304Speter } 73519304Speter if (i != 3 || p == NULL || t == NULL) 73619304Speter continue; 73719304Speter 73819304Speter /* The rest of the string is the search pattern. */ 73919304Speter search = p; 74019304Speter slen = strlen(p); 74119304Speter 74219304Speter /* Resolve the file name. */ 74319304Speter csc_file(sp, csc, name, &dname, &dlen, &isolder); 74419304Speter 74519304Speter /* 74619304Speter * If the file is older than the cscope database, that is, 74719304Speter * the database was built since the file was last modified, 74819304Speter * or there wasn't a search string, use the line number. 74919304Speter */ 75019304Speter if (isolder || strcmp(search, "<unknown>") == 0) { 75119304Speter search = NULL; 75219304Speter slen = 0; 75319304Speter } 75419304Speter 75519304Speter /* 75619304Speter * Allocate and initialize a tag structure plus the variable 75719304Speter * length cscope information that follows it. 75819304Speter */ 75919304Speter CALLOC_RET(sp, tp, 760254225Speter TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + 761254225Speter (slen + 1) * sizeof(CHAR_T)); 762254225Speter tp->fname = (char *)tp->buf; 763254225Speter if (dlen == 1 && *dname == '.') 764254225Speter --dlen; 765254225Speter else if (dlen != 0) { 76619304Speter memcpy(tp->fname, dname, dlen); 76719304Speter tp->fname[dlen] = '/'; 76819304Speter ++dlen; 76919304Speter } 77019304Speter memcpy(tp->fname + dlen, name, nlen + 1); 77119304Speter tp->fnlen = dlen + nlen; 77219304Speter tp->slno = slno; 773254225Speter tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1); 774254225Speter CHAR2INT(sp, search, slen + 1, wp, wlen); 775254225Speter MEMCPY(tp->search, wp, (tp->slen = slen) + 1); 776254225Speter TAILQ_INSERT_TAIL(tqp->tagq, tp, q); 77719304Speter 778254225Speter /* Try to preset the tag within the current file. */ 779254225Speter if (sp->frp != NULL && sp->frp->name != NULL && 780254225Speter tqp->current == NULL && !strcmp(tp->fname, sp->frp->name)) 781254225Speter tqp->current = tp; 782254225Speter 78319304Speter ++*matchesp; 78419304Speter } 78519304Speter 786254225Speter if (tqp->current == NULL) 787254225Speter tqp->current = TAILQ_FIRST(tqp->tagq); 78819304Speter 789254225Speter return read_prompt(sp, csc); 790254225Speter 79119304Speterio_err: if (feof(csc->from_fp)) 79219304Speter errno = EIO; 79319304Speter msgq_str(sp, M_SYSERR, "%s", csc->dname); 79419304Speter terminate(sp, csc, 0); 79519304Speter return (1); 79619304Speter} 79719304Speter 79819304Speter/* 79919304Speter * csc_file -- 80019304Speter * Search for the right path to this file. 80119304Speter */ 80219304Speterstatic void 803254225Spetercsc_file(SCR *sp, CSC *csc, char *name, char **dirp, size_t *dlenp, int *isolderp) 80419304Speter{ 80519304Speter struct stat sb; 806254225Speter char **pp, *buf; 80719304Speter 80819304Speter /* 80919304Speter * Check for the file in all of the listed paths. If we don't 81019304Speter * find it, we simply return it unchanged. We have to do this 81119304Speter * now, even though it's expensive, because if the user changes 81219304Speter * directories, we can't change our minds as to where the file 81319304Speter * lives. 81419304Speter */ 81519304Speter for (pp = csc->paths; *pp != NULL; ++pp) { 816254225Speter if ((buf = join(*pp, name)) == NULL) { 817254225Speter msgq(sp, M_SYSERR, NULL); 818254225Speter *dlenp = 0; 819254225Speter return; 820254225Speter } 82119304Speter if (stat(buf, &sb) == 0) { 822254225Speter free(buf); 82319304Speter *dirp = *pp; 82419304Speter *dlenp = strlen(*pp); 825254225Speter *isolderp = timespeccmp( 826254225Speter &sb.st_mtimespec, &csc->mtim, <); 82719304Speter return; 82819304Speter } 829254225Speter free(buf); 83019304Speter } 83119304Speter *dlenp = 0; 83219304Speter} 83319304Speter 83419304Speter/* 83519304Speter * cscope_help -- 83619304Speter * The cscope help command. 83719304Speter */ 83819304Speterstatic int 839254225Spetercscope_help(SCR *sp, EXCMD *cmdp, CHAR_T *subcmd) 84019304Speter{ 841254225Speter char *np; 842254225Speter size_t nlen; 843254225Speter 844254225Speter INT2CHAR(sp, subcmd, STRLEN(subcmd) + 1, np, nlen); 845254225Speter return (csc_help(sp, np)); 84619304Speter} 84719304Speter 84819304Speter/* 84919304Speter * csc_help -- 85019304Speter * Display help/usage messages. 85119304Speter */ 85219304Speterstatic int 853254225Spetercsc_help(SCR *sp, char *cmd) 85419304Speter{ 85519304Speter CC const *ccp; 85619304Speter 85719304Speter if (cmd != NULL && *cmd != '\0') 85819304Speter if ((ccp = lookup_ccmd(cmd)) == NULL) { 85919304Speter ex_printf(sp, 86019304Speter "%s doesn't match any cscope command\n", cmd); 86119304Speter return (1); 86219304Speter } else { 86319304Speter ex_printf(sp, 86419304Speter "Command: %s (%s)\n", ccp->name, ccp->help_msg); 86519304Speter ex_printf(sp, " Usage: %s\n", ccp->usage_msg); 86619304Speter return (0); 86719304Speter } 86819304Speter 86919304Speter ex_printf(sp, "cscope commands:\n"); 87019304Speter for (ccp = cscope_cmds; ccp->name != NULL; ++ccp) 87119304Speter ex_printf(sp, " %*s: %s\n", 5, ccp->name, ccp->help_msg); 87219304Speter return (0); 87319304Speter} 87419304Speter 87519304Speter/* 87619304Speter * cscope_kill -- 87719304Speter * The cscope kill command. 87819304Speter */ 87919304Speterstatic int 880254225Spetercscope_kill(SCR *sp, EXCMD *cmdp, CHAR_T *cn) 88119304Speter{ 882254225Speter char *np; 883254225Speter size_t nlen; 884254225Speter int n = 1; 885254225Speter 886254225Speter if (*cn) { 887254225Speter INT2CHAR(sp, cn, STRLEN(cn) + 1, np, nlen); 888254225Speter n = atoi(np); 889254225Speter } 890254225Speter return (terminate(sp, NULL, n)); 89119304Speter} 89219304Speter 89319304Speter/* 89419304Speter * terminate -- 89519304Speter * Detach from a cscope process. 89619304Speter */ 89719304Speterstatic int 898254225Speterterminate(SCR *sp, CSC *csc, int n) 89919304Speter{ 90019304Speter EX_PRIVATE *exp; 901254225Speter int i = 0, pstat; 902254225Speter CSC *cp, *pre_cp = NULL; 90319304Speter 90419304Speter exp = EXP(sp); 90519304Speter 90619304Speter /* 907254225Speter * We either get a csc structure or a number. Locate and remove 908254225Speter * the candidate which matches the structure or the number. 90919304Speter */ 910254225Speter if (csc == NULL && n < 1) 911254225Speter goto badno; 912254225Speter SLIST_FOREACH(cp, exp->cscq, q) { 913254225Speter ++i; 914254225Speter if (csc == NULL ? i != n : cp != csc) { 915254225Speter pre_cp = cp; 916254225Speter continue; 91719304Speter } 918254225Speter if (cp == SLIST_FIRST(exp->cscq)) 919254225Speter SLIST_REMOVE_HEAD(exp->cscq, q); 920254225Speter else 921254225Speter SLIST_REMOVE_AFTER(pre_cp, q); 922254225Speter csc = cp; 923254225Speter break; 92419304Speter } 925254225Speter if (csc == NULL) { 926254225Speterbadno: msgq(sp, M_ERR, "312|%d: no such cscope session", n); 927254225Speter return (1); 928254225Speter } 92919304Speter 93019304Speter /* 93119304Speter * XXX 93219304Speter * Theoretically, we have the only file descriptors to the process, 93319304Speter * so closing them should let it exit gracefully, deleting temporary 934254225Speter * files, etc. However, the earlier created cscope processes seems 935254225Speter * to refuse to quit unless we send a SIGTERM signal. 93619304Speter */ 93719304Speter if (csc->from_fp != NULL) 93819304Speter (void)fclose(csc->from_fp); 93919304Speter if (csc->to_fp != NULL) 94019304Speter (void)fclose(csc->to_fp); 941254225Speter if (i > 1) 942254225Speter (void)kill(csc->pid, SIGTERM); 94319304Speter (void)waitpid(csc->pid, &pstat, 0); 94419304Speter 94519304Speter /* Discard cscope connection information. */ 94619304Speter if (csc->pbuf != NULL) 94719304Speter free(csc->pbuf); 94819304Speter if (csc->paths != NULL) 94919304Speter free(csc->paths); 95019304Speter free(csc); 95119304Speter return (0); 95219304Speter} 95319304Speter 95419304Speter/* 95519304Speter * cscope_reset -- 95619304Speter * The cscope reset command. 95719304Speter */ 95819304Speterstatic int 959254225Spetercscope_reset(SCR *sp, EXCMD *cmdp, CHAR_T *notusedp) 96019304Speter{ 961254225Speter return cscope_end(sp); 962254225Speter} 963254225Speter 964254225Speter/* 965254225Speter * cscope_end -- 966254225Speter * End all cscope connections. 967254225Speter * 968254225Speter * PUBLIC: int cscope_end __P((SCR *)); 969254225Speter */ 970254225Speterint 971254225Spetercscope_end(SCR *sp) 972254225Speter{ 97319304Speter EX_PRIVATE *exp; 97419304Speter 975254225Speter for (exp = EXP(sp); !SLIST_EMPTY(exp->cscq);) 976254225Speter if (terminate(sp, NULL, 1)) 97719304Speter return (1); 97819304Speter return (0); 97919304Speter} 98019304Speter 98119304Speter/* 98219304Speter * cscope_display -- 98319304Speter * Display current connections. 98419304Speter * 98519304Speter * PUBLIC: int cscope_display __P((SCR *)); 98619304Speter */ 98719304Speterint 988254225Spetercscope_display(SCR *sp) 98919304Speter{ 99019304Speter EX_PRIVATE *exp; 99119304Speter CSC *csc; 992254225Speter int i = 0; 99319304Speter 99419304Speter exp = EXP(sp); 995254225Speter if (SLIST_EMPTY(exp->cscq)) { 99619304Speter ex_printf(sp, "No cscope connections.\n"); 99719304Speter return (0); 99819304Speter } 999254225Speter SLIST_FOREACH(csc, exp->cscq, q) 1000254225Speter ex_printf(sp, "%2d %s (process %lu)\n", 1001254225Speter ++i, csc->dname, (u_long)csc->pid); 100219304Speter return (0); 100319304Speter} 100419304Speter 100519304Speter/* 100619304Speter * cscope_search -- 100719304Speter * Search a file for a cscope entry. 100819304Speter * 100919304Speter * PUBLIC: int cscope_search __P((SCR *, TAGQ *, TAG *)); 101019304Speter */ 101119304Speterint 1012254225Spetercscope_search(SCR *sp, TAGQ *tqp, TAG *tp) 101319304Speter{ 101419304Speter MARK m; 101519304Speter 101619304Speter /* If we don't have a search pattern, use the line number. */ 101719304Speter if (tp->search == NULL) { 101819304Speter if (!db_exist(sp, tp->slno)) { 101919304Speter tag_msg(sp, TAG_BADLNO, tqp->tag); 102019304Speter return (1); 102119304Speter } 102219304Speter m.lno = tp->slno; 102319304Speter } else { 102419304Speter /* 102519304Speter * Search for the tag; cheap fallback for C functions 102619304Speter * if the name is the same but the arguments have changed. 102719304Speter */ 102819304Speter m.lno = 1; 102919304Speter m.cno = 0; 103019304Speter if (f_search(sp, &m, &m, 103119304Speter tp->search, tp->slen, NULL, SEARCH_CSCOPE | SEARCH_FILE)) { 103219304Speter tag_msg(sp, TAG_SEARCH, tqp->tag); 103319304Speter return (1); 103419304Speter } 103519304Speter 103619304Speter /* 103719304Speter * !!! 103819304Speter * Historically, tags set the search direction if it wasn't 103919304Speter * already set. 104019304Speter */ 104119304Speter if (sp->searchdir == NOTSET) 104219304Speter sp->searchdir = FORWARD; 104319304Speter } 104419304Speter 104519304Speter /* 104619304Speter * !!! 104719304Speter * Tags move to the first non-blank, NOT the search pattern start. 104819304Speter */ 104919304Speter sp->lno = m.lno; 105019304Speter sp->cno = 0; 105119304Speter (void)nonblank(sp, sp->lno, &sp->cno); 105219304Speter return (0); 105319304Speter} 105419304Speter 105519304Speter 105619304Speter/* 105719304Speter * lookup_ccmd -- 105819304Speter * Return a pointer to the command structure. 105919304Speter */ 106019304Speterstatic CC const * 1061254225Speterlookup_ccmd(char *name) 106219304Speter{ 106319304Speter CC const *ccp; 106419304Speter size_t len; 106519304Speter 106619304Speter len = strlen(name); 106719304Speter for (ccp = cscope_cmds; ccp->name != NULL; ++ccp) 106819304Speter if (strncmp(name, ccp->name, len) == 0) 106919304Speter return (ccp); 107019304Speter return (NULL); 107119304Speter} 107219304Speter 107319304Speter/* 107419304Speter * read_prompt -- 107519304Speter * Read a prompt from cscope. 107619304Speter */ 107719304Speterstatic int 1078254225Speterread_prompt(SCR *sp, CSC *csc) 107919304Speter{ 108019304Speter int ch; 108119304Speter 108219304Speter#define CSCOPE_PROMPT ">> " 108319304Speter for (;;) { 108419304Speter while ((ch = 108519304Speter getc(csc->from_fp)) != EOF && ch != CSCOPE_PROMPT[0]); 108619304Speter if (ch == EOF) { 108719304Speter terminate(sp, csc, 0); 108819304Speter return (1); 108919304Speter } 109019304Speter if (getc(csc->from_fp) != CSCOPE_PROMPT[1]) 109119304Speter continue; 109219304Speter if (getc(csc->from_fp) != CSCOPE_PROMPT[2]) 109319304Speter continue; 109419304Speter break; 109519304Speter } 109619304Speter return (0); 109719304Speter} 1098