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