Home | History | Annotate | Line # | Download | only in ex
      1 /*	$NetBSD: ex_tag.c,v 1.12 2014/08/22 21:28:20 aymeric Exp $ */
      2 /*-
      3  * Copyright (c) 1992, 1993, 1994
      4  *	The Regents of the University of California.  All rights reserved.
      5  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  *	Keith Bostic.  All rights reserved.
      7  *
      8  * This code is derived from software contributed to Berkeley by
      9  * David Hitz of Auspex Systems, Inc.
     10  *
     11  * See the LICENSE file for redistribution information.
     12  */
     13 
     14 #include "config.h"
     15 
     16 #include <sys/cdefs.h>
     17 #if 0
     18 #ifndef lint
     19 static const char sccsid[] = "Id: ex_tag.c,v 10.50 2004/03/16 14:09:11 skimo Exp  (Berkeley) Date: 2004/03/16 14:09:11 ";
     20 #endif /* not lint */
     21 #else
     22 __RCSID("$NetBSD: ex_tag.c,v 1.12 2014/08/22 21:28:20 aymeric Exp $");
     23 #endif
     24 
     25 #include <sys/param.h>
     26 #include <sys/types.h>		/* XXX: param.h may not have included types.h */
     27 
     28 #ifdef HAVE_SYS_MMAN_H
     29 #include <sys/mman.h>
     30 #endif
     31 
     32 #include <sys/queue.h>
     33 #include <sys/stat.h>
     34 #include <sys/time.h>
     35 
     36 #include <bitstring.h>
     37 #include <ctype.h>
     38 #include <errno.h>
     39 #include <fcntl.h>
     40 #include <limits.h>
     41 #include <stddef.h>
     42 #include <stdio.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <unistd.h>
     46 
     47 #include "../common/common.h"
     48 #include "../vi/vi.h"
     49 #include "tag.h"
     50 
     51 static char	*binary_search __P((char *, char *, char *));
     52 static int	 compare __P((char *, char *, char *));
     53 static void	 ctag_file __P((SCR *, TAGF *, char *, char **, size_t *));
     54 static int	 ctag_search __P((SCR *, CHAR_T *, size_t, char *));
     55 #ifdef GTAGS
     56 static int	 getentry __P((char *, char **, char **, char **));
     57 static TAGQ	*gtag_slist __P((SCR *, CHAR_T *, int));
     58 #endif
     59 static int	 ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
     60 static TAGQ	*ctag_slist __P((SCR *, CHAR_T *));
     61 static char	*linear_search __P((char *, char *, char *, unsigned long));
     62 static int	 tag_copy __P((SCR *, TAG *, TAG **));
     63 static int	 tag_pop __P((SCR *, TAGQ *, int));
     64 static int	 tagf_copy __P((SCR *, TAGF *, TAGF **));
     65 static int	 tagf_free __P((SCR *, TAGF *));
     66 static int	 tagq_copy __P((SCR *, TAGQ *, TAGQ **));
     67 
     68 /*
     69  * ex_tag_first --
     70  *	The tag code can be entered from main, e.g., "vi -t tag".
     71  *
     72  * PUBLIC: int ex_tag_first __P((SCR *, const CHAR_T *));
     73  */
     74 int
     75 ex_tag_first(SCR *sp, const CHAR_T *tagarg)
     76 {
     77 	EXCMD cmd;
     78 
     79 	/* Build an argument for the ex :tag command. */
     80 	ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, OOBLNO, 0);
     81 	argv_exp0(sp, &cmd, tagarg, STRLEN(tagarg));
     82 
     83 	/*
     84 	 * XXX
     85 	 * Historic vi went ahead and created a temporary file when it failed
     86 	 * to find the tag.  We match historic practice, but don't distinguish
     87 	 * between real error and failure to find the tag.
     88 	 */
     89 	if (ex_tag_push(sp, &cmd))
     90 		return (0);
     91 
     92 	/* Display tags in the center of the screen. */
     93 	F_CLR(sp, SC_SCR_TOP);
     94 	F_SET(sp, SC_SCR_CENTER);
     95 
     96 	return (0);
     97 }
     98 
     99 #ifdef GTAGS
    100 /*
    101  * ex_rtag_push -- ^]
    102  *              :rtag[!] [string]
    103  *
    104  * Enter a new TAGQ context based on a ctag string.
    105  *
    106  * PUBLIC: int ex_rtag_push __P((SCR *, EXCMD *));
    107  */
    108 int
    109 ex_rtag_push(SCR *sp, EXCMD *cmdp)
    110 {
    111 	F_SET(cmdp, E_REFERENCE);
    112 	return ex_tag_push(sp, cmdp);
    113 }
    114 #endif
    115 
    116 /*
    117  * ex_tag_push -- ^]
    118  *		  :tag[!] [string]
    119  *
    120  * Enter a new TAGQ context based on a ctag string.
    121  *
    122  * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *));
    123  */
    124 int
    125 ex_tag_push(SCR *sp, EXCMD *cmdp)
    126 {
    127 	EX_PRIVATE *exp;
    128 	TAGQ *tqp;
    129 	unsigned long tl;
    130 
    131 	exp = EXP(sp);
    132 	switch (cmdp->argc) {
    133 	case 1:
    134 		if (exp->tag_last != NULL)
    135 			free(exp->tag_last);
    136 
    137 		if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp,
    138 		    cmdp->argv[0]->len)) == NULL) {
    139 			msgq(sp, M_SYSERR, NULL);
    140 			return (1);
    141 		}
    142 
    143 		/* Taglength may limit the number of characters. */
    144 		if ((tl =
    145 		    O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl)
    146 			exp->tag_last[tl] = '\0';
    147 		break;
    148 	case 0:
    149 		if (exp->tag_last == NULL) {
    150 			msgq(sp, M_ERR, "158|No previous tag entered");
    151 			return (1);
    152 		}
    153 		break;
    154 	default:
    155 		abort();
    156 	}
    157 
    158 	/* Get the tag information. */
    159 #ifdef GTAGS
    160 	if (O_ISSET(sp, O_GTAGSMODE)) {
    161 		if ((tqp = gtag_slist(sp, exp->tag_last,
    162 		    F_ISSET(cmdp, E_REFERENCE))) == NULL)
    163 			return (1);
    164 	} else
    165 #endif
    166 	if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
    167 		return (1);
    168 
    169 	if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN),
    170 			       FL_ISSET(cmdp->iflags, E_C_FORCE)))
    171 		return 1;
    172 
    173 	return 0;
    174 }
    175 
    176 /*
    177  * ex_tag_next --
    178  *	Switch context to the next TAG.
    179  *
    180  * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
    181  */
    182 int
    183 ex_tag_next(SCR *sp, EXCMD *cmdp)
    184 {
    185 	EX_PRIVATE *exp;
    186 	TAG *tp;
    187 	TAGQ *tqp;
    188 	const char *np;
    189 	size_t nlen;
    190 
    191 	exp = EXP(sp);
    192 	if ((tqp = TAILQ_FIRST(&exp->tq)) == NULL) {
    193 		tag_msg(sp, TAG_EMPTY, NULL);
    194 		return (1);
    195 	}
    196 	if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) {
    197 		msgq(sp, M_ERR, "282|Already at the last tag of this group");
    198 		return (1);
    199 	}
    200 	if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
    201 		return (1);
    202 	tqp->current = tp;
    203 
    204 	if (F_ISSET(tqp, TAG_CSCOPE))
    205 		(void)cscope_search(sp, tqp, tp);
    206 	else
    207 		(void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
    208 	if (tqp->current->msg) {
    209 	    INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
    210 		     np, nlen);
    211 	    msgq(sp, M_INFO, "%s", np);
    212 	}
    213 	return (0);
    214 }
    215 
    216 /*
    217  * ex_tag_prev --
    218  *	Switch context to the next TAG.
    219  *
    220  * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
    221  */
    222 int
    223 ex_tag_prev(SCR *sp, EXCMD *cmdp)
    224 {
    225 	EX_PRIVATE *exp;
    226 	TAG *tp;
    227 	TAGQ *tqp;
    228 	const char *np;
    229 	size_t nlen;
    230 
    231 	exp = EXP(sp);
    232 	if ((tqp = TAILQ_FIRST(&exp->tq)) == NULL) {
    233 		tag_msg(sp, TAG_EMPTY, NULL);
    234 		return (0);
    235 	}
    236 	if ((tp = TAILQ_PREV(tqp->current, _tagqh, q)) == NULL) {
    237 		msgq(sp, M_ERR, "255|Already at the first tag of this group");
    238 		return (1);
    239 	}
    240 	if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
    241 		return (1);
    242 	tqp->current = tp;
    243 
    244 	if (F_ISSET(tqp, TAG_CSCOPE))
    245 		(void)cscope_search(sp, tqp, tp);
    246 	else
    247 		(void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
    248 	if (tqp->current->msg) {
    249 	    INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
    250 		     np, nlen);
    251 	    msgq(sp, M_INFO, "%s", np);
    252 	}
    253 	return (0);
    254 }
    255 
    256 /*
    257  * ex_tag_nswitch --
    258  *	Switch context to the specified TAG.
    259  *
    260  * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int));
    261  */
    262 int
    263 ex_tag_nswitch(SCR *sp, TAG *tp, int force)
    264 {
    265 	/* Get a file structure. */
    266 	if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
    267 		return (1);
    268 
    269 	/* If not changing files, return, we're done. */
    270 	if (tp->frp == sp->frp)
    271 		return (0);
    272 
    273 	/* Check for permission to leave. */
    274 	if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
    275 		return (1);
    276 
    277 	/* Initialize the new file. */
    278 	if (file_init(sp, tp->frp, NULL, FS_SETALT))
    279 		return (1);
    280 
    281 	/* Display tags in the center of the screen. */
    282 	F_CLR(sp, SC_SCR_TOP);
    283 	F_SET(sp, SC_SCR_CENTER);
    284 
    285 	/* Switch. */
    286 	F_SET(sp, SC_FSWITCH);
    287 	return (0);
    288 }
    289 
    290 /*
    291  * ex_tag_Nswitch --
    292  *	Switch context to the specified TAG in a new screen.
    293  *
    294  * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int));
    295  */
    296 int
    297 ex_tag_Nswitch(SCR *sp, TAG *tp, int force)
    298 {
    299 	SCR *new;
    300 
    301 	/* Get a file structure. */
    302 	if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
    303 		return (1);
    304 
    305 	/* Get a new screen. */
    306 	if (screen_init(sp->gp, sp, &new))
    307 		return (1);
    308 	if (vs_split(sp, new, 0)) {
    309 		(void)file_end(new, new->ep, 1);
    310 		(void)screen_fini(new);
    311 		return (1);
    312 	}
    313 
    314 	/* Get a backing file. */
    315 	if (tp->frp == sp->frp) {
    316 		/* Copy file state. */
    317 		new->ep = sp->ep;
    318 		++new->ep->refcnt;
    319 		TAILQ_INSERT_HEAD(&new->ep->scrq, new, eq);
    320 
    321 		new->frp = tp->frp;
    322 		new->frp->flags = sp->frp->flags;
    323 	} else if (file_init(new, tp->frp, NULL, force)) {
    324 		(void)vs_discard(new, NULL);
    325 		(void)screen_end(new);
    326 		return (1);
    327 	}
    328 
    329 	/* Create the argument list. */
    330 	new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name);
    331 
    332 	/* Display tags in the center of the screen. */
    333 	F_CLR(new, SC_SCR_TOP);
    334 	F_SET(new, SC_SCR_CENTER);
    335 
    336 	/* Switch. */
    337 	sp->nextdisp = new;
    338 	F_SET(sp, SC_SSWITCH);
    339 
    340 	return (0);
    341 }
    342 
    343 /*
    344  * ex_tag_pop -- ^T
    345  *		 :tagp[op][!] [number | file]
    346  *
    347  *	Pop to a previous TAGQ context.
    348  *
    349  * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *));
    350  */
    351 int
    352 ex_tag_pop(SCR *sp, EXCMD *cmdp)
    353 {
    354 	EX_PRIVATE *exp;
    355 	TAGQ *tqp, *dtqp = NULL;
    356 	size_t arglen;
    357 	long off;
    358 	const char *arg;
    359 	char *p, *t;
    360 	size_t nlen;
    361 
    362 	/* Check for an empty stack. */
    363 	exp = EXP(sp);
    364 	if (TAILQ_EMPTY(&exp->tq)) {
    365 		tag_msg(sp, TAG_EMPTY, NULL);
    366 		return (1);
    367 	}
    368 
    369 	/* Find the last TAG structure that we're going to DISCARD! */
    370 	switch (cmdp->argc) {
    371 	case 0:				/* Pop one tag. */
    372 		dtqp = TAILQ_FIRST(&exp->tq);
    373 		break;
    374 	case 1:				/* Name or number. */
    375 		INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1,
    376 			 arg, nlen);
    377 		off = strtol(arg, &p, 10);
    378 		if (*p != '\0')
    379 			goto filearg;
    380 
    381 		/* Number: pop that many queue entries. */
    382 		if (off < 1)
    383 			return (0);
    384 		TAILQ_FOREACH(tqp, &exp->tq, q)
    385 			if (--off <= 1)
    386 				break;
    387 		if (tqp == NULL) {
    388 			msgq(sp, M_ERR,
    389 	"159|Less than %s entries on the tags stack; use :display t[ags]",
    390 			    arg);
    391 			return (1);
    392 		}
    393 		dtqp = tqp;
    394 		break;
    395 
    396 		/* File argument: pop to that queue entry. */
    397 filearg:	arglen = strlen(arg);
    398 		for (tqp = TAILQ_FIRST(&exp->tq);
    399 		    tqp != NULL;
    400 		    dtqp = tqp, tqp = TAILQ_NEXT(tqp, q)) {
    401 			/* Don't pop to the current file. */
    402 			if (tqp == TAILQ_FIRST(&exp->tq))
    403 				continue;
    404 			p = tqp->current->frp->name;
    405 			if ((t = strrchr(p, '/')) == NULL)
    406 				t = p;
    407 			else
    408 				++t;
    409 			if (!strncmp(arg, t, arglen))
    410 				break;
    411 		}
    412 		if (tqp == NULL) {
    413 			msgq_str(sp, M_ERR, arg,
    414 	"160|No file %s on the tags stack to return to; use :display t[ags]");
    415 			return (1);
    416 		}
    417 		break;
    418 	default:
    419 		abort();
    420 	}
    421 
    422 	return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)));
    423 }
    424 
    425 /*
    426  * ex_tag_top -- :tagt[op][!]
    427  *	Clear the tag stack.
    428  *
    429  * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *));
    430  */
    431 int
    432 ex_tag_top(SCR *sp, EXCMD *cmdp)
    433 {
    434 	EX_PRIVATE *exp;
    435 	TAGQ *tqp;
    436 
    437 	exp = EXP(sp);
    438 
    439 	/* Check for an empty stack. */
    440 	if (TAILQ_EMPTY(&exp->tq)) {
    441 		tag_msg(sp, TAG_EMPTY, NULL);
    442 		return (1);
    443 	}
    444 
    445 	/* Return to the oldest information. */
    446 	tqp = TAILQ_LAST(&exp->tq, _tqh);
    447 	tqp = TAILQ_PREV(tqp, _tqh, q);
    448 	return tag_pop(sp, tqp, FL_ISSET(cmdp->iflags, E_C_FORCE));
    449 }
    450 
    451 /*
    452  * tag_pop --
    453  *	Pop up to and including the specified TAGQ context.
    454  */
    455 static int
    456 tag_pop(SCR *sp, TAGQ *dtqp, int force)
    457 {
    458 	EX_PRIVATE *exp;
    459 	TAG *tp;
    460 	TAGQ *tqp;
    461 
    462 	exp = EXP(sp);
    463 
    464 	/*
    465 	 * Update the cursor from the saved TAG information of the TAG
    466 	 * structure we're moving to.
    467 	 */
    468 	tp = TAILQ_NEXT(dtqp, q)->current;
    469 	if (tp->frp == sp->frp) {
    470 		sp->lno = tp->lno;
    471 		sp->cno = tp->cno;
    472 	} else {
    473 		if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
    474 			return (1);
    475 
    476 		tp->frp->lno = tp->lno;
    477 		tp->frp->cno = tp->cno;
    478 		F_SET(sp->frp, FR_CURSORSET);
    479 		if (file_init(sp, tp->frp, NULL, FS_SETALT))
    480 			return (1);
    481 
    482 		F_SET(sp, SC_FSWITCH);
    483 	}
    484 
    485 	/* Pop entries off the queue up to and including dtqp. */
    486 	do {
    487 		tqp = TAILQ_FIRST(&exp->tq);
    488 		if (tagq_free(sp, tqp))
    489 			return (0);
    490 	} while (tqp != dtqp);
    491 
    492 	/*
    493 	 * If only a single tag left, we've returned to the first tag point,
    494 	 * and the stack is now empty.
    495 	 */
    496 	if (TAILQ_NEXT(TAILQ_FIRST(&exp->tq), q) == NULL)
    497 		tagq_free(sp, TAILQ_FIRST(&exp->tq));
    498 
    499 	return (0);
    500 }
    501 
    502 /*
    503  * ex_tag_display --
    504  *	Display the list of tags.
    505  *
    506  * PUBLIC: int ex_tag_display __P((SCR *));
    507  */
    508 int
    509 ex_tag_display(SCR *sp)
    510 {
    511 	EX_PRIVATE *exp;
    512 	TAG *tp;
    513 	TAGQ *tqp;
    514 	int cnt;
    515 	size_t len;
    516 	char *p;
    517 
    518 	exp = EXP(sp);
    519 	if (TAILQ_EMPTY(&exp->tq)) {
    520 		tag_msg(sp, TAG_EMPTY, NULL);
    521 		return (0);
    522 	}
    523 
    524 	/*
    525 	 * We give the file name 20 columns and the search string the rest.
    526 	 * If there's not enough room, we don't do anything special, it's
    527 	 * not worth the effort, it just makes the display more confusing.
    528 	 *
    529 	 * We also assume that characters in file names map 1-1 to printing
    530 	 * characters.  This might not be true, but I don't think it's worth
    531 	 * fixing.  (The obvious fix is to pass the filenames through the
    532 	 * msg_print function.)
    533 	 */
    534 #define	L_NAME	30		/* Name. */
    535 #define	L_SLOP	 4		/* Leading number plus trailing *. */
    536 #define	L_SPACE	 5		/* Spaces after name, before tag. */
    537 #define	L_TAG	20		/* Tag. */
    538 	if (sp->cols <= L_NAME + L_SLOP) {
    539 		msgq(sp, M_ERR, "292|Display too small.");
    540 		return (0);
    541 	}
    542 
    543 	/*
    544 	 * Display the list of tags for each queue entry.  The first entry
    545 	 * is numbered, and the current tag entry has an asterisk appended.
    546 	 */
    547 	for (cnt = 1, tqp = TAILQ_FIRST(&exp->tq); !INTERRUPTED(sp) &&
    548 	    tqp != NULL; ++cnt, tqp = TAILQ_NEXT(tqp, q))
    549 		TAILQ_FOREACH(tp, &tqp->tagq, q) {
    550 			if (tp == TAILQ_FIRST(&tqp->tagq))
    551 				(void)ex_printf(sp, "%2d ", cnt);
    552 			else
    553 				(void)ex_printf(sp, "   ");
    554 			p = tp->frp == NULL ? tp->fname : tp->frp->name;
    555 			if ((len = strlen(p)) > L_NAME) {
    556 				len = len - (L_NAME - 4);
    557 				(void)ex_printf(sp, "   ... %*.*s",
    558 				    L_NAME - 4, L_NAME - 4, p + len);
    559 			} else
    560 				(void)ex_printf(sp,
    561 				    "   %*.*s", L_NAME, L_NAME, p);
    562 			if (tqp->current == tp)
    563 				(void)ex_printf(sp, "*");
    564 
    565 			if (tp == TAILQ_FIRST(&tqp->tagq) && tqp->tag != NULL &&
    566 			    (sp->cols - L_NAME) >= L_TAG + L_SPACE) {
    567 				len = strlen(tqp->tag);
    568 				if (len > sp->cols - (L_NAME + L_SPACE))
    569 					len = sp->cols - (L_NAME + L_SPACE);
    570 				(void)ex_printf(sp, "%s%.*s",
    571 				    tqp->current == tp ? "    " : "     ",
    572 				    (int)len, tqp->tag);
    573 			}
    574 			(void)ex_printf(sp, "\n");
    575 		}
    576 	return (0);
    577 }
    578 
    579 /*
    580  * ex_tag_copy --
    581  *	Copy a screen's tag structures.
    582  *
    583  * PUBLIC: int ex_tag_copy __P((SCR *, SCR *));
    584  */
    585 int
    586 ex_tag_copy(SCR *orig, SCR *sp)
    587 {
    588 	EX_PRIVATE *oexp, *nexp;
    589 	TAGQ *aqp, *tqp;
    590 	TAG *ap, *tp;
    591 	TAGF *atfp, *tfp;
    592 
    593 	oexp = EXP(orig);
    594 	nexp = EXP(sp);
    595 
    596 	/* Copy tag queue and tags stack. */
    597 	TAILQ_FOREACH(aqp, &oexp->tq, q) {
    598 		if (tagq_copy(sp, aqp, &tqp))
    599 			return (1);
    600 		TAILQ_FOREACH(ap, &aqp->tagq, q) {
    601 			if (tag_copy(sp, ap, &tp))
    602 				return (1);
    603 			/* Set the current pointer. */
    604 			if (aqp->current == ap)
    605 				tqp->current = tp;
    606 			TAILQ_INSERT_TAIL(&tqp->tagq, tp, q);
    607 		}
    608 		TAILQ_INSERT_TAIL(&nexp->tq, tqp, q);
    609 		F_SET(tqp, TAG_IS_LINKED);
    610 	}
    611 
    612 	/* Copy list of tag files. */
    613 
    614 	TAILQ_FOREACH(atfp, &oexp->tagfq, q) {
    615 		if (tagf_copy(sp, atfp, &tfp))
    616 			return (1);
    617 		TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
    618 	}
    619 
    620 	/* Copy the last tag. */
    621 	if (oexp->tag_last != NULL &&
    622 	    (nexp->tag_last = v_wstrdup(sp, oexp->tag_last,
    623 					STRLEN(oexp->tag_last))) == NULL) {
    624 		msgq(sp, M_SYSERR, NULL);
    625 		return (1);
    626 	}
    627 	return (0);
    628 }
    629 
    630 /*
    631  * tagf_copy --
    632  *	Copy a TAGF structure and return it in new memory.
    633  */
    634 static int
    635 tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp)
    636 {
    637 	TAGF *tfp;
    638 
    639 	MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
    640 	*tfp = *otfp;
    641 
    642 	/* XXX: Allocate as part of the TAGF structure!!! */
    643 	if ((tfp->name = strdup(otfp->name)) == NULL)
    644 		return (1);
    645 
    646 	*tfpp = tfp;
    647 	return (0);
    648 }
    649 
    650 /*
    651  * tagq_copy --
    652  *	Copy a TAGQ structure and return it in new memory.
    653  */
    654 static int
    655 tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp)
    656 {
    657 	TAGQ *tqp;
    658 	size_t len;
    659 
    660 	len = sizeof(TAGQ);
    661 	if (otqp->tag != NULL)
    662 		len += otqp->tlen + 1;
    663 	MALLOC_RET(sp, tqp, TAGQ *, len);
    664 	memcpy(tqp, otqp, len);
    665 
    666 	TAILQ_INIT(&tqp->tagq);
    667 	tqp->current = NULL;
    668 	if (otqp->tag != NULL)
    669 		tqp->tag = tqp->buf;
    670 
    671 	*tqpp = tqp;
    672 	return (0);
    673 }
    674 
    675 /*
    676  * tag_copy --
    677  *	Copy a TAG structure and return it in new memory.
    678  */
    679 static int
    680 tag_copy(SCR *sp, TAG *otp, TAG **tpp)
    681 {
    682 	TAG *tp;
    683 	size_t len;
    684 
    685 	len = sizeof(TAG);
    686 	if (otp->fname != NULL)
    687 		len += otp->fnlen + 1;
    688 	if (otp->search != NULL)
    689 		len += otp->slen + 1;
    690 	if (otp->msg != NULL)
    691 		len += otp->mlen + 1;
    692 	MALLOC_RET(sp, tp, TAG *, len);
    693 	memcpy(tp, otp, len);
    694 
    695 	if (otp->fname != NULL)
    696 		tp->fname = (char *)tp->buf;
    697 	if (otp->search != NULL)
    698 		tp->search = tp->buf + (otp->search - otp->buf);
    699 	if (otp->msg != NULL)
    700 		tp->msg = tp->buf + (otp->msg - otp->buf);
    701 
    702 	*tpp = tp;
    703 	return (0);
    704 }
    705 
    706 /*
    707  * tagf_free --
    708  *	Free a TAGF structure.
    709  */
    710 static int
    711 tagf_free(SCR *sp, TAGF *tfp)
    712 {
    713 	EX_PRIVATE *exp;
    714 
    715 	exp = EXP(sp);
    716 	TAILQ_REMOVE(&exp->tagfq, tfp, q);
    717 	free(tfp->name);
    718 	free(tfp);
    719 	return (0);
    720 }
    721 
    722 /*
    723  * tagq_free --
    724  *	Free a TAGQ structure (and associated TAG structures).
    725  *
    726  * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
    727  */
    728 int
    729 tagq_free(SCR *sp, TAGQ *tqp)
    730 {
    731 	EX_PRIVATE *exp;
    732 	TAG *tp;
    733 
    734 	exp = EXP(sp);
    735 	while ((tp = TAILQ_FIRST(&tqp->tagq)) != NULL) {
    736 		TAILQ_REMOVE(&tqp->tagq, tp, q);
    737 		free(tp);
    738 	}
    739 	/*
    740 	 * !!!
    741 	 * If allocated and then the user failed to switch files, the TAGQ
    742 	 * structure was never attached to any list.
    743 	 */
    744 	if (F_ISSET(tqp, TAG_IS_LINKED))
    745 		TAILQ_REMOVE(&exp->tq, tqp, q);
    746 	free(tqp);
    747 	return (0);
    748 }
    749 
    750 /*
    751  * PUBLIC: int tagq_push __P((SCR*, TAGQ*, int, int ));
    752  */
    753 int
    754 tagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force)
    755 {
    756 	EX_PRIVATE *exp;
    757 	FREF *frp;
    758 	TAG *rtp;
    759 	TAGQ *rtqp;
    760 	db_recno_t lno;
    761 	size_t cno;
    762 	int istmp;
    763 	const char *np;
    764 	size_t nlen;
    765 
    766 	exp = EXP(sp);
    767 
    768 	/*
    769 	 * Allocate all necessary memory before swapping screens.  Initialize
    770 	 * flags so we know what to free.
    771 	 */
    772 	rtp = NULL;
    773 	rtqp = NULL;
    774 	if (TAILQ_EMPTY(&exp->tq)) {
    775 		/* Initialize the `local context' tag queue structure. */
    776 		CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
    777 		TAILQ_INIT(&rtqp->tagq);
    778 
    779 		/* Initialize and link in its tag structure. */
    780 		CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
    781 		TAILQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
    782 		rtqp->current = rtp;
    783 	}
    784 
    785 	/*
    786 	 * Stick the current context information in a convenient place, we're
    787 	 * about to lose it.  Note, if we're called on editor startup, there
    788 	 * will be no FREF structure.
    789 	 */
    790 	frp = sp->frp;
    791 	lno = sp->lno;
    792 	cno = sp->cno;
    793 	istmp = frp == NULL || (F_ISSET(frp, FR_TMPFILE) && !new_screen);
    794 
    795 	/* Try to switch to the tag. */
    796 	if (new_screen) {
    797 		if (ex_tag_Nswitch(sp, TAILQ_FIRST(&tqp->tagq), force))
    798 			goto err;
    799 
    800 		/* Everything else gets done in the new screen. */
    801 		sp = sp->nextdisp;
    802 		exp = EXP(sp);
    803 	} else
    804 		if (ex_tag_nswitch(sp, TAILQ_FIRST(&tqp->tagq), force))
    805 			goto err;
    806 
    807 	/*
    808 	 * If this is the first tag, put a `current location' queue entry
    809 	 * in place, so we can pop all the way back to the current mark.
    810 	 * Note, it doesn't point to much of anything, it's a placeholder.
    811 	 */
    812 	if (TAILQ_EMPTY(&exp->tq)) {
    813 		TAILQ_INSERT_HEAD(&exp->tq, rtqp, q);
    814 		F_SET(rtqp, TAG_IS_LINKED);
    815 	} else {
    816 		free(rtqp);
    817 		rtqp = TAILQ_FIRST(&exp->tq);
    818 	}
    819 
    820 	/* Link the new TAGQ structure into place. */
    821 	TAILQ_INSERT_HEAD(&exp->tq, tqp, q);
    822 	F_SET(tqp, TAG_IS_LINKED);
    823 
    824 	(void)ctag_search(sp,
    825 	    tqp->current->search, tqp->current->slen, tqp->tag);
    826 	if (tqp->current->msg) {
    827 	    INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
    828 		     np, nlen);
    829 	    msgq(sp, M_INFO, "%s", np);
    830 	}
    831 
    832 	/*
    833 	 * Move the current context from the temporary save area into the
    834 	 * right structure.
    835 	 *
    836 	 * If we were in a temporary file, we don't have a context to which
    837 	 * we can return, so just make it be the same as what we're moving
    838 	 * to.  It will be a little odd that ^T doesn't change anything, but
    839 	 * I don't think it's a big deal.
    840 	 */
    841 	if (istmp) {
    842 		rtqp->current->frp = sp->frp;
    843 		rtqp->current->lno = sp->lno;
    844 		rtqp->current->cno = sp->cno;
    845 	} else {
    846 		rtqp->current->frp = frp;
    847 		rtqp->current->lno = lno;
    848 		rtqp->current->cno = cno;
    849 	}
    850 	return (0);
    851 
    852 err:
    853 alloc_err:
    854 	if (rtqp != NULL)
    855 		free(rtqp);
    856 	if (rtp != NULL)
    857 		free(rtp);
    858 	tagq_free(sp, tqp);
    859 	return (1);
    860 }
    861 
    862 /*
    863  * tag_msg
    864  *	A few common messages.
    865  *
    866  * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
    867  */
    868 void
    869 tag_msg(SCR *sp, tagmsg_t msg, char *tag)
    870 {
    871 	switch (msg) {
    872 	case TAG_BADLNO:
    873 		msgq_str(sp, M_ERR, tag,
    874 	    "164|%s: the tag's line number is past the end of the file");
    875 		break;
    876 	case TAG_EMPTY:
    877 		msgq(sp, M_INFO, "165|The tags stack is empty");
    878 		break;
    879 	case TAG_SEARCH:
    880 		msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
    881 		break;
    882 	default:
    883 		abort();
    884 	}
    885 }
    886 
    887 /*
    888  * ex_tagf_alloc --
    889  *	Create a new list of ctag files.
    890  *
    891  * PUBLIC: int ex_tagf_alloc __P((SCR *, const char *));
    892  */
    893 int
    894 ex_tagf_alloc(SCR *sp, const char *str)
    895 {
    896 	EX_PRIVATE *exp;
    897 	TAGF *tfp;
    898 	size_t len;
    899 	const char *p, *t;
    900 
    901 	/* Free current queue. */
    902 	exp = EXP(sp);
    903 	while ((tfp = TAILQ_FIRST(&exp->tagfq)) != NULL)
    904 		tagf_free(sp, tfp);
    905 
    906 	/* Create new queue. */
    907 	for (p = t = str;; ++p) {
    908 		if (*p == '\0' || isblank((unsigned char)*p)) {
    909 			if ((len = p - t) > 1) {
    910 				MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
    911 				MALLOC(sp, tfp->name, char *, len + 1);
    912 				if (tfp->name == NULL) {
    913 					free(tfp);
    914 					return (1);
    915 				}
    916 				memcpy(tfp->name, t, len);
    917 				tfp->name[len] = '\0';
    918 				tfp->flags = 0;
    919 				TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q);
    920 			}
    921 			t = p + 1;
    922 		}
    923 		if (*p == '\0')
    924 			 break;
    925 	}
    926 	return (0);
    927 }
    928 						/* Free previous queue. */
    929 /*
    930  * ex_tag_free --
    931  *	Free the ex tag information.
    932  *
    933  * PUBLIC: int ex_tag_free __P((SCR *));
    934  */
    935 int
    936 ex_tag_free(SCR *sp)
    937 {
    938 	EX_PRIVATE *exp;
    939 	TAGF *tfp;
    940 	TAGQ *tqp;
    941 
    942 	/* Free up tag information. */
    943 	exp = EXP(sp);
    944 	while ((tqp = TAILQ_FIRST(&exp->tq)) != NULL)
    945 		tagq_free(sp, tqp);
    946 	while ((tfp = TAILQ_FIRST(&exp->tagfq)) != NULL)
    947 		tagf_free(sp, tfp);
    948 	if (exp->tag_last != NULL)
    949 		free(exp->tag_last);
    950 	return (0);
    951 }
    952 
    953 /*
    954  * ctag_search --
    955  *	Search a file for a tag.
    956  */
    957 static int
    958 ctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag)
    959 {
    960 	MARK m;
    961 	char *p;
    962 	const char *np;
    963 	size_t nlen;
    964 
    965 	/*
    966 	 * !!!
    967 	 * The historic tags file format (from a long, long time ago...)
    968 	 * used a line number, not a search string.  I got complaints, so
    969 	 * people are still using the format.  POSIX 1003.2 permits it.
    970 	 */
    971 	if (ISDIGIT((UCHAR_T)search[0])) {
    972 		INT2CHAR(sp, search, slen+1, np, nlen);
    973 		m.lno = atoi(np);
    974 		if (!db_exist(sp, m.lno)) {
    975 			tag_msg(sp, TAG_BADLNO, tag);
    976 			return (1);
    977 		}
    978 	} else {
    979 		/*
    980 		 * Search for the tag; cheap fallback for C functions
    981 		 * if the name is the same but the arguments have changed.
    982 		 */
    983 		m.lno = 1;
    984 		m.cno = 0;
    985 		if (f_search(sp, &m, &m,
    986 		    search, slen, NULL,
    987 		    SEARCH_FIRST | SEARCH_TAG | SEARCH_PARSE)) {
    988 			INT2CHAR(sp, search, slen, np, nlen);
    989 			if ((p = strrchr(np, '(')) != NULL) {
    990 				slen = p - np;
    991 				if (f_search(sp, &m, &m, search, slen,
    992 				    NULL, SEARCH_FIRST | SEARCH_TAG))
    993 					goto notfound;
    994 			} else {
    995 notfound:			tag_msg(sp, TAG_SEARCH, tag);
    996 				return (1);
    997 			}
    998 		}
    999 		/*
   1000 		 * !!!
   1001 		 * Historically, tags set the search direction if it wasn't
   1002 		 * already set.
   1003 		 */
   1004 		if (sp->searchdir == NOTSET)
   1005 			sp->searchdir = FORWARD;
   1006 	}
   1007 
   1008 	/*
   1009 	 * !!!
   1010 	 * Tags move to the first non-blank, NOT the search pattern start.
   1011 	 */
   1012 	sp->lno = m.lno;
   1013 	sp->cno = 0;
   1014 	(void)nonblank(sp, sp->lno, &sp->cno);
   1015 	return (0);
   1016 }
   1017 
   1018 #ifdef GTAGS
   1019 /*
   1020  * getentry --
   1021  *	get tag information from current line.
   1022  *
   1023  * gtags temporary file format.
   1024  * <tag>   <lineno>  <file>         <image>
   1025  *
   1026  * sample.
   1027  * +------------------------------------------------
   1028  * |main     30      main.c         main(argc, argv)
   1029  * |func     21      subr.c         func(arg)
   1030  */
   1031 static int
   1032 getentry(char *buf, char **tag, char **file, char **line)
   1033 {
   1034 	char *p = buf;
   1035 
   1036 	for (*tag = p; *p && !isspace((unsigned char)*p); p++)	/* tag name */
   1037 		;
   1038 	if (*p == 0)
   1039 		goto err;
   1040 	*p++ = 0;
   1041 	for (; *p && isspace((unsigned char)*p); p++)	/* (skip blanks) */
   1042 		;
   1043 	if (*p == 0)
   1044 		goto err;
   1045 	*line = p;					/* line no */
   1046 	for (*line = p; *p && !isspace((unsigned char)*p); p++)
   1047 		;
   1048 	if (*p == 0)
   1049 		goto err;
   1050 	*p++ = 0;
   1051 	for (; *p && isspace((unsigned char)*p); p++)	/* (skip blanks) */
   1052 		;
   1053 	if (*p == 0)
   1054 		goto err;
   1055 	*file = p;					/* file name */
   1056 	for (*file = p; *p && !isspace((unsigned char)*p); p++)
   1057 		;
   1058 	if (*p == 0)
   1059 		goto err;
   1060 	*p = 0;
   1061 
   1062 	/* value check */
   1063 	if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
   1064 		return 1;	/* OK */
   1065 err:
   1066 	return 0;		/* ERROR */
   1067 }
   1068 
   1069 /*
   1070  * gtag_slist --
   1071  *	Search the list of tags files for a tag, and return tag queue.
   1072  */
   1073 static TAGQ *
   1074 gtag_slist(SCR *sp, CHAR_T *tag, int ref)
   1075 {
   1076 	TAGQ *tqp;
   1077 	size_t len, nlen, slen, wlen;
   1078 	TAG *tp;
   1079 	const char *np;
   1080 	char *name, *file, *search;
   1081 	char command[BUFSIZ];
   1082 	char buf[BUFSIZ];
   1083 	const CHAR_T *wp;
   1084 	FILE *fp;
   1085 
   1086 	/* Allocate and initialize the tag queue structure. */
   1087 	INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
   1088 	len = nlen - 1;
   1089 	CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
   1090 	TAILQ_INIT(&tqp->tagq);
   1091 	tqp->tag = tqp->buf;
   1092 	memcpy(tqp->tag, np, (tqp->tlen = len) + 1);
   1093 
   1094 	/*
   1095 	 * Find the tag, only display missing file messages once, and
   1096 	 * then only if we didn't find the tag.
   1097 	 */
   1098 	snprintf(command, sizeof(command), "global -%s '%s'", ref ? "rx" : "x",
   1099 	    np);
   1100 	if ((fp = popen(command, "r")) != NULL) {
   1101 		while (fgets(buf, sizeof(buf), fp)) {
   1102 			if (buf[strlen(buf)-1] == '\n')		/* chop(buf) */
   1103 				buf[strlen(buf)-1] = 0;
   1104 			else
   1105 				while (fgetc(fp) != '\n')
   1106 					;
   1107 			if (getentry(buf, &name, &file, &search) == 0) {
   1108 				break;
   1109 			}
   1110 			slen = strlen(search);
   1111 			CALLOC_GOTO(sp, tp,
   1112 			    TAG *, 1, sizeof(TAG) + strlen(file) + 1 +
   1113 			    (slen + 1) * sizeof(CHAR_T));
   1114 			tp->fname = (char *)tp->buf;
   1115 			strcpy(tp->fname, file);
   1116 			tp->fnlen = strlen(file);
   1117 			tp->search = (CHAR_T *)(tp->fname + tp->fnlen + 1);
   1118 			CHAR2INT(sp, search, slen + 1, wp, wlen);
   1119 			MEMCPYW(tp->search, wp, (tp->slen = slen) + 1);
   1120 			TAILQ_INSERT_TAIL(&tqp->tagq, tp, q);
   1121 		}
   1122 		pclose(fp);
   1123 	}
   1124 
   1125 	/* Check to see if we found anything. */
   1126 	if (TAILQ_EMPTY(&tqp->tagq)) {
   1127 		msgq_str(sp, M_ERR, np, "162|%s: tag not found");
   1128 		free(tqp);
   1129 		return (NULL);
   1130 	}
   1131 
   1132 	tqp->current = TAILQ_FIRST(&tqp->tagq);
   1133 	return (tqp);
   1134 
   1135 alloc_err:
   1136 	free(tqp);
   1137 	return (NULL);
   1138 }
   1139 #endif
   1140 
   1141 /*
   1142  * ctag_slist --
   1143  *	Search the list of tags files for a tag, and return tag queue.
   1144  */
   1145 static TAGQ *
   1146 ctag_slist(SCR *sp, CHAR_T *tag)
   1147 {
   1148 	EX_PRIVATE *exp;
   1149 	TAGF *tfp;
   1150 	TAGQ *tqp;
   1151 	size_t len;
   1152 	int echk;
   1153 	const char *np;
   1154 	size_t nlen;
   1155 
   1156 	exp = EXP(sp);
   1157 
   1158 	/* Allocate and initialize the tag queue structure. */
   1159 	INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
   1160 	len = nlen - 1;
   1161 	CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
   1162 	TAILQ_INIT(&tqp->tagq);
   1163 	tqp->tag = tqp->buf;
   1164 	memcpy(tqp->tag, np, (tqp->tlen = len) + 1);
   1165 
   1166 	/*
   1167 	 * Find the tag, only display missing file messages once, and
   1168 	 * then only if we didn't find the tag.
   1169 	 */
   1170 	echk = 0;
   1171 	TAILQ_FOREACH(tfp, &exp->tagfq, q)
   1172 		if (ctag_sfile(sp, tfp, tqp, tqp->tag)) {
   1173 			echk = 1;
   1174 			F_SET(tfp, TAGF_ERR);
   1175 		} else
   1176 			F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN);
   1177 
   1178 	/* Check to see if we found anything. */
   1179 	if (TAILQ_EMPTY(&tqp->tagq)) {
   1180 		msgq_str(sp, M_ERR, tqp->tag, "162|%s: tag not found");
   1181 		if (echk)
   1182 			TAILQ_FOREACH(tfp, &exp->tagfq, q)
   1183 				if (F_ISSET(tfp, TAGF_ERR) &&
   1184 				    !F_ISSET(tfp, TAGF_ERR_WARN)) {
   1185 					errno = tfp->errnum;
   1186 					msgq_str(sp, M_SYSERR, tfp->name, "%s");
   1187 					F_SET(tfp, TAGF_ERR_WARN);
   1188 				}
   1189 		free(tqp);
   1190 		return (NULL);
   1191 	}
   1192 
   1193 	tqp->current = TAILQ_FIRST(&tqp->tagq);
   1194 	return (tqp);
   1195 
   1196 alloc_err:
   1197 	return (NULL);
   1198 }
   1199 
   1200 /*
   1201  * ctag_sfile --
   1202  *	Search a tags file for a tag, adding any found to the tag queue.
   1203  */
   1204 static int
   1205 ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname)
   1206 {
   1207 	struct stat sb;
   1208 	TAG *tp;
   1209 	size_t dlen, nlen = 0, slen;
   1210 	int fd, i, nf1, nf2;
   1211 	char *back, *front, *map, *p, *search, *t;
   1212 	char *cname = NULL, *dname = NULL, *name = NULL;
   1213 	const CHAR_T *wp;
   1214 	size_t wlen;
   1215 	unsigned long tl;
   1216 
   1217 	if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
   1218 		tfp->errnum = errno;
   1219 		return (1);
   1220 	}
   1221 
   1222 	/*
   1223 	 * XXX
   1224 	 * Some old BSD systems require MAP_FILE as an argument when mapping
   1225 	 * regular files.
   1226 	 */
   1227 #ifndef MAP_FILE
   1228 #define	MAP_FILE	0
   1229 #endif
   1230 	/*
   1231 	 * XXX
   1232 	 * We'd like to test if the file is too big to mmap.  Since we don't
   1233 	 * know what size or type off_t's or size_t's are, what the largest
   1234 	 * unsigned integral type is, or what random insanity the local C
   1235 	 * compiler will perpetrate, doing the comparison in a portable way
   1236 	 * is flatly impossible.  Hope mmap fails if the file is too large.
   1237 	 */
   1238 	if (fstat(fd, &sb) != 0 ||
   1239 	    (map = mmap(NULL, (size_t)sb.st_size, PROT_READ | PROT_WRITE,
   1240 	    MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
   1241 		tfp->errnum = errno;
   1242 		(void)close(fd);
   1243 		return (1);
   1244 	}
   1245 
   1246 	tl = O_VAL(sp, O_TAGLENGTH);
   1247 	front = map;
   1248 	back = front + sb.st_size;
   1249 	front = binary_search(tname, front, back);
   1250 	front = linear_search(tname, front, back, tl);
   1251 	if (front == NULL)
   1252 		goto done;
   1253 
   1254 	/*
   1255 	 * Initialize and link in the tag structure(s).  The historic ctags
   1256 	 * file format only permitted a single tag location per tag.  The
   1257 	 * obvious extension to permit multiple tags locations per tag is to
   1258 	 * output multiple records in the standard format.  Unfortunately,
   1259 	 * this won't work correctly with historic ex/vi implementations,
   1260 	 * because their binary search assumes that there's only one record
   1261 	 * per tag, and so will use a random tag entry if there si more than
   1262 	 * one.  This code handles either format.
   1263 	 *
   1264 	 * The tags file is in the following format:
   1265 	 *
   1266 	 *	<tag> <filename> <line number> | <pattern>
   1267 	 *
   1268 	 * Figure out how long everything is so we can allocate in one swell
   1269 	 * foop, but discard anything that looks wrong.
   1270 	 */
   1271 	for (;;) {
   1272 		/* Nul-terminate the end of the line. */
   1273 		for (p = front; p < back && *p != '\n'; ++p);
   1274 		if (p == back || *p != '\n')
   1275 			break;
   1276 		*p = '\0';
   1277 
   1278 		/* Update the pointers for the next time. */
   1279 		t = p + 1;
   1280 		p = front;
   1281 		front = t;
   1282 
   1283 		/* Break the line into tokens. */
   1284 		for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
   1285 			switch (i) {
   1286 			case 0:			/* Tag. */
   1287 				cname = t;
   1288 				break;
   1289 			case 1:			/* Filename. */
   1290 				name = t;
   1291 				nlen = strlen(name);
   1292 				break;
   1293 			}
   1294 
   1295 		/* Check for corruption. */
   1296 		if (i != 2 || p == NULL || t == NULL)
   1297 			goto corrupt;
   1298 
   1299 		/* The rest of the string is the search pattern. */
   1300 		search = p;
   1301 		if ((slen = strlen(p)) == 0) {
   1302 corrupt:		p = msg_print(sp, tname, &nf1);
   1303 			t = msg_print(sp, tfp->name, &nf2);
   1304 			msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t);
   1305 			if (nf1)
   1306 				FREE_SPACE(sp, p, 0);
   1307 			if (nf2)
   1308 				FREE_SPACE(sp, t, 0);
   1309 			continue;
   1310 		}
   1311 
   1312 		/* Check for passing the last entry. */
   1313 		if (tl ? strncmp(tname, cname, tl) : strcmp(tname, cname))
   1314 			break;
   1315 
   1316 		/* Resolve the file name. */
   1317 		ctag_file(sp, tfp, name, &dname, &dlen);
   1318 
   1319 		CALLOC_GOTO(sp, tp,
   1320 		    TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 +
   1321 		    (slen + 1) * sizeof(CHAR_T));
   1322 		tp->fname = (char *)tp->buf;
   1323 		if (dlen != 0) {
   1324 			memcpy(tp->fname, dname, dlen);
   1325 			tp->fname[dlen] = '/';
   1326 			++dlen;
   1327 		}
   1328 		memcpy(tp->fname + dlen, name, nlen + 1);
   1329 		tp->fnlen = dlen + nlen;
   1330 		tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1);
   1331 		CHAR2INT(sp, search, slen + 1, wp, wlen);
   1332 		MEMCPYW(tp->search, wp, (tp->slen = slen) + 1);
   1333 		TAILQ_INSERT_TAIL(&tqp->tagq, tp, q);
   1334 	}
   1335 
   1336 alloc_err:
   1337 done:	if (munmap(map, (size_t)sb.st_size))
   1338 		msgq(sp, M_SYSERR, "munmap");
   1339 	if (close(fd))
   1340 		msgq(sp, M_SYSERR, "close");
   1341 	return (0);
   1342 }
   1343 
   1344 /*
   1345  * ctag_file --
   1346  *	Search for the right path to this file.
   1347  */
   1348 static void
   1349 ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp)
   1350 {
   1351 	struct stat sb;
   1352 	char *p, buf[MAXPATHLEN];
   1353 
   1354 	/*
   1355 	 * !!!
   1356 	 * If the tag file path is a relative path, see if it exists.  If it
   1357 	 * doesn't, look relative to the tags file path.  It's okay for a tag
   1358 	 * file to not exist, and historically, vi simply displayed a "new"
   1359 	 * file.  However, if the path exists relative to the tag file, it's
   1360 	 * pretty clear what's happening, so we may as well get it right.
   1361 	 */
   1362 	*dlenp = 0;
   1363 	if (name[0] != '/' &&
   1364 	    stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
   1365 		*p = '\0';
   1366 		(void)snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name);
   1367 		if (stat(buf, &sb) == 0) {
   1368 			*dirp = tfp->name;
   1369 			*dlenp = strlen(*dirp);
   1370 		}
   1371 		*p = '/';
   1372 	}
   1373 }
   1374 
   1375 /*
   1376  * Binary search for "string" in memory between "front" and "back".
   1377  *
   1378  * This routine is expected to return a pointer to the start of a line at
   1379  * *or before* the first word matching "string".  Relaxing the constraint
   1380  * this way simplifies the algorithm.
   1381  *
   1382  * Invariants:
   1383  * 	front points to the beginning of a line at or before the first
   1384  *	matching string.
   1385  *
   1386  * 	back points to the beginning of a line at or after the first
   1387  *	matching line.
   1388  *
   1389  * Base of the Invariants.
   1390  * 	front = NULL;
   1391  *	back = EOF;
   1392  *
   1393  * Advancing the Invariants:
   1394  *
   1395  * 	p = first newline after halfway point from front to back.
   1396  *
   1397  * 	If the string at "p" is not greater than the string to match,
   1398  *	p is the new front.  Otherwise it is the new back.
   1399  *
   1400  * Termination:
   1401  *
   1402  * 	The definition of the routine allows it return at any point,
   1403  *	since front is always at or before the line to print.
   1404  *
   1405  * 	In fact, it returns when the chosen "p" equals "back".  This
   1406  *	implies that there exists a string is least half as long as
   1407  *	(back - front), which in turn implies that a linear search will
   1408  *	be no more expensive than the cost of simply printing a string or two.
   1409  *
   1410  * 	Trying to continue with binary search at this point would be
   1411  *	more trouble than it's worth.
   1412  */
   1413 #define	EQUAL		0
   1414 #define	GREATER		1
   1415 #define	LESS		(-1)
   1416 
   1417 #define	SKIP_PAST_NEWLINE(p, back)	while (p < back && *p++ != '\n') continue;
   1418 
   1419 static char *
   1420 binary_search(register char *string, register char *front, register char *back)
   1421 {
   1422 	register char *p;
   1423 
   1424 	p = front + (back - front) / 2;
   1425 	SKIP_PAST_NEWLINE(p, back);
   1426 
   1427 	while (p != back) {
   1428 		if (compare(string, p, back) == GREATER)
   1429 			front = p;
   1430 		else
   1431 			back = p;
   1432 		p = front + (back - front) / 2;
   1433 		SKIP_PAST_NEWLINE(p, back);
   1434 	}
   1435 	return (front);
   1436 }
   1437 
   1438 /*
   1439  * Find the first line that starts with string, linearly searching from front
   1440  * to back.
   1441  *
   1442  * Return NULL for no such line.
   1443  *
   1444  * This routine assumes:
   1445  *
   1446  * 	o front points at the first character in a line.
   1447  *	o front is before or at the first line to be printed.
   1448  */
   1449 static char *
   1450 linear_search(char *string, char *front, char *back, unsigned long tl)
   1451 {
   1452 	char *end;
   1453 	while (front < back) {
   1454 		end = tl && (unsigned long)(back-front) > tl ? front+tl : back;
   1455 		switch (compare(string, front, end)) {
   1456 		case EQUAL:		/* Found it. */
   1457 			return (front);
   1458 		case LESS:		/* No such string. */
   1459 			return (NULL);
   1460 		case GREATER:		/* Keep going. */
   1461 			break;
   1462 		}
   1463 		SKIP_PAST_NEWLINE(front, back);
   1464 	}
   1465 	return (NULL);
   1466 }
   1467 
   1468 /*
   1469  * Return LESS, GREATER, or EQUAL depending on how the string1 compares
   1470  * with string2 (s1 ??? s2).
   1471  *
   1472  * 	o Matches up to len(s1) are EQUAL.
   1473  *	o Matches up to len(s2) are GREATER.
   1474  *
   1475  * The string "s1" is null terminated.  The string s2 is '\t', space, (or
   1476  * "back") terminated.
   1477  *
   1478  * !!!
   1479  * Reasonably modern ctags programs use tabs as separators, not spaces.
   1480  * However, historic programs did use spaces, and, I got complaints.
   1481  */
   1482 static int
   1483 compare(register char *s1, register char *s2, register char *back)
   1484 {
   1485 	for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
   1486 		if (*s1 != *s2)
   1487 			return (*s1 < *s2 ? LESS : GREATER);
   1488 	return (*s1 ? GREATER : s2 < back &&
   1489 	    (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);
   1490 }
   1491