1 /* $NetBSD: ex_tag.c,v 1.6 2009/01/18 03:45:50 lukem Exp $ */
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.
9 * This code is derived from software contributed to Berkeley by
10 * David Hitz of Auspex Systems, Inc.
12 * See the LICENSE file for redistribution information.
18 static const char sccsid
[] = "Id: ex_tag.c,v 10.50 2004/03/16 14:09:11 skimo Exp (Berkeley) Date: 2004/03/16 14:09:11";
21 #include <sys/param.h>
22 #include <sys/types.h> /* XXX: param.h may not have included types.h */
24 #ifdef HAVE_SYS_MMAN_H
28 #include <sys/queue.h>
32 #include <bitstring.h>
43 #include "../common/common.h"
47 static char *binary_search
__P((char *, char *, char *));
48 static int compare
__P((char *, char *, char *));
49 static void ctag_file
__P((SCR
*, TAGF
*, char *, char **, size_t *));
50 static int ctag_search
__P((SCR
*, CHAR_T
*, size_t, char *));
52 static int getentry
__P((char *, char **, char **, char **));
53 static TAGQ
*gtag_slist
__P((SCR
*, CHAR_T
*, int));
55 static int ctag_sfile
__P((SCR
*, TAGF
*, TAGQ
*, char *));
56 static TAGQ
*ctag_slist
__P((SCR
*, CHAR_T
*));
57 static char *linear_search
__P((char *, char *, char *, unsigned long));
58 static int tag_copy
__P((SCR
*, TAG
*, TAG
**));
59 static int tag_pop
__P((SCR
*, TAGQ
*, int));
60 static int tagf_copy
__P((SCR
*, TAGF
*, TAGF
**));
61 static int tagf_free
__P((SCR
*, TAGF
*));
62 static int tagq_copy
__P((SCR
*, TAGQ
*, TAGQ
**));
66 * The tag code can be entered from main, e.g., "vi -t tag".
68 * PUBLIC: int ex_tag_first __P((SCR *, CHAR_T *));
71 ex_tag_first(SCR
*sp
, const CHAR_T
*tagarg
)
75 /* Build an argument for the ex :tag command. */
76 ex_cinit(sp
, &cmd
, C_TAG
, 0, OOBLNO
, OOBLNO
, 0);
77 argv_exp0(sp
, &cmd
, tagarg
, STRLEN(tagarg
));
81 * Historic vi went ahead and created a temporary file when it failed
82 * to find the tag. We match historic practice, but don't distinguish
83 * between real error and failure to find the tag.
85 if (ex_tag_push(sp
, &cmd
))
88 /* Display tags in the center of the screen. */
89 F_CLR(sp
, SC_SCR_TOP
);
90 F_SET(sp
, SC_SCR_CENTER
);
100 * Enter a new TAGQ context based on a ctag string.
102 * PUBLIC: int ex_rtag_push __P((SCR *, EXCMD *));
105 ex_rtag_push(SCR
*sp
, EXCMD
*cmdp
)
107 F_SET(cmdp
, E_REFERENCE
);
108 return ex_tag_push(sp
, cmdp
);
116 * Enter a new TAGQ context based on a ctag string.
118 * PUBLIC: int ex_tag_push __P((SCR *, EXCMD *));
121 ex_tag_push(SCR
*sp
, EXCMD
*cmdp
)
128 switch (cmdp
->argc
) {
130 if (exp
->tag_last
!= NULL
)
133 if ((exp
->tag_last
= v_wstrdup(sp
, cmdp
->argv
[0]->bp
,
134 cmdp
->argv
[0]->len
)) == NULL
) {
135 msgq(sp
, M_SYSERR
, NULL
);
139 /* Taglength may limit the number of characters. */
141 O_VAL(sp
, O_TAGLENGTH
)) != 0 && STRLEN(exp
->tag_last
) > tl
)
142 exp
->tag_last
[tl
] = '\0';
145 if (exp
->tag_last
== NULL
) {
146 msgq(sp
, M_ERR
, "158|No previous tag entered");
154 /* Get the tag information. */
156 if (O_ISSET(sp
, O_GTAGSMODE
)) {
157 if ((tqp
= gtag_slist(sp
, exp
->tag_last
,
158 F_ISSET(cmdp
, E_REFERENCE
))) == NULL
)
162 if ((tqp
= ctag_slist(sp
, exp
->tag_last
)) == NULL
)
165 if (tagq_push(sp
, tqp
, F_ISSET(cmdp
, E_NEWSCREEN
),
166 FL_ISSET(cmdp
->iflags
, E_C_FORCE
)))
174 * Switch context to the next TAG.
176 * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
179 ex_tag_next(SCR
*sp
, EXCMD
*cmdp
)
188 if ((tqp
= exp
->tq
.cqh_first
) == (void *)&exp
->tq
) {
189 tag_msg(sp
, TAG_EMPTY
, NULL
);
192 if ((tp
= tqp
->current
->q
.cqe_next
) == (void *)&tqp
->tagq
) {
193 msgq(sp
, M_ERR
, "282|Already at the last tag of this group");
196 if (ex_tag_nswitch(sp
, tp
, FL_ISSET(cmdp
->iflags
, E_C_FORCE
)))
200 if (F_ISSET(tqp
, TAG_CSCOPE
))
201 (void)cscope_search(sp
, tqp
, tp
);
203 (void)ctag_search(sp
, tp
->search
, tp
->slen
, tqp
->tag
);
204 if (tqp
->current
->msg
) {
205 INT2CHAR(sp
, tqp
->current
->msg
, tqp
->current
->mlen
+ 1,
207 msgq(sp
, M_INFO
, np
);
214 * Switch context to the next TAG.
216 * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
219 ex_tag_prev(SCR
*sp
, EXCMD
*cmdp
)
228 if ((tqp
= exp
->tq
.cqh_first
) == (void *)&exp
->tq
) {
229 tag_msg(sp
, TAG_EMPTY
, NULL
);
232 if ((tp
= tqp
->current
->q
.cqe_prev
) == (void *)&tqp
->tagq
) {
233 msgq(sp
, M_ERR
, "255|Already at the first tag of this group");
236 if (ex_tag_nswitch(sp
, tp
, FL_ISSET(cmdp
->iflags
, E_C_FORCE
)))
240 if (F_ISSET(tqp
, TAG_CSCOPE
))
241 (void)cscope_search(sp
, tqp
, tp
);
243 (void)ctag_search(sp
, tp
->search
, tp
->slen
, tqp
->tag
);
244 if (tqp
->current
->msg
) {
245 INT2CHAR(sp
, tqp
->current
->msg
, tqp
->current
->mlen
+ 1,
247 msgq(sp
, M_INFO
, "%s", np
);
254 * Switch context to the specified TAG.
256 * PUBLIC: int ex_tag_nswitch __P((SCR *, TAG *, int));
259 ex_tag_nswitch(SCR
*sp
, TAG
*tp
, int force
)
261 /* Get a file structure. */
262 if (tp
->frp
== NULL
&& (tp
->frp
= file_add(sp
, tp
->fname
)) == NULL
)
265 /* If not changing files, return, we're done. */
266 if (tp
->frp
== sp
->frp
)
269 /* Check for permission to leave. */
270 if (file_m1(sp
, force
, FS_ALL
| FS_POSSIBLE
))
273 /* Initialize the new file. */
274 if (file_init(sp
, tp
->frp
, NULL
, FS_SETALT
))
277 /* Display tags in the center of the screen. */
278 F_CLR(sp
, SC_SCR_TOP
);
279 F_SET(sp
, SC_SCR_CENTER
);
282 F_SET(sp
, SC_FSWITCH
);
288 * Switch context to the specified TAG in a new screen.
290 * PUBLIC: int ex_tag_Nswitch __P((SCR *, TAG *, int));
293 ex_tag_Nswitch(SCR
*sp
, TAG
*tp
, int force
)
297 /* Get a file structure. */
298 if (tp
->frp
== NULL
&& (tp
->frp
= file_add(sp
, tp
->fname
)) == NULL
)
301 /* Get a new screen. */
302 if (screen_init(sp
->gp
, sp
, &new))
304 if (vs_split(sp
, new, 0)) {
305 (void)file_end(new, new->ep
, 1);
306 (void)screen_end(new);
310 /* Get a backing file. */
311 if (tp
->frp
== sp
->frp
) {
312 /* Copy file state. */
315 CIRCLEQ_INSERT_HEAD(&new->ep
->scrq
, new, eq
);
318 new->frp
->flags
= sp
->frp
->flags
;
319 } else if (file_init(new, tp
->frp
, NULL
, force
)) {
320 (void)vs_discard(new, NULL
);
321 (void)screen_end(new);
325 /* Create the argument list. */
326 new->cargv
= new->argv
= ex_buildargv(sp
, NULL
, tp
->frp
->name
);
328 /* Display tags in the center of the screen. */
329 F_CLR(new, SC_SCR_TOP
);
330 F_SET(new, SC_SCR_CENTER
);
334 F_SET(sp
, SC_SSWITCH
);
341 * :tagp[op][!] [number | file]
343 * Pop to a previous TAGQ context.
345 * PUBLIC: int ex_tag_pop __P((SCR *, EXCMD *));
348 ex_tag_pop(SCR
*sp
, EXCMD
*cmdp
)
351 TAGQ
*tqp
, *dtqp
= NULL
;
358 /* Check for an empty stack. */
360 if (exp
->tq
.cqh_first
== (void *)&exp
->tq
) {
361 tag_msg(sp
, TAG_EMPTY
, NULL
);
365 /* Find the last TAG structure that we're going to DISCARD! */
366 switch (cmdp
->argc
) {
367 case 0: /* Pop one tag. */
368 dtqp
= exp
->tq
.cqh_first
;
370 case 1: /* Name or number. */
371 INT2CHAR(sp
, cmdp
->argv
[0]->bp
, cmdp
->argv
[0]->len
+1,
373 off
= strtol(arg
, &p
, 10);
377 /* Number: pop that many queue entries. */
380 for (tqp
= exp
->tq
.cqh_first
;
381 tqp
!= (void *)&exp
->tq
&& --off
> 1;
382 tqp
= tqp
->q
.cqe_next
);
383 if (tqp
== (void *)&exp
->tq
) {
385 "159|Less than %s entries on the tags stack; use :display t[ags]",
392 /* File argument: pop to that queue entry. */
393 filearg
: arglen
= strlen(arg
);
394 for (tqp
= exp
->tq
.cqh_first
;
395 tqp
!= (void *)&exp
->tq
;
396 dtqp
= tqp
, tqp
= tqp
->q
.cqe_next
) {
397 /* Don't pop to the current file. */
398 if (tqp
== exp
->tq
.cqh_first
)
400 p
= tqp
->current
->frp
->name
;
401 if ((t
= strrchr(p
, '/')) == NULL
)
405 if (!strncmp(arg
, t
, arglen
))
408 if (tqp
== (void *)&exp
->tq
) {
409 msgq_str(sp
, M_ERR
, arg
,
410 "160|No file %s on the tags stack to return to; use :display t[ags]");
413 if (tqp
== exp
->tq
.cqh_first
)
420 return (tag_pop(sp
, dtqp
, FL_ISSET(cmdp
->iflags
, E_C_FORCE
)));
424 * ex_tag_top -- :tagt[op][!]
425 * Clear the tag stack.
427 * PUBLIC: int ex_tag_top __P((SCR *, EXCMD *));
430 ex_tag_top(SCR
*sp
, EXCMD
*cmdp
)
436 /* Check for an empty stack. */
437 if (exp
->tq
.cqh_first
== (void *)&exp
->tq
) {
438 tag_msg(sp
, TAG_EMPTY
, NULL
);
442 /* Return to the oldest information. */
444 exp
->tq
.cqh_last
->q
.cqe_prev
, FL_ISSET(cmdp
->iflags
, E_C_FORCE
)));
449 * Pop up to and including the specified TAGQ context.
452 tag_pop(SCR
*sp
, TAGQ
*dtqp
, int force
)
461 * Update the cursor from the saved TAG information of the TAG
462 * structure we're moving to.
464 tp
= dtqp
->q
.cqe_next
->current
;
465 if (tp
->frp
== sp
->frp
) {
469 if (file_m1(sp
, force
, FS_ALL
| FS_POSSIBLE
))
472 tp
->frp
->lno
= tp
->lno
;
473 tp
->frp
->cno
= tp
->cno
;
474 F_SET(sp
->frp
, FR_CURSORSET
);
475 if (file_init(sp
, tp
->frp
, NULL
, FS_SETALT
))
478 F_SET(sp
, SC_FSWITCH
);
481 /* Pop entries off the queue up to and including dtqp. */
483 tqp
= exp
->tq
.cqh_first
;
484 if (tagq_free(sp
, tqp
))
486 } while (tqp
!= dtqp
);
489 * If only a single tag left, we've returned to the first tag point,
490 * and the stack is now empty.
492 if (exp
->tq
.cqh_first
->q
.cqe_next
== (void *)&exp
->tq
)
493 tagq_free(sp
, exp
->tq
.cqh_first
);
500 * Display the list of tags.
502 * PUBLIC: int ex_tag_display __P((SCR *));
505 ex_tag_display(SCR
*sp
)
515 if ((tqp
= exp
->tq
.cqh_first
) == (void *)&exp
->tq
) {
516 tag_msg(sp
, TAG_EMPTY
, NULL
);
521 * We give the file name 20 columns and the search string the rest.
522 * If there's not enough room, we don't do anything special, it's
523 * not worth the effort, it just makes the display more confusing.
525 * We also assume that characters in file names map 1-1 to printing
526 * characters. This might not be true, but I don't think it's worth
527 * fixing. (The obvious fix is to pass the filenames through the
528 * msg_print function.)
530 #define L_NAME 30 /* Name. */
531 #define L_SLOP 4 /* Leading number plus trailing *. */
532 #define L_SPACE 5 /* Spaces after name, before tag. */
533 #define L_TAG 20 /* Tag. */
534 if (sp
->cols
<= L_NAME
+ L_SLOP
) {
535 msgq(sp
, M_ERR
, "292|Display too small.");
540 * Display the list of tags for each queue entry. The first entry
541 * is numbered, and the current tag entry has an asterisk appended.
543 for (cnt
= 1, tqp
= exp
->tq
.cqh_first
; !INTERRUPTED(sp
) &&
544 tqp
!= (void *)&exp
->tq
; ++cnt
, tqp
= tqp
->q
.cqe_next
)
545 for (tp
= tqp
->tagq
.cqh_first
;
546 tp
!= (void *)&tqp
->tagq
; tp
= tp
->q
.cqe_next
) {
547 if (tp
== tqp
->tagq
.cqh_first
)
548 (void)ex_printf(sp
, "%2d ", cnt
);
550 (void)ex_printf(sp
, " ");
551 p
= tp
->frp
== NULL
? tp
->fname
: tp
->frp
->name
;
552 if ((len
= strlen(p
)) > L_NAME
) {
553 len
= len
- (L_NAME
- 4);
554 (void)ex_printf(sp
, " ... %*.*s",
555 L_NAME
- 4, L_NAME
- 4, p
+ len
);
558 " %*.*s", L_NAME
, L_NAME
, p
);
559 if (tqp
->current
== tp
)
560 (void)ex_printf(sp
, "*");
562 if (tp
== tqp
->tagq
.cqh_first
&& tqp
->tag
!= NULL
&&
563 (sp
->cols
- L_NAME
) >= L_TAG
+ L_SPACE
) {
564 len
= strlen(tqp
->tag
);
565 if (len
> sp
->cols
- (L_NAME
+ L_SPACE
))
566 len
= sp
->cols
- (L_NAME
+ L_SPACE
);
567 (void)ex_printf(sp
, "%s%.*s",
568 tqp
->current
== tp
? " " : " ",
571 (void)ex_printf(sp
, "\n");
578 * Copy a screen's tag structures.
580 * PUBLIC: int ex_tag_copy __P((SCR *, SCR *));
583 ex_tag_copy(SCR
*orig
, SCR
*sp
)
585 EX_PRIVATE
*oexp
, *nexp
;
593 /* Copy tag queue and tags stack. */
594 for (aqp
= oexp
->tq
.cqh_first
;
595 aqp
!= (void *)&oexp
->tq
; aqp
= aqp
->q
.cqe_next
) {
596 if (tagq_copy(sp
, aqp
, &tqp
))
598 for (ap
= aqp
->tagq
.cqh_first
;
599 ap
!= (void *)&aqp
->tagq
; ap
= ap
->q
.cqe_next
) {
600 if (tag_copy(sp
, ap
, &tp
))
602 /* Set the current pointer. */
603 if (aqp
->current
== ap
)
605 CIRCLEQ_INSERT_TAIL(&tqp
->tagq
, tp
, q
);
607 CIRCLEQ_INSERT_TAIL(&nexp
->tq
, tqp
, q
);
610 /* Copy list of tag files. */
611 for (atfp
= oexp
->tagfq
.tqh_first
;
612 atfp
!= NULL
; atfp
= atfp
->q
.tqe_next
) {
613 if (tagf_copy(sp
, atfp
, &tfp
))
615 TAILQ_INSERT_TAIL(&nexp
->tagfq
, tfp
, q
);
618 /* Copy the last tag. */
619 if (oexp
->tag_last
!= NULL
&&
620 (nexp
->tag_last
= v_wstrdup(sp
, oexp
->tag_last
,
621 STRLEN(oexp
->tag_last
))) == NULL
) {
622 msgq(sp
, M_SYSERR
, NULL
);
630 * Copy a TAGF structure and return it in new memory.
633 tagf_copy(SCR
*sp
, TAGF
*otfp
, TAGF
**tfpp
)
637 MALLOC_RET(sp
, tfp
, TAGF
*, sizeof(TAGF
));
640 /* XXX: Allocate as part of the TAGF structure!!! */
641 if ((tfp
->name
= strdup(otfp
->name
)) == NULL
)
650 * Copy a TAGQ structure and return it in new memory.
653 tagq_copy(SCR
*sp
, TAGQ
*otqp
, TAGQ
**tqpp
)
659 if (otqp
->tag
!= NULL
)
660 len
+= otqp
->tlen
+ 1;
661 MALLOC_RET(sp
, tqp
, TAGQ
*, len
);
662 memcpy(tqp
, otqp
, len
);
664 CIRCLEQ_INIT(&tqp
->tagq
);
666 if (otqp
->tag
!= NULL
)
675 * Copy a TAG structure and return it in new memory.
678 tag_copy(SCR
*sp
, TAG
*otp
, TAG
**tpp
)
684 if (otp
->fname
!= NULL
)
685 len
+= otp
->fnlen
+ 1;
686 if (otp
->search
!= NULL
)
687 len
+= otp
->slen
+ 1;
688 if (otp
->msg
!= NULL
)
689 len
+= otp
->mlen
+ 1;
690 MALLOC_RET(sp
, tp
, TAG
*, len
);
691 memcpy(tp
, otp
, len
);
693 if (otp
->fname
!= NULL
)
694 tp
->fname
= (char *)tp
->buf
;
695 if (otp
->search
!= NULL
)
696 tp
->search
= tp
->buf
+ (otp
->search
- otp
->buf
);
697 if (otp
->msg
!= NULL
)
698 tp
->msg
= tp
->buf
+ (otp
->msg
- otp
->buf
);
706 * Free a TAGF structure.
709 tagf_free(SCR
*sp
, TAGF
*tfp
)
714 TAILQ_REMOVE(&exp
->tagfq
, tfp
, q
);
722 * Free a TAGQ structure (and associated TAG structures).
724 * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
727 tagq_free(SCR
*sp
, TAGQ
*tqp
)
733 while ((tp
= tqp
->tagq
.cqh_first
) != (void *)&tqp
->tagq
) {
734 CIRCLEQ_REMOVE(&tqp
->tagq
, tp
, q
);
739 * If allocated and then the user failed to switch files, the TAGQ
740 * structure was never attached to any list.
742 if (tqp
->q
.cqe_next
!= NULL
)
743 CIRCLEQ_REMOVE(&exp
->tq
, tqp
, q
);
749 * PUBLIC: int tagq_push __P((SCR*, TAGQ*, int, int ));
752 tagq_push(SCR
*sp
, TAGQ
*tqp
, int new_screen
, int force
)
767 * Allocate all necessary memory before swapping screens. Initialize
768 * flags so we know what to free.
772 if (exp
->tq
.cqh_first
== (void *)&exp
->tq
) {
773 /* Initialize the `local context' tag queue structure. */
774 CALLOC_GOTO(sp
, rtqp
, TAGQ
*, 1, sizeof(TAGQ
));
775 CIRCLEQ_INIT(&rtqp
->tagq
);
777 /* Initialize and link in its tag structure. */
778 CALLOC_GOTO(sp
, rtp
, TAG
*, 1, sizeof(TAG
));
779 CIRCLEQ_INSERT_HEAD(&rtqp
->tagq
, rtp
, q
);
784 * Stick the current context information in a convenient place, we're
785 * about to lose it. Note, if we're called on editor startup, there
786 * will be no FREF structure.
791 istmp
= frp
== NULL
|| (F_ISSET(frp
, FR_TMPFILE
) && !new_screen
);
793 /* Try to switch to the tag. */
795 if (ex_tag_Nswitch(sp
, tqp
->tagq
.cqh_first
, force
))
798 /* Everything else gets done in the new screen. */
802 if (ex_tag_nswitch(sp
, tqp
->tagq
.cqh_first
, force
))
806 * If this is the first tag, put a `current location' queue entry
807 * in place, so we can pop all the way back to the current mark.
808 * Note, it doesn't point to much of anything, it's a placeholder.
810 if (exp
->tq
.cqh_first
== (void *)&exp
->tq
) {
811 CIRCLEQ_INSERT_HEAD(&exp
->tq
, rtqp
, q
);
813 rtqp
= exp
->tq
.cqh_first
;
815 /* Link the new TAGQ structure into place. */
816 CIRCLEQ_INSERT_HEAD(&exp
->tq
, tqp
, q
);
818 (void)ctag_search(sp
,
819 tqp
->current
->search
, tqp
->current
->slen
, tqp
->tag
);
820 if (tqp
->current
->msg
) {
821 INT2CHAR(sp
, tqp
->current
->msg
, tqp
->current
->mlen
+ 1,
823 msgq(sp
, M_INFO
, "%s", np
);
827 * Move the current context from the temporary save area into the
830 * If we were in a temporary file, we don't have a context to which
831 * we can return, so just make it be the same as what we're moving
832 * to. It will be a little odd that ^T doesn't change anything, but
833 * I don't think it's a big deal.
836 rtqp
->current
->frp
= sp
->frp
;
837 rtqp
->current
->lno
= sp
->lno
;
838 rtqp
->current
->cno
= sp
->cno
;
840 rtqp
->current
->frp
= frp
;
841 rtqp
->current
->lno
= lno
;
842 rtqp
->current
->cno
= cno
;
858 * A few common messages.
860 * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
863 tag_msg(SCR
*sp
, tagmsg_t msg
, char *tag
)
867 msgq_str(sp
, M_ERR
, tag
,
868 "164|%s: the tag's line number is past the end of the file");
871 msgq(sp
, M_INFO
, "165|The tags stack is empty");
874 msgq_str(sp
, M_ERR
, tag
, "166|%s: search pattern not found");
883 * Create a new list of ctag files.
885 * PUBLIC: int ex_tagf_alloc __P((SCR *, const char *));
888 ex_tagf_alloc(SCR
*sp
, const char *str
)
895 /* Free current queue. */
897 while ((tfp
= exp
->tagfq
.tqh_first
) != NULL
)
900 /* Create new queue. */
901 for (p
= t
= str
;; ++p
) {
902 if (*p
== '\0' || isblank(*p
)) {
903 if ((len
= p
- t
) > 1) {
904 MALLOC_RET(sp
, tfp
, TAGF
*, sizeof(TAGF
));
905 MALLOC(sp
, tfp
->name
, char *, len
+ 1);
906 if (tfp
->name
== NULL
) {
910 memcpy(tfp
->name
, t
, len
);
911 tfp
->name
[len
] = '\0';
913 TAILQ_INSERT_TAIL(&exp
->tagfq
, tfp
, q
);
922 /* Free previous queue. */
925 * Free the ex tag information.
927 * PUBLIC: int ex_tag_free __P((SCR *));
936 /* Free up tag information. */
938 while ((tqp
= exp
->tq
.cqh_first
) != (void *)&exp
->tq
)
940 while ((tfp
= exp
->tagfq
.tqh_first
) != NULL
)
942 if (exp
->tag_last
!= NULL
)
949 * Search a file for a tag.
952 ctag_search(SCR
*sp
, CHAR_T
*search
, size_t slen
, char *tag
)
961 * The historic tags file format (from a long, long time ago...)
962 * used a line number, not a search string. I got complaints, so
963 * people are still using the format. POSIX 1003.2 permits it.
965 if (ISDIGIT(search
[0])) {
966 INT2CHAR(sp
, search
, slen
+1, np
, nlen
);
968 if (!db_exist(sp
, m
.lno
)) {
969 tag_msg(sp
, TAG_BADLNO
, tag
);
974 * Search for the tag; cheap fallback for C functions
975 * if the name is the same but the arguments have changed.
979 if (f_search(sp
, &m
, &m
,
981 SEARCH_FIRST
| SEARCH_TAG
| SEARCH_PARSE
)) {
982 INT2CHAR(sp
, search
, slen
, np
, nlen
);
983 if ((p
= strrchr(np
, '(')) != NULL
) {
985 if (f_search(sp
, &m
, &m
, search
, slen
,
986 NULL
, SEARCH_FIRST
| SEARCH_TAG
))
989 notfound
: tag_msg(sp
, TAG_SEARCH
, tag
);
995 * Historically, tags set the search direction if it wasn't
998 if (sp
->searchdir
== NOTSET
)
999 sp
->searchdir
= FORWARD
;
1004 * Tags move to the first non-blank, NOT the search pattern start.
1008 (void)nonblank(sp
, sp
->lno
, &sp
->cno
);
1015 * get tag information from current line.
1017 * gtags temporary file format.
1018 * <tag> <lineno> <file> <image>
1021 * +------------------------------------------------
1022 * |main 30 main.c main(argc, argv)
1023 * |func 21 subr.c func(arg)
1026 getentry(char *buf
, char **tag
, char **file
, char **line
)
1030 for (*tag
= p
; *p
&& !isspace((unsigned char)*p
); p
++) /* tag name */
1035 for (; *p
&& isspace((unsigned char)*p
); p
++) /* (skip blanks) */
1039 *line
= p
; /* line no */
1040 for (*line
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
1045 for (; *p
&& isspace((unsigned char)*p
); p
++) /* (skip blanks) */
1049 *file
= p
; /* file name */
1050 for (*file
= p
; *p
&& !isspace((unsigned char)*p
); p
++)
1057 if (strlen(*tag
) && strlen(*line
) && strlen(*file
) && atoi(*line
) > 0)
1060 return 0; /* ERROR */
1065 * Search the list of tags files for a tag, and return tag queue.
1068 gtag_slist(SCR
*sp
, CHAR_T
*tag
, int ref
)
1071 size_t len
, nlen
, slen
, wlen
;
1075 char *name
, *file
, *search
;
1076 char command
[BUFSIZ
];
1081 /* Allocate and initialize the tag queue structure. */
1082 INT2CHAR(sp
, tag
, STRLEN(tag
) + 1, np
, nlen
);
1084 CALLOC_GOTO(sp
, tqp
, TAGQ
*, 1, sizeof(TAGQ
) + len
+ 1);
1085 CIRCLEQ_INIT(&tqp
->tagq
);
1086 tqp
->tag
= tqp
->buf
;
1087 memcpy(tqp
->tag
, np
, (tqp
->tlen
= len
) + 1);
1090 * Find the tag, only display missing file messages once, and
1091 * then only if we didn't find the tag.
1093 snprintf(command
, sizeof(command
), "global -%s '%s'", ref
? "rx" : "x",
1095 if ((fp
= popen(command
, "r")) != NULL
) {
1096 while (fgets(buf
, sizeof(buf
), fp
)) {
1097 if (buf
[strlen(buf
)-1] == '\n') /* chop(buf) */
1098 buf
[strlen(buf
)-1] = 0;
1100 while (fgetc(fp
) != '\n')
1102 if (getentry(buf
, &name
, &file
, &search
) == 0) {
1106 slen
= strlen(search
);
1108 TAG
*, 1, sizeof(TAG
) + strlen(file
) + 1 +
1109 (slen
+ 1) * sizeof(CHAR_T
));
1110 tp
->fname
= (char *)tp
->buf
;
1111 strcpy(tp
->fname
, file
);
1112 tp
->fnlen
= strlen(file
);
1113 tp
->search
= (CHAR_T
*)(tp
->fname
+ tp
->fnlen
+ 1);
1114 CHAR2INT(sp
, search
, slen
+ 1, wp
, wlen
);
1115 MEMCPYW(tp
->search
, wp
, (tp
->slen
= slen
) + 1);
1116 CIRCLEQ_INSERT_TAIL(&tqp
->tagq
, tp
, q
);
1121 /* Check to see if we found anything. */
1122 if (tqp
->tagq
.cqh_first
== (void *)&tqp
->tagq
) {
1123 msgq_str(sp
, M_ERR
, np
, "162|%s: tag not found");
1128 tqp
->current
= tqp
->tagq
.cqh_first
;
1138 * Search the list of tags files for a tag, and return tag queue.
1141 ctag_slist(SCR
*sp
, CHAR_T
*tag
)
1153 /* Allocate and initialize the tag queue structure. */
1154 INT2CHAR(sp
, tag
, STRLEN(tag
) + 1, np
, nlen
);
1156 CALLOC_GOTO(sp
, tqp
, TAGQ
*, 1, sizeof(TAGQ
) + len
+ 1);
1157 CIRCLEQ_INIT(&tqp
->tagq
);
1158 tqp
->tag
= tqp
->buf
;
1159 memcpy(tqp
->tag
, np
, (tqp
->tlen
= len
) + 1);
1162 * Find the tag, only display missing file messages once, and
1163 * then only if we didn't find the tag.
1166 tfp
= exp
->tagfq
.tqh_first
; tfp
!= NULL
; tfp
= tfp
->q
.tqe_next
)
1167 if (ctag_sfile(sp
, tfp
, tqp
, tqp
->tag
)) {
1169 F_SET(tfp
, TAGF_ERR
);
1171 F_CLR(tfp
, TAGF_ERR
| TAGF_ERR_WARN
);
1173 /* Check to see if we found anything. */
1174 if (tqp
->tagq
.cqh_first
== (void *)&tqp
->tagq
) {
1175 msgq_str(sp
, M_ERR
, tqp
->tag
, "162|%s: tag not found");
1177 for (tfp
= exp
->tagfq
.tqh_first
;
1178 tfp
!= NULL
; tfp
= tfp
->q
.tqe_next
)
1179 if (F_ISSET(tfp
, TAGF_ERR
) &&
1180 !F_ISSET(tfp
, TAGF_ERR_WARN
)) {
1181 errno
= tfp
->errnum
;
1182 msgq_str(sp
, M_SYSERR
, tfp
->name
, "%s");
1183 F_SET(tfp
, TAGF_ERR_WARN
);
1189 tqp
->current
= tqp
->tagq
.cqh_first
;
1198 * Search a tags file for a tag, adding any found to the tag queue.
1201 ctag_sfile(SCR
*sp
, TAGF
*tfp
, TAGQ
*tqp
, char *tname
)
1205 size_t dlen
, nlen
= 0, slen
;
1206 int fd
, i
, nf1
, nf2
;
1207 char *back
, *front
, *map
, *p
, *search
, *t
;
1208 char *cname
= NULL
, *dname
= NULL
, *name
= NULL
;
1213 if ((fd
= open(tfp
->name
, O_RDONLY
, 0)) < 0) {
1214 tfp
->errnum
= errno
;
1220 * Some old BSD systems require MAP_FILE as an argument when mapping
1228 * We'd like to test if the file is too big to mmap. Since we don't
1229 * know what size or type off_t's or size_t's are, what the largest
1230 * unsigned integral type is, or what random insanity the local C
1231 * compiler will perpetrate, doing the comparison in a portable way
1232 * is flatly impossible. Hope mmap fails if the file is too large.
1234 if (fstat(fd
, &sb
) != 0 ||
1235 (map
= mmap(NULL
, (size_t)sb
.st_size
, PROT_READ
| PROT_WRITE
,
1236 MAP_FILE
| MAP_PRIVATE
, fd
, (off_t
)0)) == (caddr_t
)-1) {
1237 tfp
->errnum
= errno
;
1242 tl
= O_VAL(sp
, O_TAGLENGTH
);
1244 back
= front
+ sb
.st_size
;
1245 front
= binary_search(tname
, front
, back
);
1246 front
= linear_search(tname
, front
, back
, tl
);
1251 * Initialize and link in the tag structure(s). The historic ctags
1252 * file format only permitted a single tag location per tag. The
1253 * obvious extension to permit multiple tags locations per tag is to
1254 * output multiple records in the standard format. Unfortunately,
1255 * this won't work correctly with historic ex/vi implementations,
1256 * because their binary search assumes that there's only one record
1257 * per tag, and so will use a random tag entry if there si more than
1258 * one. This code handles either format.
1260 * The tags file is in the following format:
1262 * <tag> <filename> <line number> | <pattern>
1264 * Figure out how long everything is so we can allocate in one swell
1265 * foop, but discard anything that looks wrong.
1268 /* Nul-terminate the end of the line. */
1269 for (p
= front
; p
< back
&& *p
!= '\n'; ++p
);
1270 if (p
== back
|| *p
!= '\n')
1274 /* Update the pointers for the next time. */
1279 /* Break the line into tokens. */
1280 for (i
= 0; i
< 2 && (t
= strsep(&p
, "\t ")) != NULL
; ++i
)
1285 case 1: /* Filename. */
1287 nlen
= strlen(name
);
1291 /* Check for corruption. */
1292 if (i
!= 2 || p
== NULL
|| t
== NULL
)
1295 /* The rest of the string is the search pattern. */
1297 if ((slen
= strlen(p
)) == 0) {
1298 corrupt
: p
= msg_print(sp
, tname
, &nf1
);
1299 t
= msg_print(sp
, tfp
->name
, &nf2
);
1300 msgq(sp
, M_ERR
, "163|%s: corrupted tag in %s", p
, t
);
1302 FREE_SPACE(sp
, p
, 0);
1304 FREE_SPACE(sp
, t
, 0);
1308 /* Check for passing the last entry. */
1309 if (tl
? strncmp(tname
, cname
, tl
) : strcmp(tname
, cname
))
1312 /* Resolve the file name. */
1313 ctag_file(sp
, tfp
, name
, &dname
, &dlen
);
1316 TAG
*, 1, sizeof(TAG
) + dlen
+ 2 + nlen
+ 1 +
1317 (slen
+ 1) * sizeof(CHAR_T
));
1318 tp
->fname
= (char *)tp
->buf
;
1320 memcpy(tp
->fname
, dname
, dlen
);
1321 tp
->fname
[dlen
] = '/';
1324 memcpy(tp
->fname
+ dlen
, name
, nlen
+ 1);
1325 tp
->fnlen
= dlen
+ nlen
;
1326 tp
->search
= (CHAR_T
*)(tp
->fname
+ tp
->fnlen
+ 1);
1327 CHAR2INT(sp
, search
, slen
+ 1, wp
, wlen
);
1328 MEMCPYW(tp
->search
, wp
, (tp
->slen
= slen
) + 1);
1329 CIRCLEQ_INSERT_TAIL(&tqp
->tagq
, tp
, q
);
1333 done
: if (munmap(map
, (size_t)sb
.st_size
))
1334 msgq(sp
, M_SYSERR
, "munmap");
1336 msgq(sp
, M_SYSERR
, "close");
1342 * Search for the right path to this file.
1345 ctag_file(SCR
*sp
, TAGF
*tfp
, char *name
, char **dirp
, size_t *dlenp
)
1349 char *p
, buf
[MAXPATHLEN
];
1353 * If the tag file path is a relative path, see if it exists. If it
1354 * doesn't, look relative to the tags file path. It's okay for a tag
1355 * file to not exist, and historically, vi simply displayed a "new"
1356 * file. However, if the path exists relative to the tag file, it's
1357 * pretty clear what's happening, so we may as well get it right.
1360 if (name
[0] != '/' &&
1361 stat(name
, &sb
) && (p
= strrchr(tfp
->name
, '/')) != NULL
) {
1363 len
= snprintf(buf
, sizeof(buf
), "%s/%s", tfp
->name
, name
);
1364 if (stat(buf
, &sb
) == 0) {
1366 *dlenp
= strlen(*dirp
);
1373 * Binary search for "string" in memory between "front" and "back".
1375 * This routine is expected to return a pointer to the start of a line at
1376 * *or before* the first word matching "string". Relaxing the constraint
1377 * this way simplifies the algorithm.
1380 * front points to the beginning of a line at or before the first
1383 * back points to the beginning of a line at or after the first
1386 * Base of the Invariants.
1390 * Advancing the Invariants:
1392 * p = first newline after halfway point from front to back.
1394 * If the string at "p" is not greater than the string to match,
1395 * p is the new front. Otherwise it is the new back.
1399 * The definition of the routine allows it return at any point,
1400 * since front is always at or before the line to print.
1402 * In fact, it returns when the chosen "p" equals "back". This
1403 * implies that there exists a string is least half as long as
1404 * (back - front), which in turn implies that a linear search will
1405 * be no more expensive than the cost of simply printing a string or two.
1407 * Trying to continue with binary search at this point would be
1408 * more trouble than it's worth.
1414 #define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
1417 binary_search(register char *string
, register char *front
, register char *back
)
1421 p
= front
+ (back
- front
) / 2;
1422 SKIP_PAST_NEWLINE(p
, back
);
1425 if (compare(string
, p
, back
) == GREATER
)
1429 p
= front
+ (back
- front
) / 2;
1430 SKIP_PAST_NEWLINE(p
, back
);
1436 * Find the first line that starts with string, linearly searching from front
1439 * Return NULL for no such line.
1441 * This routine assumes:
1443 * o front points at the first character in a line.
1444 * o front is before or at the first line to be printed.
1447 linear_search(char *string
, char *front
, char *back
, unsigned long tl
)
1450 while (front
< back
) {
1451 end
= tl
&& (unsigned long)(back
-front
) > tl
? front
+tl
: back
;
1452 switch (compare(string
, front
, end
)) {
1453 case EQUAL
: /* Found it. */
1455 case LESS
: /* No such string. */
1457 case GREATER
: /* Keep going. */
1460 SKIP_PAST_NEWLINE(front
, back
);
1466 * Return LESS, GREATER, or EQUAL depending on how the string1 compares
1467 * with string2 (s1 ??? s2).
1469 * o Matches up to len(s1) are EQUAL.
1470 * o Matches up to len(s2) are GREATER.
1472 * The string "s1" is null terminated. The string s2 is '\t', space, (or
1473 * "back") terminated.
1476 * Reasonably modern ctags programs use tabs as separators, not spaces.
1477 * However, historic programs did use spaces, and, I got complaints.
1480 compare(register char *s1
, register char *s2
, register char *back
)
1482 for (; *s1
&& s2
< back
&& (*s2
!= '\t' && *s2
!= ' '); ++s1
, ++s2
)
1484 return (*s1
< *s2
? LESS
: GREATER
);
1485 return (*s1
? GREATER
: s2
< back
&&
1486 (*s2
!= '\t' && *s2
!= ' ') ? LESS
: EQUAL
);