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