ignore errors on making txt and html output
[nvi.git] / ex / ex_tag.c
blobca6f540e1fae0701ca64f4d6754a1d1909466c10
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.
7 * This code is derived from software contributed to Berkeley by
8 * David Hitz of Auspex Systems, Inc.
10 * See the LICENSE file for redistribution information.
13 #include "config.h"
15 #ifndef lint
16 static const char sccsid[] = "$Id: ex_tag.c,v 10.48 2001/06/25 15:19:20 skimo Exp $ (Berkeley) $Date: 2001/06/25 15:19:20 $";
17 #endif /* not lint */
19 #include <sys/param.h>
20 #include <sys/types.h> /* XXX: param.h may not have included types.h */
22 #ifdef HAVE_SYS_MMAN_H
23 #include <sys/mman.h>
24 #endif
26 #include <sys/queue.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
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>
41 #include "../common/common.h"
42 #include "../vi/vi.h"
43 #include "tag.h"
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_T *, size_t, char *));
49 static int ctag_sfile __P((SCR *, TAGF *, TAGQ *, char *));
50 static TAGQ *ctag_slist __P((SCR *, CHAR_T *));
51 static char *linear_search __P((char *, char *, char *));
52 static int tag_copy __P((SCR *, TAG *, TAG **));
53 static int tag_pop __P((SCR *, TAGQ *, int));
54 static int tagf_copy __P((SCR *, TAGF *, TAGF **));
55 static int tagf_free __P((SCR *, TAGF *));
56 static int tagq_copy __P((SCR *, TAGQ *, TAGQ **));
59 * ex_tag_first --
60 * The tag code can be entered from main, e.g., "vi -t tag".
62 * PUBLIC: int ex_tag_first __P((SCR *, CHAR_T *));
64 int
65 ex_tag_first(SCR *sp, CHAR_T *tagarg)
67 EXCMD cmd;
69 /* Build an argument for the ex :tag command. */
70 ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, OOBLNO, 0);
71 argv_exp0(sp, &cmd, tagarg, STRLEN(tagarg));
74 * XXX
75 * Historic vi went ahead and created a temporary file when it failed
76 * to find the tag. We match historic practice, but don't distinguish
77 * between real error and failure to find the tag.
79 if (ex_tag_push(sp, &cmd))
80 return (0);
82 /* Display tags in the center of the screen. */
83 F_CLR(sp, SC_SCR_TOP);
84 F_SET(sp, SC_SCR_CENTER);
86 return (0);
90 * ex_tag_push -- ^]
91 * :tag[!] [string]
93 * Enter a new TAGQ context based on a ctag string.
95 * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *));
97 int
98 ex_tag_push(SCR *sp, EXCMD *cmdp)
100 EX_PRIVATE *exp;
101 TAGQ *tqp;
102 long tl;
104 exp = EXP(sp);
105 switch (cmdp->argc) {
106 case 1:
107 if (exp->tag_last != NULL)
108 free(exp->tag_last);
110 if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp,
111 cmdp->argv[0]->len)) == NULL) {
112 msgq(sp, M_SYSERR, NULL);
113 return (1);
116 /* Taglength may limit the number of characters. */
117 if ((tl =
118 O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl)
119 exp->tag_last[tl] = '\0';
120 break;
121 case 0:
122 if (exp->tag_last == NULL) {
123 msgq(sp, M_ERR, "158|No previous tag entered");
124 return (1);
126 break;
127 default:
128 abort();
131 /* Get the tag information. */
132 if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
133 return (1);
135 if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN),
136 FL_ISSET(cmdp->iflags, E_C_FORCE)))
137 return 1;
139 return 0;
143 * ex_tag_next --
144 * Switch context to the next TAG.
146 * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
149 ex_tag_next(SCR *sp, EXCMD *cmdp)
151 EX_PRIVATE *exp;
152 TAG *tp;
153 TAGQ *tqp;
154 char *np;
155 size_t nlen;
157 exp = EXP(sp);
158 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
159 tag_msg(sp, TAG_EMPTY, NULL);
160 return (1);
162 if ((tp = tqp->current->q.cqe_next) == (void *)&tqp->tagq) {
163 msgq(sp, M_ERR, "282|Already at the last tag of this group");
164 return (1);
166 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
167 return (1);
168 tqp->current = tp;
170 if (F_ISSET(tqp, TAG_CSCOPE))
171 (void)cscope_search(sp, tqp, tp);
172 else
173 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
174 if (tqp->current->msg) {
175 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
176 np, nlen);
177 msgq(sp, M_INFO, np);
179 return (0);
183 * ex_tag_prev --
184 * Switch context to the next TAG.
186 * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
189 ex_tag_prev(SCR *sp, EXCMD *cmdp)
191 EX_PRIVATE *exp;
192 TAG *tp;
193 TAGQ *tqp;
194 char *np;
195 size_t nlen;
197 exp = EXP(sp);
198 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
199 tag_msg(sp, TAG_EMPTY, NULL);
200 return (0);
202 if ((tp = tqp->current->q.cqe_prev) == (void *)&tqp->tagq) {
203 msgq(sp, M_ERR, "255|Already at the first tag of this group");
204 return (1);
206 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
207 return (1);
208 tqp->current = tp;
210 if (F_ISSET(tqp, TAG_CSCOPE))
211 (void)cscope_search(sp, tqp, tp);
212 else
213 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
214 if (tqp->current->msg) {
215 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
216 np, nlen);
217 msgq(sp, M_INFO, np);
219 return (0);
223 * ex_tag_nswitch --
224 * Switch context to the specified TAG.
226 * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int));
229 ex_tag_nswitch(SCR *sp, TAG *tp, int force)
231 /* Get a file structure. */
232 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
233 return (1);
235 /* If not changing files, return, we're done. */
236 if (tp->frp == sp->frp)
237 return (0);
239 /* Check for permission to leave. */
240 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
241 return (1);
243 /* Initialize the new file. */
244 if (file_init(sp, tp->frp, NULL, FS_SETALT))
245 return (1);
247 /* Display tags in the center of the screen. */
248 F_CLR(sp, SC_SCR_TOP);
249 F_SET(sp, SC_SCR_CENTER);
251 /* Switch. */
252 F_SET(sp, SC_FSWITCH);
253 return (0);
257 * ex_tag_Nswitch --
258 * Switch context to the specified TAG in a new screen.
260 * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int));
263 ex_tag_Nswitch(SCR *sp, TAG *tp, int force)
265 SCR *new;
267 /* Get a file structure. */
268 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
269 return (1);
271 /* Get a new screen. */
272 if (screen_init(sp->gp, sp, &new))
273 return (1);
274 if (vs_split(sp, new, 0)) {
275 (void)file_end(new, new->ep, 1);
276 (void)screen_end(new);
277 return (1);
280 /* Get a backing file. */
281 if (tp->frp == sp->frp) {
282 /* Copy file state. */
283 new->ep = sp->ep;
284 ++new->ep->refcnt;
286 new->frp = tp->frp;
287 new->frp->flags = sp->frp->flags;
288 } else if (file_init(new, tp->frp, NULL, force)) {
289 (void)vs_discard(new, NULL);
290 (void)screen_end(new);
291 return (1);
294 /* Create the argument list. */
295 new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name);
297 /* Display tags in the center of the screen. */
298 F_CLR(new, SC_SCR_TOP);
299 F_SET(new, SC_SCR_CENTER);
301 /* Switch. */
302 sp->nextdisp = new;
303 F_SET(sp, SC_SSWITCH);
305 return (0);
309 * ex_tag_pop -- ^T
310 * :tagp[op][!] [number | file]
312 * Pop to a previous TAGQ context.
314 * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *));
317 ex_tag_pop(SCR *sp, EXCMD *cmdp)
319 EX_PRIVATE *exp;
320 TAGQ *tqp, *dtqp;
321 size_t arglen;
322 long off;
323 char *arg, *p, *t;
324 size_t nlen;
326 /* Check for an empty stack. */
327 exp = EXP(sp);
328 if (exp->tq.cqh_first == (void *)&exp->tq) {
329 tag_msg(sp, TAG_EMPTY, NULL);
330 return (1);
333 /* Find the last TAG structure that we're going to DISCARD! */
334 switch (cmdp->argc) {
335 case 0: /* Pop one tag. */
336 dtqp = exp->tq.cqh_first;
337 break;
338 case 1: /* Name or number. */
339 INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1,
340 arg, nlen);
341 off = strtol(arg, &p, 10);
342 if (*p != '\0')
343 goto filearg;
345 /* Number: pop that many queue entries. */
346 if (off < 1)
347 return (0);
348 for (tqp = exp->tq.cqh_first;
349 tqp != (void *)&exp->tq && --off > 1;
350 tqp = tqp->q.cqe_next);
351 if (tqp == (void *)&exp->tq) {
352 msgq(sp, M_ERR,
353 "159|Less than %s entries on the tags stack; use :display t[ags]",
354 arg);
355 return (1);
357 dtqp = tqp;
358 break;
360 /* File argument: pop to that queue entry. */
361 filearg: arglen = strlen(arg);
362 for (tqp = exp->tq.cqh_first;
363 tqp != (void *)&exp->tq;
364 dtqp = tqp, tqp = tqp->q.cqe_next) {
365 /* Don't pop to the current file. */
366 if (tqp == exp->tq.cqh_first)
367 continue;
368 p = tqp->current->frp->name;
369 if ((t = strrchr(p, '/')) == NULL)
370 t = p;
371 else
372 ++t;
373 if (!strncmp(arg, t, arglen))
374 break;
376 if (tqp == (void *)&exp->tq) {
377 msgq_str(sp, M_ERR, arg,
378 "160|No file %s on the tags stack to return to; use :display t[ags]");
379 return (1);
381 if (tqp == exp->tq.cqh_first)
382 return (0);
383 break;
384 default:
385 abort();
388 return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)));
392 * ex_tag_top -- :tagt[op][!]
393 * Clear the tag stack.
395 * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *));
398 ex_tag_top(SCR *sp, EXCMD *cmdp)
400 EX_PRIVATE *exp;
402 exp = EXP(sp);
404 /* Check for an empty stack. */
405 if (exp->tq.cqh_first == (void *)&exp->tq) {
406 tag_msg(sp, TAG_EMPTY, NULL);
407 return (1);
410 /* Return to the oldest information. */
411 return (tag_pop(sp,
412 exp->tq.cqh_last->q.cqe_prev, FL_ISSET(cmdp->iflags, E_C_FORCE)));
416 * tag_pop --
417 * Pop up to and including the specified TAGQ context.
419 static int
420 tag_pop(SCR *sp, TAGQ *dtqp, int force)
422 EX_PRIVATE *exp;
423 TAG *tp;
424 TAGQ *tqp;
426 exp = EXP(sp);
429 * Update the cursor from the saved TAG information of the TAG
430 * structure we're moving to.
432 tp = dtqp->q.cqe_next->current;
433 if (tp->frp == sp->frp) {
434 sp->lno = tp->lno;
435 sp->cno = tp->cno;
436 } else {
437 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
438 return (1);
440 tp->frp->lno = tp->lno;
441 tp->frp->cno = tp->cno;
442 F_SET(sp->frp, FR_CURSORSET);
443 if (file_init(sp, tp->frp, NULL, FS_SETALT))
444 return (1);
446 F_SET(sp, SC_FSWITCH);
449 /* Pop entries off the queue up to and including dtqp. */
450 do {
451 tqp = exp->tq.cqh_first;
452 if (tagq_free(sp, tqp))
453 return (0);
454 } while (tqp != dtqp);
457 * If only a single tag left, we've returned to the first tag point,
458 * and the stack is now empty.
460 if (exp->tq.cqh_first->q.cqe_next == (void *)&exp->tq)
461 tagq_free(sp, exp->tq.cqh_first);
463 return (0);
467 * ex_tag_display --
468 * Display the list of tags.
470 * PUBLIC: int ex_tag_display __P((SCR *));
473 ex_tag_display(SCR *sp)
475 EX_PRIVATE *exp;
476 TAG *tp;
477 TAGQ *tqp;
478 int cnt;
479 size_t len;
480 char *p;
482 exp = EXP(sp);
483 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
484 tag_msg(sp, TAG_EMPTY, NULL);
485 return (0);
489 * We give the file name 20 columns and the search string the rest.
490 * If there's not enough room, we don't do anything special, it's
491 * not worth the effort, it just makes the display more confusing.
493 * We also assume that characters in file names map 1-1 to printing
494 * characters. This might not be true, but I don't think it's worth
495 * fixing. (The obvious fix is to pass the filenames through the
496 * msg_print function.)
498 #define L_NAME 30 /* Name. */
499 #define L_SLOP 4 /* Leading number plus trailing *. */
500 #define L_SPACE 5 /* Spaces after name, before tag. */
501 #define L_TAG 20 /* Tag. */
502 if (sp->cols <= L_NAME + L_SLOP) {
503 msgq(sp, M_ERR, "292|Display too small.");
504 return (0);
508 * Display the list of tags for each queue entry. The first entry
509 * is numbered, and the current tag entry has an asterisk appended.
511 for (cnt = 1, tqp = exp->tq.cqh_first; !INTERRUPTED(sp) &&
512 tqp != (void *)&exp->tq; ++cnt, tqp = tqp->q.cqe_next)
513 for (tp = tqp->tagq.cqh_first;
514 tp != (void *)&tqp->tagq; tp = tp->q.cqe_next) {
515 if (tp == tqp->tagq.cqh_first)
516 (void)ex_printf(sp, "%2d ", cnt);
517 else
518 (void)ex_printf(sp, " ");
519 p = tp->frp == NULL ? tp->fname : tp->frp->name;
520 if ((len = strlen(p)) > L_NAME) {
521 len = len - (L_NAME - 4);
522 (void)ex_printf(sp, " ... %*.*s",
523 L_NAME - 4, L_NAME - 4, p + len);
524 } else
525 (void)ex_printf(sp,
526 " %*.*s", L_NAME, L_NAME, p);
527 if (tqp->current == tp)
528 (void)ex_printf(sp, "*");
530 if (tp == tqp->tagq.cqh_first && tqp->tag != NULL &&
531 (sp->cols - L_NAME) >= L_TAG + L_SPACE) {
532 len = strlen(tqp->tag);
533 if (len > sp->cols - (L_NAME + L_SPACE))
534 len = sp->cols - (L_NAME + L_SPACE);
535 (void)ex_printf(sp, "%s%.*s",
536 tqp->current == tp ? " " : " ",
537 (int)len, tqp->tag);
539 (void)ex_printf(sp, "\n");
541 return (0);
545 * ex_tag_copy --
546 * Copy a screen's tag structures.
548 * PUBLIC: int ex_tag_copy __P((SCR *, SCR *));
551 ex_tag_copy(SCR *orig, SCR *sp)
553 EX_PRIVATE *oexp, *nexp;
554 TAGQ *aqp, *tqp;
555 TAG *ap, *tp;
556 TAGF *atfp, *tfp;
558 oexp = EXP(orig);
559 nexp = EXP(sp);
561 /* Copy tag queue and tags stack. */
562 for (aqp = oexp->tq.cqh_first;
563 aqp != (void *)&oexp->tq; aqp = aqp->q.cqe_next) {
564 if (tagq_copy(sp, aqp, &tqp))
565 return (1);
566 for (ap = aqp->tagq.cqh_first;
567 ap != (void *)&aqp->tagq; ap = ap->q.cqe_next) {
568 if (tag_copy(sp, ap, &tp))
569 return (1);
570 /* Set the current pointer. */
571 if (aqp->current == ap)
572 tqp->current = tp;
573 CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
575 CIRCLEQ_INSERT_TAIL(&nexp->tq, tqp, q);
578 /* Copy list of tag files. */
579 for (atfp = oexp->tagfq.tqh_first;
580 atfp != NULL; atfp = atfp->q.tqe_next) {
581 if (tagf_copy(sp, atfp, &tfp))
582 return (1);
583 TAILQ_INSERT_TAIL(&nexp->tagfq, tfp, q);
586 /* Copy the last tag. */
587 if (oexp->tag_last != NULL &&
588 (nexp->tag_last = v_wstrdup(sp, oexp->tag_last,
589 STRLEN(oexp->tag_last))) == NULL) {
590 msgq(sp, M_SYSERR, NULL);
591 return (1);
593 return (0);
597 * tagf_copy --
598 * Copy a TAGF structure and return it in new memory.
600 static int
601 tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp)
603 TAGF *tfp;
605 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
606 *tfp = *otfp;
608 /* XXX: Allocate as part of the TAGF structure!!! */
609 if ((tfp->name = strdup(otfp->name)) == NULL)
610 return (1);
612 *tfpp = tfp;
613 return (0);
617 * tagq_copy --
618 * Copy a TAGQ structure and return it in new memory.
620 static int
621 tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp)
623 TAGQ *tqp;
624 size_t len;
626 len = sizeof(TAGQ);
627 if (otqp->tag != NULL)
628 len += otqp->tlen + 1;
629 MALLOC_RET(sp, tqp, TAGQ *, len);
630 memcpy(tqp, otqp, len);
632 CIRCLEQ_INIT(&tqp->tagq);
633 tqp->current = NULL;
634 if (otqp->tag != NULL)
635 tqp->tag = tqp->buf;
637 *tqpp = tqp;
638 return (0);
642 * tag_copy --
643 * Copy a TAG structure and return it in new memory.
645 static int
646 tag_copy(SCR *sp, TAG *otp, TAG **tpp)
648 TAG *tp;
649 size_t len;
651 len = sizeof(TAG);
652 if (otp->fname != NULL)
653 len += otp->fnlen + 1;
654 if (otp->search != NULL)
655 len += otp->slen + 1;
656 if (otp->msg != NULL)
657 len += otp->mlen + 1;
658 MALLOC_RET(sp, tp, TAG *, len);
659 memcpy(tp, otp, len);
661 if (otp->fname != NULL)
662 tp->fname = (char *)tp->buf;
663 if (otp->search != NULL)
664 tp->search = tp->buf + (otp->search - otp->buf);
665 if (otp->msg != NULL)
666 tp->msg = tp->buf + (otp->msg - otp->buf);
668 *tpp = tp;
669 return (0);
673 * tagf_free --
674 * Free a TAGF structure.
676 static int
677 tagf_free(SCR *sp, TAGF *tfp)
679 EX_PRIVATE *exp;
681 exp = EXP(sp);
682 TAILQ_REMOVE(&exp->tagfq, tfp, q);
683 free(tfp->name);
684 free(tfp);
685 return (0);
689 * tagq_free --
690 * Free a TAGQ structure (and associated TAG structures).
692 * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
695 tagq_free(SCR *sp, TAGQ *tqp)
697 EX_PRIVATE *exp;
698 TAG *tp;
700 exp = EXP(sp);
701 while ((tp = tqp->tagq.cqh_first) != (void *)&tqp->tagq) {
702 CIRCLEQ_REMOVE(&tqp->tagq, tp, q);
703 free(tp);
706 * !!!
707 * If allocated and then the user failed to switch files, the TAGQ
708 * structure was never attached to any list.
710 if (tqp->q.cqe_next != NULL)
711 CIRCLEQ_REMOVE(&exp->tq, tqp, q);
712 free(tqp);
713 return (0);
717 * PUBLIC: int tagq_push __P((SCR*, TAGQ*, int, int ));
720 tagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force)
722 EX_PRIVATE *exp;
723 FREF *frp;
724 TAG *rtp;
725 TAGQ *rtqp;
726 db_recno_t lno;
727 size_t cno;
728 int istmp;
729 char *np;
730 size_t nlen;
732 exp = EXP(sp);
735 * Allocate all necessary memory before swapping screens. Initialize
736 * flags so we know what to free.
738 rtp = NULL;
739 rtqp = NULL;
740 if (exp->tq.cqh_first == (void *)&exp->tq) {
741 /* Initialize the `local context' tag queue structure. */
742 CALLOC_GOTO(sp, rtqp, TAGQ *, 1, sizeof(TAGQ));
743 CIRCLEQ_INIT(&rtqp->tagq);
745 /* Initialize and link in its tag structure. */
746 CALLOC_GOTO(sp, rtp, TAG *, 1, sizeof(TAG));
747 CIRCLEQ_INSERT_HEAD(&rtqp->tagq, rtp, q);
748 rtqp->current = rtp;
752 * Stick the current context information in a convenient place, we're
753 * about to lose it. Note, if we're called on editor startup, there
754 * will be no FREF structure.
756 frp = sp->frp;
757 lno = sp->lno;
758 cno = sp->cno;
759 istmp = frp == NULL ||
760 F_ISSET(frp, FR_TMPFILE) && !new_screen;
762 /* Try to switch to the tag. */
763 if (new_screen) {
764 if (ex_tag_Nswitch(sp, tqp->tagq.cqh_first, force))
765 goto err;
767 /* Everything else gets done in the new screen. */
768 sp = sp->nextdisp;
769 exp = EXP(sp);
770 } else
771 if (ex_tag_nswitch(sp, tqp->tagq.cqh_first, force))
772 goto err;
775 * If this is the first tag, put a `current location' queue entry
776 * in place, so we can pop all the way back to the current mark.
777 * Note, it doesn't point to much of anything, it's a placeholder.
779 if (exp->tq.cqh_first == (void *)&exp->tq) {
780 CIRCLEQ_INSERT_HEAD(&exp->tq, rtqp, q);
781 } else
782 rtqp = exp->tq.cqh_first;
784 /* Link the new TAGQ structure into place. */
785 CIRCLEQ_INSERT_HEAD(&exp->tq, tqp, q);
787 (void)ctag_search(sp,
788 tqp->current->search, tqp->current->slen, tqp->tag);
789 if (tqp->current->msg) {
790 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
791 np, nlen);
792 msgq(sp, M_INFO, np);
796 * Move the current context from the temporary save area into the
797 * right structure.
799 * If we were in a temporary file, we don't have a context to which
800 * we can return, so just make it be the same as what we're moving
801 * to. It will be a little odd that ^T doesn't change anything, but
802 * I don't think it's a big deal.
804 if (istmp) {
805 rtqp->current->frp = sp->frp;
806 rtqp->current->lno = sp->lno;
807 rtqp->current->cno = sp->cno;
808 } else {
809 rtqp->current->frp = frp;
810 rtqp->current->lno = lno;
811 rtqp->current->cno = cno;
813 return (0);
815 err:
816 alloc_err:
817 if (rtqp != NULL)
818 free(rtqp);
819 if (rtp != NULL)
820 free(rtp);
821 tagq_free(sp, tqp);
822 return (1);
826 * tag_msg
827 * A few common messages.
829 * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
831 void
832 tag_msg(SCR *sp, tagmsg_t msg, char *tag)
834 switch (msg) {
835 case TAG_BADLNO:
836 msgq_str(sp, M_ERR, tag,
837 "164|%s: the tag's line number is past the end of the file");
838 break;
839 case TAG_EMPTY:
840 msgq(sp, M_INFO, "165|The tags stack is empty");
841 break;
842 case TAG_SEARCH:
843 msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
844 break;
845 default:
846 abort();
851 * ex_tagf_alloc --
852 * Create a new list of ctag files.
854 * PUBLIC: int ex_tagf_alloc __P((SCR *, char *));
857 ex_tagf_alloc(SCR *sp, char *str)
859 EX_PRIVATE *exp;
860 TAGF *tfp;
861 size_t len;
862 char *p, *t;
864 /* Free current queue. */
865 exp = EXP(sp);
866 while ((tfp = exp->tagfq.tqh_first) != NULL)
867 tagf_free(sp, tfp);
869 /* Create new queue. */
870 for (p = t = str;; ++p) {
871 if (*p == '\0' || isblank(*p)) {
872 if ((len = p - t) > 1) {
873 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
874 MALLOC(sp, tfp->name, char *, len + 1);
875 if (tfp->name == NULL) {
876 free(tfp);
877 return (1);
879 memcpy(tfp->name, t, len);
880 tfp->name[len] = '\0';
881 tfp->flags = 0;
882 TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q);
884 t = p + 1;
886 if (*p == '\0')
887 break;
889 return (0);
891 /* Free previous queue. */
893 * ex_tag_free --
894 * Free the ex tag information.
896 * PUBLIC: int ex_tag_free __P((SCR *));
899 ex_tag_free(SCR *sp)
901 EX_PRIVATE *exp;
902 TAGF *tfp;
903 TAGQ *tqp;
905 /* Free up tag information. */
906 exp = EXP(sp);
907 while ((tqp = exp->tq.cqh_first) != (void *)&exp->tq)
908 tagq_free(sp, tqp);
909 while ((tfp = exp->tagfq.tqh_first) != NULL)
910 tagf_free(sp, tfp);
911 if (exp->tag_last != NULL)
912 free(exp->tag_last);
913 return (0);
917 * ctag_search --
918 * Search a file for a tag.
920 static int
921 ctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag)
923 MARK m;
924 char *p;
925 char *np;
926 size_t nlen;
929 * !!!
930 * The historic tags file format (from a long, long time ago...)
931 * used a line number, not a search string. I got complaints, so
932 * people are still using the format. POSIX 1003.2 permits it.
934 if (ISDIGIT(search[0])) {
935 INT2CHAR(sp, search, slen+1, np, nlen);
936 m.lno = atoi(np);
937 if (!db_exist(sp, m.lno)) {
938 tag_msg(sp, TAG_BADLNO, tag);
939 return (1);
941 } else {
943 * Search for the tag; cheap fallback for C functions
944 * if the name is the same but the arguments have changed.
946 m.lno = 1;
947 m.cno = 0;
948 if (f_search(sp, &m, &m,
949 search, slen, NULL,
950 SEARCH_FIRST | SEARCH_TAG | SEARCH_PARSE)) {
951 INT2CHAR(sp, search, slen, np, nlen);
952 if ((p = strrchr(np, '(')) != NULL) {
953 slen = p - np;
954 if (f_search(sp, &m, &m, search, slen,
955 NULL, SEARCH_FIRST | SEARCH_TAG))
956 goto notfound;
957 } else {
958 notfound: tag_msg(sp, TAG_SEARCH, tag);
959 return (1);
963 * !!!
964 * Historically, tags set the search direction if it wasn't
965 * already set.
967 if (sp->searchdir == NOTSET)
968 sp->searchdir = FORWARD;
972 * !!!
973 * Tags move to the first non-blank, NOT the search pattern start.
975 sp->lno = m.lno;
976 sp->cno = 0;
977 (void)nonblank(sp, sp->lno, &sp->cno);
978 return (0);
982 * ctag_slist --
983 * Search the list of tags files for a tag, and return tag queue.
985 static TAGQ *
986 ctag_slist(SCR *sp, CHAR_T *tag)
988 EX_PRIVATE *exp;
989 TAGF *tfp;
990 TAGQ *tqp;
991 size_t len;
992 int echk;
993 char *np;
994 size_t nlen;
996 exp = EXP(sp);
998 /* Allocate and initialize the tag queue structure. */
999 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
1000 len = nlen - 1;
1001 CALLOC_GOTO(sp, tqp, TAGQ *, 1, sizeof(TAGQ) + len + 1);
1002 CIRCLEQ_INIT(&tqp->tagq);
1003 tqp->tag = tqp->buf;
1004 memcpy(tqp->tag, np, (tqp->tlen = len) + 1);
1007 * Find the tag, only display missing file messages once, and
1008 * then only if we didn't find the tag.
1010 for (echk = 0,
1011 tfp = exp->tagfq.tqh_first; tfp != NULL; tfp = tfp->q.tqe_next)
1012 if (ctag_sfile(sp, tfp, tqp, tqp->tag)) {
1013 echk = 1;
1014 F_SET(tfp, TAGF_ERR);
1015 } else
1016 F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN);
1018 /* Check to see if we found anything. */
1019 if (tqp->tagq.cqh_first == (void *)&tqp->tagq) {
1020 msgq_str(sp, M_ERR, tqp->tag, "162|%s: tag not found");
1021 if (echk)
1022 for (tfp = exp->tagfq.tqh_first;
1023 tfp != NULL; tfp = tfp->q.tqe_next)
1024 if (F_ISSET(tfp, TAGF_ERR) &&
1025 !F_ISSET(tfp, TAGF_ERR_WARN)) {
1026 errno = tfp->errnum;
1027 msgq_str(sp, M_SYSERR, tfp->name, "%s");
1028 F_SET(tfp, TAGF_ERR_WARN);
1030 free(tqp);
1031 return (NULL);
1034 tqp->current = tqp->tagq.cqh_first;
1035 return (tqp);
1037 alloc_err:
1038 return (NULL);
1042 * ctag_sfile --
1043 * Search a tags file for a tag, adding any found to the tag queue.
1045 static int
1046 ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname)
1048 struct stat sb;
1049 TAG *tp;
1050 size_t dlen, nlen, slen;
1051 int fd, i, nf1, nf2;
1052 char *back, *cname, *dname, *front, *map, *name, *p, *search, *t;
1053 CHAR_T *wp;
1054 size_t wlen;
1056 if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
1057 tfp->errnum = errno;
1058 return (1);
1062 * XXX
1063 * Some old BSD systems require MAP_FILE as an argument when mapping
1064 * regular files.
1066 #ifndef MAP_FILE
1067 #define MAP_FILE 0
1068 #endif
1070 * XXX
1071 * We'd like to test if the file is too big to mmap. Since we don't
1072 * know what size or type off_t's or size_t's are, what the largest
1073 * unsigned integral type is, or what random insanity the local C
1074 * compiler will perpetrate, doing the comparison in a portable way
1075 * is flatly impossible. Hope mmap fails if the file is too large.
1077 if (fstat(fd, &sb) != 0 ||
1078 (map = mmap(NULL, (size_t)sb.st_size, PROT_READ | PROT_WRITE,
1079 MAP_FILE | MAP_PRIVATE, fd, (off_t)0)) == (caddr_t)-1) {
1080 tfp->errnum = errno;
1081 (void)close(fd);
1082 return (1);
1085 front = map;
1086 back = front + sb.st_size;
1087 front = binary_search(tname, front, back);
1088 front = linear_search(tname, front, back);
1089 if (front == NULL)
1090 goto done;
1093 * Initialize and link in the tag structure(s). The historic ctags
1094 * file format only permitted a single tag location per tag. The
1095 * obvious extension to permit multiple tags locations per tag is to
1096 * output multiple records in the standard format. Unfortunately,
1097 * this won't work correctly with historic ex/vi implementations,
1098 * because their binary search assumes that there's only one record
1099 * per tag, and so will use a random tag entry if there si more than
1100 * one. This code handles either format.
1102 * The tags file is in the following format:
1104 * <tag> <filename> <line number> | <pattern>
1106 * Figure out how long everything is so we can allocate in one swell
1107 * foop, but discard anything that looks wrong.
1109 for (;;) {
1110 /* Nul-terminate the end of the line. */
1111 for (p = front; p < back && *p != '\n'; ++p);
1112 if (p == back || *p != '\n')
1113 break;
1114 *p = '\0';
1116 /* Update the pointers for the next time. */
1117 t = p + 1;
1118 p = front;
1119 front = t;
1121 /* Break the line into tokens. */
1122 for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
1123 switch (i) {
1124 case 0: /* Tag. */
1125 cname = t;
1126 break;
1127 case 1: /* Filename. */
1128 name = t;
1129 nlen = strlen(name);
1130 break;
1133 /* Check for corruption. */
1134 if (i != 2 || p == NULL || t == NULL)
1135 goto corrupt;
1137 /* The rest of the string is the search pattern. */
1138 search = p;
1139 if ((slen = strlen(p)) == 0) {
1140 corrupt: p = msg_print(sp, tname, &nf1);
1141 t = msg_print(sp, tfp->name, &nf2);
1142 msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t);
1143 if (nf1)
1144 FREE_SPACE(sp, p, 0);
1145 if (nf2)
1146 FREE_SPACE(sp, t, 0);
1147 continue;
1150 /* Check for passing the last entry. */
1151 if (strcmp(tname, cname))
1152 break;
1154 /* Resolve the file name. */
1155 ctag_file(sp, tfp, name, &dname, &dlen);
1157 CALLOC_GOTO(sp, tp,
1158 TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 +
1159 (slen + 1) * sizeof(CHAR_T));
1160 tp->fname = (char *)tp->buf;
1161 if (dlen != 0) {
1162 memcpy(tp->fname, dname, dlen);
1163 tp->fname[dlen] = '/';
1164 ++dlen;
1166 memcpy(tp->fname + dlen, name, nlen + 1);
1167 tp->fnlen = dlen + nlen;
1168 tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1);
1169 CHAR2INT(sp, search, slen + 1, wp, wlen);
1170 MEMCPYW(tp->search, wp, (tp->slen = slen) + 1);
1171 CIRCLEQ_INSERT_TAIL(&tqp->tagq, tp, q);
1174 alloc_err:
1175 done: if (munmap(map, (size_t)sb.st_size))
1176 msgq(sp, M_SYSERR, "munmap");
1177 if (close(fd))
1178 msgq(sp, M_SYSERR, "close");
1179 return (0);
1183 * ctag_file --
1184 * Search for the right path to this file.
1186 static void
1187 ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp)
1189 struct stat sb;
1190 size_t len;
1191 char *p, buf[MAXPATHLEN];
1194 * !!!
1195 * If the tag file path is a relative path, see if it exists. If it
1196 * doesn't, look relative to the tags file path. It's okay for a tag
1197 * file to not exist, and historically, vi simply displayed a "new"
1198 * file. However, if the path exists relative to the tag file, it's
1199 * pretty clear what's happening, so we may as well get it right.
1201 *dlenp = 0;
1202 if (name[0] != '/' &&
1203 stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
1204 *p = '\0';
1205 len = snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name);
1206 if (stat(buf, &sb) == 0) {
1207 *dirp = tfp->name;
1208 *dlenp = strlen(*dirp);
1210 *p = '/';
1215 * Binary search for "string" in memory between "front" and "back".
1217 * This routine is expected to return a pointer to the start of a line at
1218 * *or before* the first word matching "string". Relaxing the constraint
1219 * this way simplifies the algorithm.
1221 * Invariants:
1222 * front points to the beginning of a line at or before the first
1223 * matching string.
1225 * back points to the beginning of a line at or after the first
1226 * matching line.
1228 * Base of the Invariants.
1229 * front = NULL;
1230 * back = EOF;
1232 * Advancing the Invariants:
1234 * p = first newline after halfway point from front to back.
1236 * If the string at "p" is not greater than the string to match,
1237 * p is the new front. Otherwise it is the new back.
1239 * Termination:
1241 * The definition of the routine allows it return at any point,
1242 * since front is always at or before the line to print.
1244 * In fact, it returns when the chosen "p" equals "back". This
1245 * implies that there exists a string is least half as long as
1246 * (back - front), which in turn implies that a linear search will
1247 * be no more expensive than the cost of simply printing a string or two.
1249 * Trying to continue with binary search at this point would be
1250 * more trouble than it's worth.
1252 #define EQUAL 0
1253 #define GREATER 1
1254 #define LESS (-1)
1256 #define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
1258 static char *
1259 binary_search(register char *string, register char *front, register char *back)
1261 register char *p;
1263 p = front + (back - front) / 2;
1264 SKIP_PAST_NEWLINE(p, back);
1266 while (p != back) {
1267 if (compare(string, p, back) == GREATER)
1268 front = p;
1269 else
1270 back = p;
1271 p = front + (back - front) / 2;
1272 SKIP_PAST_NEWLINE(p, back);
1274 return (front);
1278 * Find the first line that starts with string, linearly searching from front
1279 * to back.
1281 * Return NULL for no such line.
1283 * This routine assumes:
1285 * o front points at the first character in a line.
1286 * o front is before or at the first line to be printed.
1288 static char *
1289 linear_search(char *string, char *front, char *back)
1291 while (front < back) {
1292 switch (compare(string, front, back)) {
1293 case EQUAL: /* Found it. */
1294 return (front);
1295 case LESS: /* No such string. */
1296 return (NULL);
1297 case GREATER: /* Keep going. */
1298 break;
1300 SKIP_PAST_NEWLINE(front, back);
1302 return (NULL);
1306 * Return LESS, GREATER, or EQUAL depending on how the string1 compares
1307 * with string2 (s1 ??? s2).
1309 * o Matches up to len(s1) are EQUAL.
1310 * o Matches up to len(s2) are GREATER.
1312 * The string "s1" is null terminated. The string s2 is '\t', space, (or
1313 * "back") terminated.
1315 * !!!
1316 * Reasonably modern ctags programs use tabs as separators, not spaces.
1317 * However, historic programs did use spaces, and, I got complaints.
1319 static int
1320 compare(register char *s1, register char *s2, register char *back)
1322 for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
1323 if (*s1 != *s2)
1324 return (*s1 < *s2 ? LESS : GREATER);
1325 return (*s1 ? GREATER : s2 < back &&
1326 (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);