GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/vi/build/../ex/ex_tag.c Lines: 0 446 0.0 %
Date: 2017-11-07 Branches: 0 348 0.0 %

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