119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * This code is derived from software contributed to Berkeley by 819304Speter * David Hitz of Auspex Systems, Inc. 919304Speter * 1019304Speter * See the LICENSE file for redistribution information. 1119304Speter */ 1219304Speter 1319304Speter#include "config.h" 1419304Speter 1519304Speter#ifndef lint 16254225Speterstatic const char sccsid[] = "$Id: ex_tag.c,v 10.54 2012/04/12 07:17:30 zy Exp $"; 1719304Speter#endif /* not lint */ 1819304Speter 19254225Speter#include <sys/types.h> 2019304Speter#include <sys/mman.h> 2119304Speter#include <sys/queue.h> 2219304Speter#include <sys/stat.h> 2319304Speter 2419304Speter#include <bitstring.h> 2519304Speter#include <ctype.h> 2619304Speter#include <errno.h> 2719304Speter#include <fcntl.h> 2819304Speter#include <limits.h> 2919304Speter#include <stddef.h> 3019304Speter#include <stdio.h> 3119304Speter#include <stdlib.h> 3219304Speter#include <string.h> 3319304Speter#include <unistd.h> 3419304Speter 3519304Speter#include "../common/common.h" 3619304Speter#include "../vi/vi.h" 3719304Speter#include "tag.h" 3819304Speter 39281373Sbaptstatic char *binary_search(char *, char *, char *); 40281373Sbaptstatic int compare(char *, char *, char *); 41281373Sbaptstatic void ctag_file(SCR *, TAGF *, char *, char **, size_t *); 42281373Sbaptstatic int ctag_search(SCR *, CHAR_T *, size_t, char *); 43281373Sbaptstatic int ctag_sfile(SCR *, TAGF *, TAGQ *, char *); 44281373Sbaptstatic TAGQ *ctag_slist(SCR *, CHAR_T *); 45281373Sbaptstatic char *linear_search(char *, char *, char *, long); 46281373Sbaptstatic int tag_copy(SCR *, TAG *, TAG **); 47281373Sbaptstatic int tag_pop(SCR *, TAGQ *, int); 48281373Sbaptstatic int tagf_copy(SCR *, TAGF *, TAGF **); 49281373Sbaptstatic int tagf_free(SCR *, TAGF *); 50281373Sbaptstatic int tagq_copy(SCR *, TAGQ *, TAGQ **); 5119304Speter 5219304Speter/* 5319304Speter * ex_tag_first -- 5419304Speter * The tag code can be entered from main, e.g., "vi -t tag". 5519304Speter * 56281373Sbapt * PUBLIC: int ex_tag_first(SCR *, CHAR_T *); 5719304Speter */ 5819304Speterint 59254225Speterex_tag_first(SCR *sp, CHAR_T *tagarg) 6019304Speter{ 6119304Speter EXCMD cmd; 6219304Speter 6319304Speter /* Build an argument for the ex :tag command. */ 64254225Speter ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, OOBLNO, 0); 65254225Speter argv_exp0(sp, &cmd, tagarg, STRLEN(tagarg)); 6619304Speter 6719304Speter /* 6819304Speter * XXX 6919304Speter * Historic vi went ahead and created a temporary file when it failed 7019304Speter * to find the tag. We match historic practice, but don't distinguish 7119304Speter * between real error and failure to find the tag. 7219304Speter */ 7319304Speter if (ex_tag_push(sp, &cmd)) 7419304Speter return (0); 7519304Speter 7619304Speter /* Display tags in the center of the screen. */ 7719304Speter F_CLR(sp, SC_SCR_TOP); 7819304Speter F_SET(sp, SC_SCR_CENTER); 7919304Speter 8019304Speter return (0); 8119304Speter} 8219304Speter 8319304Speter/* 8419304Speter * ex_tag_push -- ^] 8519304Speter * :tag[!] [string] 8619304Speter * 8719304Speter * Enter a new TAGQ context based on a ctag string. 8819304Speter * 89281373Sbapt * PUBLIC: int ex_tag_push(SCR *, EXCMD *); 9019304Speter */ 9119304Speterint 92254225Speterex_tag_push(SCR *sp, EXCMD *cmdp) 9319304Speter{ 9419304Speter EX_PRIVATE *exp; 95254225Speter TAGQ *tqp; 9619304Speter long tl; 9719304Speter 9819304Speter exp = EXP(sp); 9919304Speter switch (cmdp->argc) { 10019304Speter case 1: 10119304Speter if (exp->tag_last != NULL) 10219304Speter free(exp->tag_last); 10319304Speter 104254225Speter if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp, 105254225Speter cmdp->argv[0]->len)) == NULL) { 10619304Speter msgq(sp, M_SYSERR, NULL); 10719304Speter return (1); 10819304Speter } 10919304Speter 11019304Speter /* Taglength may limit the number of characters. */ 11119304Speter if ((tl = 112254225Speter O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl) 11319304Speter exp->tag_last[tl] = '\0'; 11419304Speter break; 11519304Speter case 0: 11619304Speter if (exp->tag_last == NULL) { 11719304Speter msgq(sp, M_ERR, "158|No previous tag entered"); 11819304Speter return (1); 11919304Speter } 12019304Speter break; 12119304Speter default: 12219304Speter abort(); 12319304Speter } 12419304Speter 12519304Speter /* Get the tag information. */ 12619304Speter if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL) 12719304Speter return (1); 12819304Speter 129254225Speter if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN), 130254225Speter FL_ISSET(cmdp->iflags, E_C_FORCE))) 131254225Speter return 1; 13219304Speter 133254225Speter return 0; 13419304Speter} 13519304Speter 13619304Speter/* 13719304Speter * ex_tag_next -- 13819304Speter * Switch context to the next TAG. 13919304Speter * 140281373Sbapt * PUBLIC: int ex_tag_next(SCR *, EXCMD *); 14119304Speter */ 14219304Speterint 143254225Speterex_tag_next(SCR *sp, EXCMD *cmdp) 14419304Speter{ 14519304Speter EX_PRIVATE *exp; 14619304Speter TAG *tp; 14719304Speter TAGQ *tqp; 148254225Speter char *np; 149254225Speter size_t nlen; 15019304Speter 15119304Speter exp = EXP(sp); 152254225Speter if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) { 15319304Speter tag_msg(sp, TAG_EMPTY, NULL); 15419304Speter return (1); 15519304Speter } 156254225Speter if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) { 15719304Speter msgq(sp, M_ERR, "282|Already at the last tag of this group"); 15819304Speter return (1); 15919304Speter } 16019304Speter if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) 16119304Speter return (1); 16219304Speter tqp->current = tp; 16319304Speter 16419304Speter if (F_ISSET(tqp, TAG_CSCOPE)) 16519304Speter (void)cscope_search(sp, tqp, tp); 16619304Speter else 16719304Speter (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); 168254225Speter if (tqp->current->msg) { 169254225Speter INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 170254225Speter np, nlen); 171254225Speter msgq(sp, M_INFO, "%s", np); 172254225Speter } 17319304Speter return (0); 17419304Speter} 17519304Speter 17619304Speter/* 17719304Speter * ex_tag_prev -- 17819304Speter * Switch context to the next TAG. 17919304Speter * 180281373Sbapt * PUBLIC: int ex_tag_prev(SCR *, EXCMD *); 18119304Speter */ 18219304Speterint 183254225Speterex_tag_prev(SCR *sp, EXCMD *cmdp) 18419304Speter{ 18519304Speter EX_PRIVATE *exp; 18619304Speter TAG *tp; 18719304Speter TAGQ *tqp; 188254225Speter char *np; 189254225Speter size_t nlen; 19019304Speter 19119304Speter exp = EXP(sp); 192254225Speter if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) { 19319304Speter tag_msg(sp, TAG_EMPTY, NULL); 19419304Speter return (0); 19519304Speter } 196254225Speter if ((tp = TAILQ_PREV(tqp->current, _tagqh, q)) == NULL) { 19719304Speter msgq(sp, M_ERR, "255|Already at the first tag of this group"); 19819304Speter return (1); 19919304Speter } 20019304Speter if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE))) 20119304Speter return (1); 20219304Speter tqp->current = tp; 20319304Speter 20419304Speter if (F_ISSET(tqp, TAG_CSCOPE)) 20519304Speter (void)cscope_search(sp, tqp, tp); 20619304Speter else 20719304Speter (void)ctag_search(sp, tp->search, tp->slen, tqp->tag); 208254225Speter if (tqp->current->msg) { 209254225Speter INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 210254225Speter np, nlen); 211254225Speter msgq(sp, M_INFO, "%s", np); 212254225Speter } 21319304Speter return (0); 21419304Speter} 21519304Speter 21619304Speter/* 21719304Speter * ex_tag_nswitch -- 21819304Speter * Switch context to the specified TAG. 21919304Speter * 220281373Sbapt * PUBLIC: int ex_tag_nswitch(SCR *, TAG *, int); 22119304Speter */ 22219304Speterint 223254225Speterex_tag_nswitch(SCR *sp, TAG *tp, int force) 22419304Speter{ 22519304Speter /* Get a file structure. */ 22619304Speter if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL) 22719304Speter return (1); 22819304Speter 22919304Speter /* If not changing files, return, we're done. */ 23019304Speter if (tp->frp == sp->frp) 23119304Speter return (0); 23219304Speter 23319304Speter /* Check for permission to leave. */ 23419304Speter if (file_m1(sp, force, FS_ALL | FS_POSSIBLE)) 23519304Speter return (1); 23619304Speter 23719304Speter /* Initialize the new file. */ 23819304Speter if (file_init(sp, tp->frp, NULL, FS_SETALT)) 23919304Speter return (1); 24019304Speter 24119304Speter /* Display tags in the center of the screen. */ 24219304Speter F_CLR(sp, SC_SCR_TOP); 24319304Speter F_SET(sp, SC_SCR_CENTER); 24419304Speter 24519304Speter /* Switch. */ 24619304Speter F_SET(sp, SC_FSWITCH); 24719304Speter return (0); 24819304Speter} 24919304Speter 25019304Speter/* 25119304Speter * ex_tag_Nswitch -- 25219304Speter * Switch context to the specified TAG in a new screen. 25319304Speter * 254281373Sbapt * PUBLIC: int ex_tag_Nswitch(SCR *, TAG *, int); 25519304Speter */ 25619304Speterint 257254225Speterex_tag_Nswitch(SCR *sp, TAG *tp, int force) 25819304Speter{ 25919304Speter SCR *new; 26019304Speter 26119304Speter /* Get a file structure. */ 26219304Speter if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL) 26319304Speter return (1); 26419304Speter 26519304Speter /* Get a new screen. */ 26619304Speter if (screen_init(sp->gp, sp, &new)) 26719304Speter return (1); 26819304Speter if (vs_split(sp, new, 0)) { 26919304Speter (void)file_end(new, new->ep, 1); 27019304Speter (void)screen_end(new); 27119304Speter return (1); 27219304Speter } 27319304Speter 27419304Speter /* Get a backing file. */ 27519304Speter if (tp->frp == sp->frp) { 27619304Speter /* Copy file state. */ 27719304Speter new->ep = sp->ep; 27819304Speter ++new->ep->refcnt; 27919304Speter 28019304Speter new->frp = tp->frp; 28119304Speter new->frp->flags = sp->frp->flags; 28219304Speter } else if (file_init(new, tp->frp, NULL, force)) { 28319304Speter (void)vs_discard(new, NULL); 28419304Speter (void)screen_end(new); 28519304Speter return (1); 28619304Speter } 28719304Speter 28819304Speter /* Create the argument list. */ 28919304Speter new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name); 29019304Speter 29119304Speter /* Display tags in the center of the screen. */ 29219304Speter F_CLR(new, SC_SCR_TOP); 29319304Speter F_SET(new, SC_SCR_CENTER); 29419304Speter 29519304Speter /* Switch. */ 29619304Speter sp->nextdisp = new; 29719304Speter F_SET(sp, SC_SSWITCH); 29819304Speter 29919304Speter return (0); 30019304Speter} 30119304Speter 30219304Speter/* 30319304Speter * ex_tag_pop -- ^T 30419304Speter * :tagp[op][!] [number | file] 30519304Speter * 30619304Speter * Pop to a previous TAGQ context. 30719304Speter * 308281373Sbapt * PUBLIC: int ex_tag_pop(SCR *, EXCMD *); 30919304Speter */ 31019304Speterint 311254225Speterex_tag_pop(SCR *sp, EXCMD *cmdp) 31219304Speter{ 31319304Speter EX_PRIVATE *exp; 31419304Speter TAGQ *tqp, *dtqp; 31519304Speter size_t arglen; 31619304Speter long off; 31719304Speter char *arg, *p, *t; 318254225Speter size_t nlen; 31919304Speter 32019304Speter /* Check for an empty stack. */ 32119304Speter exp = EXP(sp); 322254225Speter if (TAILQ_EMPTY(exp->tq)) { 32319304Speter tag_msg(sp, TAG_EMPTY, NULL); 32419304Speter return (1); 32519304Speter } 32619304Speter 32719304Speter /* Find the last TAG structure that we're going to DISCARD! */ 32819304Speter switch (cmdp->argc) { 32919304Speter case 0: /* Pop one tag. */ 330254225Speter dtqp = TAILQ_FIRST(exp->tq); 33119304Speter break; 33219304Speter case 1: /* Name or number. */ 333254225Speter INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1, 334254225Speter arg, nlen); 33519304Speter off = strtol(arg, &p, 10); 33619304Speter if (*p != '\0') 33719304Speter goto filearg; 33819304Speter 33919304Speter /* Number: pop that many queue entries. */ 34019304Speter if (off < 1) 34119304Speter return (0); 342254225Speter TAILQ_FOREACH(tqp, exp->tq, q) 343254225Speter if (--off <= 1) break; 344254225Speter if (tqp == NULL) { 34519304Speter msgq(sp, M_ERR, 34619304Speter "159|Less than %s entries on the tags stack; use :display t[ags]", 34719304Speter arg); 34819304Speter return (1); 34919304Speter } 35019304Speter dtqp = tqp; 35119304Speter break; 35219304Speter 35319304Speter /* File argument: pop to that queue entry. */ 35419304Speterfilearg: arglen = strlen(arg); 355254225Speter for (tqp = TAILQ_FIRST(exp->tq); tqp; 356254225Speter dtqp = tqp, tqp = TAILQ_NEXT(tqp, q)) { 35719304Speter /* Don't pop to the current file. */ 358254225Speter if (tqp == TAILQ_FIRST(exp->tq)) 35919304Speter continue; 36019304Speter p = tqp->current->frp->name; 36119304Speter if ((t = strrchr(p, '/')) == NULL) 36219304Speter t = p; 36319304Speter else 36419304Speter ++t; 36519304Speter if (!strncmp(arg, t, arglen)) 36619304Speter break; 36719304Speter } 368254225Speter if (tqp == NULL) { 36919304Speter msgq_str(sp, M_ERR, arg, 37019304Speter "160|No file %s on the tags stack to return to; use :display t[ags]"); 37119304Speter return (1); 37219304Speter } 373254225Speter if (tqp == TAILQ_FIRST(exp->tq)) 37419304Speter return (0); 37519304Speter break; 37619304Speter default: 37719304Speter abort(); 37819304Speter } 37919304Speter 38019304Speter return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE))); 38119304Speter} 38219304Speter 38319304Speter/* 38419304Speter * ex_tag_top -- :tagt[op][!] 38519304Speter * Clear the tag stack. 38619304Speter * 387281373Sbapt * PUBLIC: int ex_tag_top(SCR *, EXCMD *); 38819304Speter */ 38919304Speterint 390254225Speterex_tag_top(SCR *sp, EXCMD *cmdp) 39119304Speter{ 39219304Speter EX_PRIVATE *exp; 39319304Speter 39419304Speter exp = EXP(sp); 39519304Speter 39619304Speter /* Check for an empty stack. */ 397254225Speter if (TAILQ_EMPTY(exp->tq)) { 39819304Speter tag_msg(sp, TAG_EMPTY, NULL); 39919304Speter return (1); 40019304Speter } 40119304Speter 40219304Speter /* Return to the oldest information. */ 403254225Speter return (tag_pop(sp, TAILQ_PREV(TAILQ_LAST(exp->tq, _tqh), _tqh, q), 404254225Speter FL_ISSET(cmdp->iflags, E_C_FORCE))); 40519304Speter} 40619304Speter 40719304Speter/* 40819304Speter * tag_pop -- 40919304Speter * Pop up to and including the specified TAGQ context. 41019304Speter */ 41119304Speterstatic int 412254225Spetertag_pop(SCR *sp, TAGQ *dtqp, int force) 41319304Speter{ 41419304Speter EX_PRIVATE *exp; 41519304Speter TAG *tp; 41619304Speter TAGQ *tqp; 41719304Speter 41819304Speter exp = EXP(sp); 41919304Speter 42019304Speter /* 42119304Speter * Update the cursor from the saved TAG information of the TAG 42219304Speter * structure we're moving to. 42319304Speter */ 424254225Speter tp = TAILQ_NEXT(dtqp, q)->current; 42519304Speter if (tp->frp == sp->frp) { 42619304Speter sp->lno = tp->lno; 42719304Speter sp->cno = tp->cno; 42819304Speter } else { 42919304Speter if (file_m1(sp, force, FS_ALL | FS_POSSIBLE)) 43019304Speter return (1); 43119304Speter 43219304Speter tp->frp->lno = tp->lno; 43319304Speter tp->frp->cno = tp->cno; 43419304Speter F_SET(sp->frp, FR_CURSORSET); 43519304Speter if (file_init(sp, tp->frp, NULL, FS_SETALT)) 43619304Speter return (1); 43719304Speter 43819304Speter F_SET(sp, SC_FSWITCH); 43919304Speter } 44019304Speter 44119304Speter /* Pop entries off the queue up to and including dtqp. */ 44219304Speter do { 443254225Speter tqp = TAILQ_FIRST(exp->tq); 44419304Speter if (tagq_free(sp, tqp)) 44519304Speter return (0); 44619304Speter } while (tqp != dtqp); 44719304Speter 44819304Speter /* 44919304Speter * If only a single tag left, we've returned to the first tag point, 45019304Speter * and the stack is now empty. 45119304Speter */ 452254225Speter if (TAILQ_NEXT(TAILQ_FIRST(exp->tq), q) == NULL) 453254225Speter tagq_free(sp, TAILQ_FIRST(exp->tq)); 45419304Speter 45519304Speter return (0); 45619304Speter} 45719304Speter 45819304Speter/* 45919304Speter * ex_tag_display -- 46019304Speter * Display the list of tags. 46119304Speter * 462281373Sbapt * PUBLIC: int ex_tag_display(SCR *); 46319304Speter */ 46419304Speterint 465254225Speterex_tag_display(SCR *sp) 46619304Speter{ 46719304Speter EX_PRIVATE *exp; 46819304Speter TAG *tp; 46919304Speter TAGQ *tqp; 47019304Speter int cnt; 47119304Speter size_t len; 472254225Speter char *p; 47319304Speter 47419304Speter exp = EXP(sp); 475254225Speter if (TAILQ_EMPTY(exp->tq)) { 47619304Speter tag_msg(sp, TAG_EMPTY, NULL); 47719304Speter return (0); 47819304Speter } 47919304Speter 48019304Speter /* 48119304Speter * We give the file name 20 columns and the search string the rest. 48219304Speter * If there's not enough room, we don't do anything special, it's 48319304Speter * not worth the effort, it just makes the display more confusing. 48419304Speter * 48519304Speter * We also assume that characters in file names map 1-1 to printing 48619304Speter * characters. This might not be true, but I don't think it's worth 48719304Speter * fixing. (The obvious fix is to pass the filenames through the 48819304Speter * msg_print function.) 48919304Speter */ 49019304Speter#define L_NAME 30 /* Name. */ 49119304Speter#define L_SLOP 4 /* Leading number plus trailing *. */ 49219304Speter#define L_SPACE 5 /* Spaces after name, before tag. */ 49319304Speter#define L_TAG 20 /* Tag. */ 49419304Speter if (sp->cols <= L_NAME + L_SLOP) { 49519304Speter msgq(sp, M_ERR, "292|Display too small."); 49619304Speter return (0); 49719304Speter } 49819304Speter 49919304Speter /* 50019304Speter * Display the list of tags for each queue entry. The first entry 50119304Speter * is numbered, and the current tag entry has an asterisk appended. 50219304Speter */ 503254225Speter for (cnt = 1, tqp = TAILQ_FIRST(exp->tq); !INTERRUPTED(sp) && 504254225Speter tqp != NULL; ++cnt, tqp = TAILQ_NEXT(tqp, q)) 505254225Speter TAILQ_FOREACH(tp, tqp->tagq, q) { 506254225Speter if (tp == TAILQ_FIRST(tqp->tagq)) 50719304Speter (void)ex_printf(sp, "%2d ", cnt); 50819304Speter else 50919304Speter (void)ex_printf(sp, " "); 51019304Speter p = tp->frp == NULL ? tp->fname : tp->frp->name; 51119304Speter if ((len = strlen(p)) > L_NAME) { 51219304Speter len = len - (L_NAME - 4); 51319304Speter (void)ex_printf(sp, " ... %*.*s", 51419304Speter L_NAME - 4, L_NAME - 4, p + len); 51519304Speter } else 51619304Speter (void)ex_printf(sp, 51719304Speter " %*.*s", L_NAME, L_NAME, p); 51819304Speter if (tqp->current == tp) 51919304Speter (void)ex_printf(sp, "*"); 52019304Speter 521254225Speter if (tp == TAILQ_FIRST(tqp->tagq) && tqp->tag != NULL && 52219304Speter (sp->cols - L_NAME) >= L_TAG + L_SPACE) { 52319304Speter len = strlen(tqp->tag); 52419304Speter if (len > sp->cols - (L_NAME + L_SPACE)) 52519304Speter len = sp->cols - (L_NAME + L_SPACE); 52619304Speter (void)ex_printf(sp, "%s%.*s", 52719304Speter tqp->current == tp ? " " : " ", 52819304Speter (int)len, tqp->tag); 52919304Speter } 53019304Speter (void)ex_printf(sp, "\n"); 53119304Speter } 53219304Speter return (0); 53319304Speter} 53419304Speter 53519304Speter/* 53619304Speter * ex_tag_copy -- 53719304Speter * Copy a screen's tag structures. 53819304Speter * 539281373Sbapt * PUBLIC: int ex_tag_copy(SCR *, SCR *); 54019304Speter */ 54119304Speterint 542254225Speterex_tag_copy(SCR *orig, SCR *sp) 54319304Speter{ 54419304Speter EX_PRIVATE *oexp, *nexp; 54519304Speter TAGQ *aqp, *tqp; 54619304Speter TAG *ap, *tp; 54719304Speter TAGF *atfp, *tfp; 54819304Speter 54919304Speter oexp = EXP(orig); 55019304Speter nexp = EXP(sp); 55119304Speter 55219304Speter /* Copy tag queue and tags stack. */ 553254225Speter TAILQ_FOREACH(aqp, oexp->tq, q) { 55419304Speter if (tagq_copy(sp, aqp, &tqp)) 55519304Speter return (1); 556254225Speter TAILQ_FOREACH(ap, aqp->tagq, q) { 55719304Speter if (tag_copy(sp, ap, &tp)) 55819304Speter return (1); 55919304Speter /* Set the current pointer. */ 56019304Speter if (aqp->current == ap) 56119304Speter tqp->current = tp; 562254225Speter TAILQ_INSERT_TAIL(tqp->tagq, tp, q); 56319304Speter } 564254225Speter TAILQ_INSERT_TAIL(nexp->tq, tqp, q); 56519304Speter } 56619304Speter 56719304Speter /* Copy list of tag files. */ 568254225Speter TAILQ_FOREACH(atfp, oexp->tagfq, q) { 56919304Speter if (tagf_copy(sp, atfp, &tfp)) 57019304Speter return (1); 571254225Speter TAILQ_INSERT_TAIL(nexp->tagfq, tfp, q); 57219304Speter } 57319304Speter 57419304Speter /* Copy the last tag. */ 57519304Speter if (oexp->tag_last != NULL && 576254225Speter (nexp->tag_last = v_wstrdup(sp, oexp->tag_last, 577254225Speter STRLEN(oexp->tag_last))) == NULL) { 57819304Speter msgq(sp, M_SYSERR, NULL); 57919304Speter return (1); 58019304Speter } 58119304Speter return (0); 58219304Speter} 58319304Speter 58419304Speter/* 58519304Speter * tagf_copy -- 58619304Speter * Copy a TAGF structure and return it in new memory. 58719304Speter */ 58819304Speterstatic int 589254225Spetertagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp) 59019304Speter{ 59119304Speter TAGF *tfp; 59219304Speter 59319304Speter MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF)); 59419304Speter *tfp = *otfp; 59519304Speter 59619304Speter /* XXX: Allocate as part of the TAGF structure!!! */ 59719304Speter if ((tfp->name = strdup(otfp->name)) == NULL) 59819304Speter return (1); 59919304Speter 60019304Speter *tfpp = tfp; 60119304Speter return (0); 60219304Speter} 60319304Speter 60419304Speter/* 60519304Speter * tagq_copy -- 60619304Speter * Copy a TAGQ structure and return it in new memory. 60719304Speter */ 60819304Speterstatic int 609254225Spetertagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp) 61019304Speter{ 61119304Speter TAGQ *tqp; 61219304Speter size_t len; 61319304Speter 61419304Speter len = sizeof(TAGQ); 61519304Speter if (otqp->tag != NULL) 61619304Speter len += otqp->tlen + 1; 61719304Speter MALLOC_RET(sp, tqp, TAGQ *, len); 61819304Speter memcpy(tqp, otqp, len); 61919304Speter 620254225Speter TAILQ_INIT(tqp->tagq); 62119304Speter tqp->current = NULL; 62219304Speter if (otqp->tag != NULL) 62319304Speter tqp->tag = tqp->buf; 62419304Speter 62519304Speter *tqpp = tqp; 62619304Speter return (0); 62719304Speter} 62819304Speter 62919304Speter/* 63019304Speter * tag_copy -- 63119304Speter * Copy a TAG structure and return it in new memory. 63219304Speter */ 63319304Speterstatic int 634254225Spetertag_copy(SCR *sp, TAG *otp, TAG **tpp) 63519304Speter{ 63619304Speter TAG *tp; 63719304Speter size_t len; 63819304Speter 63919304Speter len = sizeof(TAG); 64019304Speter if (otp->fname != NULL) 64119304Speter len += otp->fnlen + 1; 64219304Speter if (otp->search != NULL) 64319304Speter len += otp->slen + 1; 644254225Speter if (otp->msg != NULL) 645254225Speter len += otp->mlen + 1; 64619304Speter MALLOC_RET(sp, tp, TAG *, len); 64719304Speter memcpy(tp, otp, len); 64819304Speter 64919304Speter if (otp->fname != NULL) 650254225Speter tp->fname = (char *)tp->buf; 65119304Speter if (otp->search != NULL) 652254225Speter tp->search = tp->buf + (otp->search - otp->buf); 653254225Speter if (otp->msg != NULL) 654254225Speter tp->msg = tp->buf + (otp->msg - otp->buf); 65519304Speter 65619304Speter *tpp = tp; 65719304Speter return (0); 65819304Speter} 65919304Speter 66019304Speter/* 66119304Speter * tagf_free -- 66219304Speter * Free a TAGF structure. 66319304Speter */ 66419304Speterstatic int 665254225Spetertagf_free(SCR *sp, TAGF *tfp) 66619304Speter{ 66719304Speter EX_PRIVATE *exp; 66819304Speter 66919304Speter exp = EXP(sp); 670254225Speter TAILQ_REMOVE(exp->tagfq, tfp, q); 67119304Speter free(tfp->name); 67219304Speter free(tfp); 67319304Speter return (0); 67419304Speter} 67519304Speter 67619304Speter/* 67719304Speter * tagq_free -- 67819304Speter * Free a TAGQ structure (and associated TAG structures). 67919304Speter * 680281373Sbapt * PUBLIC: int tagq_free(SCR *, TAGQ *); 68119304Speter */ 68219304Speterint 683254225Spetertagq_free(SCR *sp, TAGQ *tqp) 68419304Speter{ 68519304Speter EX_PRIVATE *exp; 68619304Speter TAG *tp; 68719304Speter 68819304Speter exp = EXP(sp); 689254225Speter while ((tp = TAILQ_FIRST(tqp->tagq)) != NULL) { 690254225Speter TAILQ_REMOVE(tqp->tagq, tp, q); 69119304Speter free(tp); 69219304Speter } 69319304Speter /* 69419304Speter * !!! 69519304Speter * If allocated and then the user failed to switch files, the TAGQ 69619304Speter * structure was never attached to any list. 69719304Speter */ 698254225Speter if (TAILQ_ENTRY_ISVALID(tqp, q)) 699254225Speter TAILQ_REMOVE(exp->tq, tqp, q); 70019304Speter free(tqp); 70119304Speter return (0); 70219304Speter} 70319304Speter 70419304Speter/* 705281373Sbapt * PUBLIC: int tagq_push(SCR*, TAGQ*, int, int ); 706254225Speter */ 707254225Speterint 708254225Spetertagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force) 709254225Speter{ 710254225Speter EX_PRIVATE *exp; 711254225Speter FREF *frp; 712254225Speter TAG *rtp; 713254225Speter TAGQ *rtqp; 714254225Speter recno_t lno; 715254225Speter size_t cno; 716254225Speter int istmp; 717254225Speter char *np; 718254225Speter size_t nlen; 719254225Speter 720254225Speter exp = EXP(sp); 721254225Speter 722254225Speter /* 723254225Speter * Allocate all necessary memory before swapping screens. Initialize 724254225Speter * flags so we know what to free. 725254225Speter */ 726254225Speter rtp = NULL; 727254225Speter rtqp = NULL; 728254225Speter if (TAILQ_EMPTY(exp->tq)) { 729254225Speter /* Initialize the `local context' tag queue structure. */ 730254225Speter CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ)); 731254225Speter TAILQ_INIT(rtqp->tagq); 732254225Speter 733254225Speter /* Initialize and link in its tag structure. */ 734254225Speter CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG)); 735254225Speter TAILQ_INSERT_HEAD(rtqp->tagq, rtp, q); 736254225Speter rtqp->current = rtp; 737254225Speter } 738254225Speter 739254225Speter /* 740254225Speter * Stick the current context information in a convenient place, we're 741254225Speter * about to lose it. Note, if we're called on editor startup, there 742254225Speter * will be no FREF structure. 743254225Speter */ 744254225Speter frp = sp->frp; 745254225Speter lno = sp->lno; 746254225Speter cno = sp->cno; 747254225Speter istmp = frp == NULL || 748254225Speter (F_ISSET(frp, FR_TMPFILE) && !new_screen); 749254225Speter 750254225Speter /* Try to switch to the preset tag. */ 751254225Speter if (new_screen) { 752254225Speter if (ex_tag_Nswitch(sp, tqp->current, force)) 753254225Speter goto err; 754254225Speter 755254225Speter /* Everything else gets done in the new screen. */ 756254225Speter sp = sp->nextdisp; 757254225Speter exp = EXP(sp); 758254225Speter } else 759254225Speter if (ex_tag_nswitch(sp, tqp->current, force)) 760254225Speter goto err; 761254225Speter 762254225Speter /* 763254225Speter * If this is the first tag, put a `current location' queue entry 764254225Speter * in place, so we can pop all the way back to the current mark. 765254225Speter * Note, it doesn't point to much of anything, it's a placeholder. 766254225Speter */ 767254225Speter if (TAILQ_EMPTY(exp->tq)) { 768254225Speter TAILQ_INSERT_HEAD(exp->tq, rtqp, q); 769254225Speter } else 770254225Speter rtqp = TAILQ_FIRST(exp->tq); 771254225Speter 772254225Speter /* Link the new TAGQ structure into place. */ 773254225Speter TAILQ_INSERT_HEAD(exp->tq, tqp, q); 774254225Speter 775254225Speter (void)ctag_search(sp, 776254225Speter tqp->current->search, tqp->current->slen, tqp->tag); 777254225Speter if (tqp->current->msg) { 778254225Speter INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1, 779254225Speter np, nlen); 780254225Speter msgq(sp, M_INFO, "%s", np); 781254225Speter } 782254225Speter 783254225Speter /* 784254225Speter * Move the current context from the temporary save area into the 785254225Speter * right structure. 786254225Speter * 787254225Speter * If we were in a temporary file, we don't have a context to which 788254225Speter * we can return, so just make it be the same as what we're moving 789254225Speter * to. It will be a little odd that ^T doesn't change anything, but 790254225Speter * I don't think it's a big deal. 791254225Speter */ 792254225Speter if (istmp) { 793254225Speter rtqp->current->frp = sp->frp; 794254225Speter rtqp->current->lno = sp->lno; 795254225Speter rtqp->current->cno = sp->cno; 796254225Speter } else { 797254225Speter rtqp->current->frp = frp; 798254225Speter rtqp->current->lno = lno; 799254225Speter rtqp->current->cno = cno; 800254225Speter } 801254225Speter return (0); 802254225Speter 803254225Spetererr: 804254225Speteralloc_err: 805254225Speter if (rtqp != NULL) 806254225Speter free(rtqp); 807254225Speter if (rtp != NULL) 808254225Speter free(rtp); 809254225Speter tagq_free(sp, tqp); 810254225Speter return (1); 811254225Speter} 812254225Speter 813254225Speter/* 81419304Speter * tag_msg 81519304Speter * A few common messages. 81619304Speter * 817281373Sbapt * PUBLIC: void tag_msg(SCR *, tagmsg_t, char *); 81819304Speter */ 81919304Spetervoid 820254225Spetertag_msg(SCR *sp, tagmsg_t msg, char *tag) 82119304Speter{ 82219304Speter switch (msg) { 82319304Speter case TAG_BADLNO: 82419304Speter msgq_str(sp, M_ERR, tag, 82519304Speter "164|%s: the tag's line number is past the end of the file"); 82619304Speter break; 82719304Speter case TAG_EMPTY: 82819304Speter msgq(sp, M_INFO, "165|The tags stack is empty"); 82919304Speter break; 83019304Speter case TAG_SEARCH: 83119304Speter msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found"); 83219304Speter break; 83319304Speter default: 83419304Speter abort(); 83519304Speter } 83619304Speter} 83719304Speter 83819304Speter/* 83919304Speter * ex_tagf_alloc -- 84019304Speter * Create a new list of ctag files. 84119304Speter * 842281373Sbapt * PUBLIC: int ex_tagf_alloc(SCR *, char *); 84319304Speter */ 84419304Speterint 845254225Speterex_tagf_alloc(SCR *sp, char *str) 84619304Speter{ 84719304Speter EX_PRIVATE *exp; 84819304Speter TAGF *tfp; 84919304Speter size_t len; 85019304Speter char *p, *t; 85119304Speter 85219304Speter /* Free current queue. */ 85319304Speter exp = EXP(sp); 854254225Speter while ((tfp = TAILQ_FIRST(exp->tagfq)) != NULL) 85519304Speter tagf_free(sp, tfp); 85619304Speter 85719304Speter /* Create new queue. */ 85819304Speter for (p = t = str;; ++p) { 859254225Speter if (*p == '\0' || cmdskip(*p)) { 860254225Speter if ((len = p - t)) { 86119304Speter MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF)); 86219304Speter MALLOC(sp, tfp->name, char *, len + 1); 86319304Speter if (tfp->name == NULL) { 86419304Speter free(tfp); 86519304Speter return (1); 86619304Speter } 86719304Speter memcpy(tfp->name, t, len); 86819304Speter tfp->name[len] = '\0'; 86919304Speter tfp->flags = 0; 870254225Speter TAILQ_INSERT_TAIL(exp->tagfq, tfp, q); 87119304Speter } 87219304Speter t = p + 1; 87319304Speter } 87419304Speter if (*p == '\0') 87519304Speter break; 87619304Speter } 87719304Speter return (0); 87819304Speter} 87919304Speter /* Free previous queue. */ 88019304Speter/* 88119304Speter * ex_tag_free -- 88219304Speter * Free the ex tag information. 88319304Speter * 884281373Sbapt * PUBLIC: int ex_tag_free(SCR *); 88519304Speter */ 88619304Speterint 887254225Speterex_tag_free(SCR *sp) 88819304Speter{ 88919304Speter EX_PRIVATE *exp; 89019304Speter TAGF *tfp; 89119304Speter TAGQ *tqp; 89219304Speter 89319304Speter /* Free up tag information. */ 89419304Speter exp = EXP(sp); 895254225Speter while ((tqp = TAILQ_FIRST(exp->tq)) != NULL) 89619304Speter tagq_free(sp, tqp); 897254225Speter while ((tfp = TAILQ_FIRST(exp->tagfq)) != NULL) 89819304Speter tagf_free(sp, tfp); 89919304Speter if (exp->tag_last != NULL) 90019304Speter free(exp->tag_last); 90119304Speter return (0); 90219304Speter} 90319304Speter 90419304Speter/* 90519304Speter * ctag_search -- 90619304Speter * Search a file for a tag. 90719304Speter */ 90819304Speterstatic int 909254225Speterctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag) 91019304Speter{ 91119304Speter MARK m; 91219304Speter char *p; 913254225Speter char *np; 914254225Speter size_t nlen; 91519304Speter 91619304Speter /* 91719304Speter * !!! 91819304Speter * The historic tags file format (from a long, long time ago...) 91919304Speter * used a line number, not a search string. I got complaints, so 92019304Speter * people are still using the format. POSIX 1003.2 permits it. 92119304Speter */ 922254225Speter if (ISDIGIT(search[0])) { 923254225Speter INT2CHAR(sp, search, slen+1, np, nlen); 924254225Speter m.lno = atoi(np); 92519304Speter if (!db_exist(sp, m.lno)) { 92619304Speter tag_msg(sp, TAG_BADLNO, tag); 92719304Speter return (1); 92819304Speter } 92919304Speter } else { 93019304Speter /* 93119304Speter * Search for the tag; cheap fallback for C functions 93219304Speter * if the name is the same but the arguments have changed. 93319304Speter */ 93419304Speter m.lno = 1; 93519304Speter m.cno = 0; 93619304Speter if (f_search(sp, &m, &m, 937254225Speter search, slen, NULL, SEARCH_FILE | SEARCH_TAG)) { 938254225Speter INT2CHAR(sp, search, slen, np, nlen); 939254225Speter if ((p = strrchr(np, '(')) != NULL) { 940254225Speter slen = p - np; 94119304Speter if (f_search(sp, &m, &m, search, slen, 94219304Speter NULL, SEARCH_FILE | SEARCH_TAG)) 94319304Speter goto notfound; 94419304Speter } else { 94519304Speternotfound: tag_msg(sp, TAG_SEARCH, tag); 94619304Speter return (1); 94719304Speter } 948254225Speter } 94919304Speter /* 95019304Speter * !!! 95119304Speter * Historically, tags set the search direction if it wasn't 95219304Speter * already set. 95319304Speter */ 95419304Speter if (sp->searchdir == NOTSET) 95519304Speter sp->searchdir = FORWARD; 95619304Speter } 95719304Speter 95819304Speter /* 95919304Speter * !!! 96019304Speter * Tags move to the first non-blank, NOT the search pattern start. 96119304Speter */ 96219304Speter sp->lno = m.lno; 96319304Speter sp->cno = 0; 96419304Speter (void)nonblank(sp, sp->lno, &sp->cno); 96519304Speter return (0); 96619304Speter} 96719304Speter 96819304Speter/* 96919304Speter * ctag_slist -- 97019304Speter * Search the list of tags files for a tag, and return tag queue. 97119304Speter */ 97219304Speterstatic TAGQ * 973254225Speterctag_slist(SCR *sp, CHAR_T *tag) 97419304Speter{ 97519304Speter EX_PRIVATE *exp; 97619304Speter TAGF *tfp; 97719304Speter TAGQ *tqp; 97819304Speter size_t len; 979254225Speter int echk = 0; 980254225Speter char *np; 981254225Speter size_t nlen; 98219304Speter 98319304Speter exp = EXP(sp); 98419304Speter 98519304Speter /* Allocate and initialize the tag queue structure. */ 986254225Speter INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen); 987254225Speter len = nlen - 1; 98819304Speter CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1); 989254225Speter TAILQ_INIT(tqp->tagq); 99019304Speter tqp->tag = tqp->buf; 991254225Speter memcpy(tqp->tag, np, (tqp->tlen = len) + 1); 99219304Speter 99319304Speter /* 99419304Speter * Find the tag, only display missing file messages once, and 99519304Speter * then only if we didn't find the tag. 99619304Speter */ 997254225Speter TAILQ_FOREACH(tfp, exp->tagfq, q) 998254225Speter if (ctag_sfile(sp, tfp, tqp, tqp->tag)) { 99919304Speter echk = 1; 100019304Speter F_SET(tfp, TAGF_ERR); 100119304Speter } else 100219304Speter F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN); 100319304Speter 100419304Speter /* Check to see if we found anything. */ 1005254225Speter if (TAILQ_EMPTY(tqp->tagq)) { 1006254225Speter msgq_str(sp, M_ERR, tqp->tag, "162|%s: tag not found"); 100719304Speter if (echk) 1008254225Speter TAILQ_FOREACH(tfp, exp->tagfq, q) 100919304Speter if (F_ISSET(tfp, TAGF_ERR) && 101019304Speter !F_ISSET(tfp, TAGF_ERR_WARN)) { 101119304Speter errno = tfp->errnum; 101219304Speter msgq_str(sp, M_SYSERR, tfp->name, "%s"); 101319304Speter F_SET(tfp, TAGF_ERR_WARN); 101419304Speter } 101519304Speter free(tqp); 101619304Speter return (NULL); 101719304Speter } 101819304Speter 101919304Speter return (tqp); 102019304Speter 102119304Speteralloc_err: 102219304Speter return (NULL); 102319304Speter} 102419304Speter 102519304Speter/* 102619304Speter * ctag_sfile -- 102719304Speter * Search a tags file for a tag, adding any found to the tag queue. 102819304Speter */ 102919304Speterstatic int 1030254225Speterctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname) 103119304Speter{ 103219304Speter struct stat sb; 103319304Speter TAG *tp; 1034254225Speter size_t dlen, nlen = 0, slen; 103519304Speter int fd, i, nf1, nf2; 1036254225Speter char *back, *front, *map, *p, *search, *t; 1037254225Speter char *cname = NULL, *dname = NULL, *name = NULL; 1038254225Speter CHAR_T *wp; 1039254225Speter size_t wlen; 1040254225Speter long tl; 104119304Speter 104219304Speter if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) { 104319304Speter tfp->errnum = errno; 104419304Speter return (1); 104519304Speter } 104619304Speter 104719304Speter if (fstat(fd, &sb) != 0 || 1048254225Speter (map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, 1049254225Speter MAP_PRIVATE, fd, 0)) == MAP_FAILED) { 105019304Speter tfp->errnum = errno; 105119304Speter (void)close(fd); 105219304Speter return (1); 105319304Speter } 105419304Speter 1055254225Speter tl = O_VAL(sp, O_TAGLENGTH); 105619304Speter front = map; 105719304Speter back = front + sb.st_size; 105819304Speter front = binary_search(tname, front, back); 1059254225Speter front = linear_search(tname, front, back, tl); 106019304Speter if (front == NULL) 106119304Speter goto done; 106219304Speter 106319304Speter /* 106419304Speter * Initialize and link in the tag structure(s). The historic ctags 106519304Speter * file format only permitted a single tag location per tag. The 106619304Speter * obvious extension to permit multiple tags locations per tag is to 106719304Speter * output multiple records in the standard format. Unfortunately, 106819304Speter * this won't work correctly with historic ex/vi implementations, 106919304Speter * because their binary search assumes that there's only one record 107019304Speter * per tag, and so will use a random tag entry if there si more than 107119304Speter * one. This code handles either format. 107219304Speter * 107319304Speter * The tags file is in the following format: 107419304Speter * 107519304Speter * <tag> <filename> <line number> | <pattern> 107619304Speter * 107719304Speter * Figure out how long everything is so we can allocate in one swell 107819304Speter * foop, but discard anything that looks wrong. 107919304Speter */ 108019304Speter for (;;) { 108119304Speter /* Nul-terminate the end of the line. */ 108219304Speter for (p = front; p < back && *p != '\n'; ++p); 108319304Speter if (p == back || *p != '\n') 108419304Speter break; 108519304Speter *p = '\0'; 108619304Speter 108719304Speter /* Update the pointers for the next time. */ 108819304Speter t = p + 1; 108919304Speter p = front; 109019304Speter front = t; 109119304Speter 109219304Speter /* Break the line into tokens. */ 109319304Speter for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i) 109419304Speter switch (i) { 109519304Speter case 0: /* Tag. */ 109619304Speter cname = t; 109719304Speter break; 109819304Speter case 1: /* Filename. */ 109919304Speter name = t; 110019304Speter nlen = strlen(name); 110119304Speter break; 110219304Speter } 110319304Speter 110419304Speter /* Check for corruption. */ 110519304Speter if (i != 2 || p == NULL || t == NULL) 110619304Speter goto corrupt; 110719304Speter 110819304Speter /* The rest of the string is the search pattern. */ 110919304Speter search = p; 111019304Speter if ((slen = strlen(p)) == 0) { 111119304Spetercorrupt: p = msg_print(sp, tname, &nf1); 111219304Speter t = msg_print(sp, tfp->name, &nf2); 111319304Speter msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t); 111419304Speter if (nf1) 111519304Speter FREE_SPACE(sp, p, 0); 111619304Speter if (nf2) 111719304Speter FREE_SPACE(sp, t, 0); 111819304Speter continue; 111919304Speter } 112019304Speter 112119304Speter /* Check for passing the last entry. */ 1122254225Speter if (tl ? strncmp(tname, cname, tl) : strcmp(tname, cname)) 112319304Speter break; 112419304Speter 112519304Speter /* Resolve the file name. */ 112619304Speter ctag_file(sp, tfp, name, &dname, &dlen); 112719304Speter 112819304Speter CALLOC_GOTO(sp, tp, 1129254225Speter TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 + 1130254225Speter (slen + 1) * sizeof(CHAR_T)); 1131254225Speter tp->fname = (char *)tp->buf; 1132254225Speter if (dlen == 1 && *dname == '.') 1133254225Speter --dlen; 1134254225Speter else if (dlen != 0) { 113519304Speter memcpy(tp->fname, dname, dlen); 113619304Speter tp->fname[dlen] = '/'; 113719304Speter ++dlen; 113819304Speter } 113919304Speter memcpy(tp->fname + dlen, name, nlen + 1); 114019304Speter tp->fnlen = dlen + nlen; 1141254225Speter tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1); 1142254225Speter CHAR2INT(sp, search, slen + 1, wp, wlen); 1143254225Speter MEMCPY(tp->search, wp, (tp->slen = slen) + 1); 1144254225Speter TAILQ_INSERT_TAIL(tqp->tagq, tp, q); 1145254225Speter 1146254225Speter /* Try to preset the tag within the current file. */ 1147254225Speter if (sp->frp != NULL && sp->frp->name != NULL && 1148254225Speter tqp->current == NULL && !strcmp(tp->fname, sp->frp->name)) 1149254225Speter tqp->current = tp; 115019304Speter } 115119304Speter 1152254225Speter if (tqp->current == NULL) 1153254225Speter tqp->current = TAILQ_FIRST(tqp->tagq); 1154254225Speter 115519304Speteralloc_err: 1156254225Speterdone: if (munmap(map, sb.st_size)) 115719304Speter msgq(sp, M_SYSERR, "munmap"); 115819304Speter if (close(fd)) 115919304Speter msgq(sp, M_SYSERR, "close"); 116019304Speter return (0); 116119304Speter} 116219304Speter 116319304Speter/* 116419304Speter * ctag_file -- 116519304Speter * Search for the right path to this file. 116619304Speter */ 116719304Speterstatic void 1168254225Speterctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp) 116919304Speter{ 117019304Speter struct stat sb; 1171254225Speter char *p, *buf; 117219304Speter 117319304Speter /* 117419304Speter * !!! 117519304Speter * If the tag file path is a relative path, see if it exists. If it 117619304Speter * doesn't, look relative to the tags file path. It's okay for a tag 117719304Speter * file to not exist, and historically, vi simply displayed a "new" 117819304Speter * file. However, if the path exists relative to the tag file, it's 117919304Speter * pretty clear what's happening, so we may as well get it right. 118019304Speter */ 118119304Speter *dlenp = 0; 118219304Speter if (name[0] != '/' && 118319304Speter stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) { 118419304Speter *p = '\0'; 1185254225Speter if ((buf = join(tfp->name, name)) == NULL) { 1186254225Speter msgq(sp, M_SYSERR, NULL); 1187254225Speter return; 1188254225Speter } 118919304Speter if (stat(buf, &sb) == 0) { 119019304Speter *dirp = tfp->name; 119119304Speter *dlenp = strlen(*dirp); 119219304Speter } 1193254225Speter free(buf); 1194254225Speter *p = '/'; 119519304Speter } 119619304Speter} 119719304Speter 119819304Speter/* 119919304Speter * Binary search for "string" in memory between "front" and "back". 120019304Speter * 120119304Speter * This routine is expected to return a pointer to the start of a line at 120219304Speter * *or before* the first word matching "string". Relaxing the constraint 120319304Speter * this way simplifies the algorithm. 120419304Speter * 120519304Speter * Invariants: 120619304Speter * front points to the beginning of a line at or before the first 120719304Speter * matching string. 120819304Speter * 120919304Speter * back points to the beginning of a line at or after the first 121019304Speter * matching line. 121119304Speter * 121219304Speter * Base of the Invariants. 121319304Speter * front = NULL; 121419304Speter * back = EOF; 121519304Speter * 121619304Speter * Advancing the Invariants: 121719304Speter * 121819304Speter * p = first newline after halfway point from front to back. 121919304Speter * 122019304Speter * If the string at "p" is not greater than the string to match, 122119304Speter * p is the new front. Otherwise it is the new back. 122219304Speter * 122319304Speter * Termination: 122419304Speter * 122519304Speter * The definition of the routine allows it return at any point, 122619304Speter * since front is always at or before the line to print. 122719304Speter * 122819304Speter * In fact, it returns when the chosen "p" equals "back". This 122919304Speter * implies that there exists a string is least half as long as 123019304Speter * (back - front), which in turn implies that a linear search will 123119304Speter * be no more expensive than the cost of simply printing a string or two. 123219304Speter * 123319304Speter * Trying to continue with binary search at this point would be 123419304Speter * more trouble than it's worth. 123519304Speter */ 123619304Speter#define EQUAL 0 123719304Speter#define GREATER 1 123819304Speter#define LESS (-1) 123919304Speter 1240254225Speter#define SKIP_PAST_NEWLINE(p, back) \ 1241254225Speter while (p < back && *p++ != '\n') continue; 124219304Speter 124319304Speterstatic char * 1244281373Sbaptbinary_search(char *string, char *front, char *back) 124519304Speter{ 1246281373Sbapt char *p; 124719304Speter 124819304Speter p = front + (back - front) / 2; 124919304Speter SKIP_PAST_NEWLINE(p, back); 125019304Speter 125119304Speter while (p != back) { 125219304Speter if (compare(string, p, back) == GREATER) 125319304Speter front = p; 125419304Speter else 125519304Speter back = p; 125619304Speter p = front + (back - front) / 2; 125719304Speter SKIP_PAST_NEWLINE(p, back); 125819304Speter } 125919304Speter return (front); 126019304Speter} 126119304Speter 126219304Speter/* 126319304Speter * Find the first line that starts with string, linearly searching from front 126419304Speter * to back. 126519304Speter * 126619304Speter * Return NULL for no such line. 126719304Speter * 126819304Speter * This routine assumes: 126919304Speter * 127019304Speter * o front points at the first character in a line. 127119304Speter * o front is before or at the first line to be printed. 127219304Speter */ 127319304Speterstatic char * 1274254225Speterlinear_search(char *string, char *front, char *back, long tl) 127519304Speter{ 1276254225Speter char *end; 127719304Speter while (front < back) { 1278254225Speter end = tl && back-front > tl ? front+tl : back; 1279254225Speter switch (compare(string, front, end)) { 128019304Speter case EQUAL: /* Found it. */ 128119304Speter return (front); 128219304Speter case LESS: /* No such string. */ 128319304Speter return (NULL); 128419304Speter case GREATER: /* Keep going. */ 128519304Speter break; 128619304Speter } 128719304Speter SKIP_PAST_NEWLINE(front, back); 128819304Speter } 128919304Speter return (NULL); 129019304Speter} 129119304Speter 129219304Speter/* 129319304Speter * Return LESS, GREATER, or EQUAL depending on how the string1 compares 129419304Speter * with string2 (s1 ??? s2). 129519304Speter * 129619304Speter * o Matches up to len(s1) are EQUAL. 129719304Speter * o Matches up to len(s2) are GREATER. 129819304Speter * 129919304Speter * The string "s1" is null terminated. The string s2 is '\t', space, (or 130019304Speter * "back") terminated. 130119304Speter * 130219304Speter * !!! 130319304Speter * Reasonably modern ctags programs use tabs as separators, not spaces. 130419304Speter * However, historic programs did use spaces, and, I got complaints. 130519304Speter */ 130619304Speterstatic int 1307281373Sbaptcompare(char *s1, char *s2, char *back) 130819304Speter{ 130919304Speter for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2) 131019304Speter if (*s1 != *s2) 131119304Speter return (*s1 < *s2 ? LESS : GREATER); 131219304Speter return (*s1 ? GREATER : s2 < back && 131319304Speter (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL); 131419304Speter} 1315