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