1/*-
2 * Copyright (c) 1992, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 1992, 1993, 1994, 1995, 1996
5 *	Keith Bostic.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * David Hitz of Auspex Systems, Inc.
9 *
10 * See the LICENSE file for redistribution information.
11 */
12
13#include "config.h"
14
15#ifndef lint
16static const char sccsid[] = "@(#)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
45static char	*binary_search __P((char *, char *, char *));
46static int	 compare __P((char *, char *, char *));
47static void	 ctag_file __P((SCR *, TAGF *, char *, char **, size_t *));
48static int	 ctag_search __P((SCR *, char *, size_t, char *));
49#ifdef GTAGS
50static int	 getentry __P((char *, char **, char **, char **));
51static TAGQ	*gtag_slist __P((SCR *, char *, int));
52#endif
53static int	 ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
54static TAGQ	*ctag_slist __P((SCR *, char *));
55static char	*linear_search __P((char *, char *, char *));
56static int	 tag_copy __P((SCR *, TAG *, TAG **));
57static int	 tag_pop __P((SCR *, TAGQ *, int));
58static int	 tagf_copy __P((SCR *, TAGF *, TAGF **));
59static int	 tagf_free __P((SCR *, TAGF *));
60static 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 */
68int
69ex_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 */
105int
106ex_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 */
123int
124ex_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
250err:
251alloc_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 */
266int
267ex_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 */
301int
302ex_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 */
336int
337ex_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 */
373int
374ex_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 */
430int
431ex_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. */
475filearg:	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 */
511int
512ex_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 */
535static int
536tag_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 */
591int
592ex_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 */
670int
671ex_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 */
720static int
721tagf_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 */
742static int
743tagq_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 */
769static int
770tag_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 */
798static int
799tagf_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 */
818int
819tagq_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 */
848void
849tag_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 */
876int
877ex_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 */
920int
921ex_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 */
943static int
944ctag_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 {
979notfound:			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 */
1014static int
1015getentry(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 */
1049err:
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 */
1057static TAGQ *
1058gtag_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
1120alloc_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 */
1128static TAGQ *
1129ctag_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
1179alloc_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 */
1187static int
1188ctag_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) {
1284corrupt:		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
1316alloc_err:
1317done:	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 */
1328static void
1329ctag_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
1404static char *
1405binary_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 */
1435static char *
1436linear_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 */
1467static int
1468compare(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