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