2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * David Hitz of Auspex Systems, Inc.
8 * %sccs.include.redist.c%
12 static char sccsid
[] = "$Id: ex_tag.c,v 8.26 1993/11/23 12:51:26 bostic Exp $ (Berkeley) $Date: 1993/11/23 12:51:26 $";
15 #include <sys/types.h>
31 static char *binary_search
__P((char *, char *, char *));
32 static int compare
__P((char *, char *, char *));
33 static char *linear_search
__P((char *, char *, char *));
34 static int search
__P((SCR
*, char *, char *, char **));
35 static int tag_get
__P((SCR
*, char *, char **, char **, char **));
39 * The tag code can be entered from main, i.e. "vi -t tag".
42 ex_tagfirst(sp
, tagarg
)
51 char *p
, *tag
, *name
, *search
;
53 /* Taglength may limit the number of characters. */
54 if ((tl
= O_VAL(sp
, O_TAGLENGTH
)) != 0 && strlen(tagarg
) > tl
)
57 /* Get the tag information. */
58 if (tag_get(sp
, tagarg
, &tag
, &name
, &search
))
61 /* Create the file entry. */
62 if ((frp
= file_add(sp
, NULL
, name
, 0)) == NULL
)
64 if (file_init(sp
, frp
, NULL
, 0))
69 * Historic vi accepted a line number as well as a search
70 * string, and people are apparently still using the format.
72 if (isdigit(search
[0])) {
77 * Search for the tag; cheap fallback for C functions if
78 * the name is the same but the arguments have changed.
82 flags
= SEARCH_FILE
| SEARCH_TAG
| SEARCH_TERM
;
83 sval
= f_search(sp
, sp
->ep
, &m
, &m
, search
, NULL
, &flags
);
84 if (sval
&& (p
= strrchr(search
, '(')) != NULL
) {
86 sval
= f_search(sp
, sp
->ep
,
87 &m
, &m
, search
, NULL
, &flags
);
90 msgq(sp
, M_ERR
, "%s: search pattern not found.", tag
);
93 /* Set up the screen. */
96 F_SET(frp
, FR_CURSORSET
);
98 /* Might as well make this the default tag. */
99 if ((EXP(sp
)->tlast
= strdup(tagarg
)) == NULL
) {
100 msgq(sp
, M_SYSERR
, NULL
);
107 * ex_tagpush -- :tag [file]
111 ex_tagpush(sp
, ep
, cmdp
)
116 enum {TC_CHANGE
, TC_CURRENT
} which
;
124 char *name
, *p
, *search
, *tag
;
127 switch (cmdp
->argc
) {
129 if (exp
->tlast
!= NULL
)
130 FREE(exp
->tlast
, strlen(exp
->tlast
) + 1);
131 if ((exp
->tlast
= strdup((char *)cmdp
->argv
[0])) == NULL
) {
132 msgq(sp
, M_SYSERR
, NULL
);
137 if (exp
->tlast
== NULL
) {
138 msgq(sp
, M_ERR
, "No previous tag entered.");
146 /* Taglength may limit the number of characters. */
147 if ((tl
= O_VAL(sp
, O_TAGLENGTH
)) != 0 && strlen(exp
->tlast
) > tl
)
148 exp
->tlast
[tl
] = '\0';
150 /* Get the tag information. */
151 if (tag_get(sp
, exp
->tlast
, &tag
, &name
, &search
))
154 /* Get a new FREF structure. */
155 if ((frp
= file_add(sp
, sp
->frp
, name
, 1)) == NULL
) {
156 FREE(tag
, strlen(tag
));
161 * The tags stacks in nvi are a bit tricky. The first record on the
162 * stack is the place where we first did a tag, so it has no search
163 * string. The second record is the first tag, and so on. This means
164 * that the "current" tag is always on the stack. Each tag contains
165 * a file name, search string, and line/column numbers. The search
166 * string is only used for the first access and to display to the user.
167 * we use the saved line/column number when returning to a file.
169 if ((tp
= calloc(1, sizeof(TAG
))) == NULL
)
170 msgq(sp
, M_SYSERR
, NULL
);
171 if (exp
->tagq
.tqh_first
== NULL
) {
175 TAILQ_INSERT_HEAD(&exp
->tagq
, tp
, q
);
177 if ((tp
= calloc(1, sizeof(TAG
))) == NULL
)
178 msgq(sp
, M_SYSERR
, NULL
);
182 /* Copy the search pattern. */
183 if ((tp
->search
= strdup(search
)) == NULL
)
184 msgq(sp
, M_SYSERR
, NULL
);
186 tp
->slen
= strlen(search
);
192 /* Switch to the new file. */
196 MODIFY_CHECK(sp
, sp
->ep
, F_ISSET(cmdp
, E_FORCE
));
198 if (file_init(sp
, frp
, NULL
, 0)) {
200 FREE(tp
, sizeof(TAG
));
201 FREE(tag
, strlen(tag
));
209 * Historic vi accepted a line number as well as a search
210 * string, and people are apparently still using the format.
212 if (isdigit(search
[0])) {
213 m
.lno
= atoi(search
);
218 * Search for the tag; cheap fallback for C functions
219 * if the name is the same but the arguments have changed.
223 flags
= SEARCH_FILE
| SEARCH_TAG
| SEARCH_TERM
;
224 sval
= f_search(sp
, sp
->ep
, &m
, &m
, search
, NULL
, &flags
);
225 if (sval
&& (p
= strrchr(search
, '(')) != NULL
) {
227 sval
= f_search(sp
, sp
->ep
,
228 &m
, &m
, search
, NULL
, &flags
);
231 msgq(sp
, M_ERR
, "%s: search pattern not found.", tag
);
233 FREE(tag
, strlen(tag
));
239 F_SET(frp
, FR_CURSORSET
);
240 F_SET(sp
, S_FSWITCH
);
250 /* Push the tag onto the stack. */
254 TAILQ_INSERT_HEAD(&exp
->tagq
, tp
, q
);
260 /* Free a tag or tagf structure from a queue. */
261 #define FREETAG(tp) { \
262 TAILQ_REMOVE(&exp->tagq, (tp), q); \
263 if ((tp)->search != NULL) \
264 FREE((tp)->search, (tp)->slen); \
265 FREE((tp), sizeof(TAGF)); \
267 #define FREETAGF(tfp) { \
268 TAILQ_REMOVE(&exp->tagfq, (tfp), q); \
269 FREE((tfp)->name, strlen((tfp)->name) + 1); \
270 FREE((tfp), sizeof(TAGF)); \
274 * ex_tagpop -- :tagp[op][!] [number | file]
278 ex_tagpop(sp
, ep
, cmdp
)
289 /* Check for an empty stack. */
291 if (exp
->tagq
.tqh_first
== NULL
) {
292 msgq(sp
, M_INFO
, "The tags stack is empty.");
296 switch (cmdp
->argc
) {
298 /* Toss the current record. */
299 tp
= exp
->tagq
.tqh_first
;
304 off
= strtol(arg
, &p
, 10);
309 tp
= exp
->tagq
.tqh_first
;
313 arglen
= strlen(arg
);
314 for (tp
= exp
->tagq
.tqh_first
;
315 tp
!= NULL
; tp
= tp
->q
.tqe_next
) {
316 /* Use the user's original file name. */
318 if ((t
= strrchr(p
, '/')) == NULL
)
322 if (!strncmp(arg
, t
, arglen
)) {
329 "No file named %s on the tags stack; use :display to see the tags stack.",
334 tp
= exp
->tagq
.tqh_first
;
345 /* If not switching files, it's easy; else do the work. */
346 tp
= exp
->tagq
.tqh_first
;
347 if (tp
->frp
== sp
->frp
) {
351 MODIFY_CHECK(sp
, ep
, F_ISSET(cmdp
, E_FORCE
));
353 if (file_init(sp
, tp
->frp
, NULL
, 0))
356 tp
->frp
->lno
= tp
->lno
;
357 tp
->frp
->cno
= tp
->cno
;
358 F_SET(sp
->frp
, FR_CURSORSET
);
360 F_SET(sp
, S_FSWITCH
);
363 /* If returning to the first tag, the stack is now empty. */
364 if (tp
->q
.tqe_next
== NULL
)
370 * ex_tagtop -- :tagt[op][!]
371 * Clear the tag stack.
374 ex_tagtop(sp
, ep
, cmdp
)
383 /* Pop to oldest saved information. */
385 for (found
= 0; (tp
= exp
->tagq
.tqh_first
) != NULL
; found
= 1) {
386 if (exp
->tagq
.tqh_first
== NULL
)
392 msgq(sp
, M_INFO
, "The tags stack is empty.");
396 /* If not switching files, it's easy; else do the work. */
397 if (tmp
.frp
== sp
->frp
) {
401 MODIFY_CHECK(sp
, sp
->ep
, F_ISSET(cmdp
, E_FORCE
));
403 if (file_init(sp
, tmp
.frp
, NULL
, 0))
406 tmp
.frp
->lno
= tmp
.lno
;
407 tmp
.frp
->cno
= tmp
.cno
;
409 F_SET(sp
->frp
, FR_CURSORSET
);
411 F_SET(sp
, S_FSWITCH
);
418 * Display the list of tags.
421 ex_tagdisplay(sp
, ep
)
433 if ((tp
= exp
->tagq
.tqh_first
) == NULL
) {
434 (void)ex_printf(EXCOOKIE
, "No tags to display.\n");
439 * Figure out the formatting. MNOC is the maximum
440 * number of file name columns before we split the line.
444 tp
= exp
->tagq
.tqh_first
; tp
!= NULL
; tp
= tp
->q
.tqe_next
) {
445 len
= tp
->frp
->nlen
; /* The original name. */
446 name
= tp
->frp
->name
;
447 if (maxlen
< len
&& len
< MNOC
)
452 tp
= exp
->tagq
.tqh_first
; tp
!= NULL
;
453 ++cnt
, tp
= tp
->q
.tqe_next
) {
454 len
= tp
->frp
->nlen
; /* The original name. */
455 name
= tp
->frp
->name
;
456 if (len
> maxlen
|| len
+ tp
->slen
> sp
->cols
)
457 if (tp
== NULL
|| tp
->search
== NULL
)
458 (void)ex_printf(EXCOOKIE
,
459 "%2d %s\n", cnt
, name
);
461 (void)ex_printf(EXCOOKIE
,
462 "%2d %s\n** %*.*s %s\n", cnt
, name
,
463 (int)maxlen
, (int)maxlen
, "", tp
->search
);
465 if (tp
== NULL
|| tp
->search
== NULL
)
466 (void)ex_printf(EXCOOKIE
, "%2d %*.*s\n",
467 cnt
, (int)maxlen
, (int)len
, name
);
469 (void)ex_printf(EXCOOKIE
, "%2d %*.*s %s\n",
470 cnt
, (int)maxlen
, (int)len
, name
,
478 * Create a new list of tag files.
490 /* Free current queue. */
492 while ((tp
= exp
->tagfq
.tqh_first
) != NULL
)
495 /* Create new queue. */
496 for (p
= t
= str
;; ++p
) {
497 if (*p
== '\0' || isblank(*p
)) {
498 if ((len
= p
- t
) > 1) {
499 if ((tp
= malloc(sizeof(TAGF
))) == NULL
||
500 (tp
->name
= malloc(len
+ 1)) == NULL
) {
502 FREE(tp
, sizeof(TAGF
));
503 msgq(sp
, M_SYSERR
, NULL
);
506 memmove(tp
->name
, t
, len
);
507 tp
->name
[len
] = '\0';
509 TAILQ_INSERT_TAIL(&exp
->tagfq
, tp
, q
);
518 /* Free previous queue. */
521 * Free the tags file list.
531 /* Free up tag information. */
533 while ((tp
= exp
->tagq
.tqh_first
) != NULL
)
535 while ((tfp
= exp
->tagfq
.tqh_first
) != NULL
)
537 FREE(exp
->tlast
, strlen(exp
->tlast
) + 1);
543 * Copy a screen's tag structures.
549 EX_PRIVATE
*oexp
, *nexp
;
553 /* Copy tag stack. */
556 for (ap
= oexp
->tagq
.tqh_first
; ap
!= NULL
; ap
= ap
->q
.tqe_next
) {
557 if ((tp
= malloc(sizeof(TAG
))) == NULL
)
560 if (ap
->search
!= NULL
&&
561 (tp
->search
= strdup(ap
->search
)) == NULL
)
563 TAILQ_INSERT_TAIL(&nexp
->tagq
, tp
, q
);
566 /* Copy list of tag files. */
567 for (atfp
= oexp
->tagfq
.tqh_first
;
568 ap
!= NULL
; atfp
= atfp
->q
.tqe_next
) {
569 if ((tfp
= malloc(sizeof(TAGF
))) == NULL
)
572 if ((tfp
->name
= strdup(atfp
->name
)) == NULL
)
574 TAILQ_INSERT_TAIL(&nexp
->tagfq
, tfp
, q
);
577 /* Copy the last tag. */
578 if (oexp
->tlast
!= NULL
&&
579 (nexp
->tlast
= strdup(oexp
->tlast
)) == NULL
) {
580 nomem
: msgq(sp
, M_SYSERR
, NULL
);
588 * Get a tag from the tags files.
591 tag_get(sp
, tag
, tagp
, filep
, searchp
)
593 char *tag
, **tagp
, **filep
, **searchp
;
601 * Find the tag, only display missing file messages once, and
602 * then only if we didn't find the tag.
606 for (p
= NULL
, tfp
= exp
->tagfq
.tqh_first
;
607 tfp
!= NULL
&& p
== NULL
; tfp
= tfp
->q
.tqe_next
) {
609 F_CLR(tfp
, TAGF_DNE
);
610 if (search(sp
, tfp
->name
, tag
, &p
))
611 if (errno
== ENOENT
) {
612 if (!F_ISSET(tfp
, TAGF_DNE_WARN
)) {
614 F_SET(tfp
, TAGF_DNE
);
617 msgq(sp
, M_SYSERR
, tfp
->name
);
621 msgq(sp
, M_ERR
, "%s: tag not found.", tag
);
623 for (tfp
= exp
->tagfq
.tqh_first
;
624 tfp
!= NULL
; tfp
= tfp
->q
.tqe_next
)
625 if (F_ISSET(tfp
, TAGF_DNE
)) {
627 msgq(sp
, M_SYSERR
, tfp
->name
);
628 F_SET(tfp
, TAGF_DNE_WARN
);
634 * Set the return pointers; tagp points to the tag, and, incidentally
635 * the allocated string, filep points to the nul-terminated file name,
636 * searchp points to the nul-terminated search string.
638 for (*tagp
= p
; *p
&& !isblank(*p
); ++p
);
641 for (*p
++ = '\0'; isblank(*p
); ++p
);
642 for (*filep
= p
; *p
&& !isblank(*p
); ++p
);
645 for (*p
++ = '\0'; isblank(*p
); ++p
);
648 malformed
: free(*tagp
);
649 msgq(sp
, M_ERR
, "%s: corrupted tag in %s.", tag
, tfp
->name
);
661 * Search a file for a tag.
664 search(sp
, name
, tname
, tag
)
666 char *name
, *tname
, **tag
;
670 char *endp
, *back
, *front
, *map
, *p
;
672 if ((fd
= open(name
, O_RDONLY
, 0)) < 0)
677 * We'd like to test if the file is too big to mmap. Since we don't
678 * know what size or type off_t's or size_t's are, what the largest
679 * unsigned integral type is, or what random insanity the local C
680 * compiler will perpetrate, doing the comparison in a portable way
681 * is flatly impossible. Hope that malloc fails if the file is too
684 if (fstat(fd
, &sb
) || (map
= mmap(NULL
, (size_t)sb
.st_size
,
685 PROT_READ
, MAP_PRIVATE
, fd
, (off_t
)0)) == (caddr_t
)-1) {
690 back
= front
+ sb
.st_size
;
692 front
= binary_search(tname
, front
, back
);
693 front
= linear_search(tname
, front
, back
);
695 if (front
== NULL
|| (endp
= strchr(front
, '\n')) == NULL
) {
701 if ((p
= malloc(len
+ 1)) == NULL
) {
702 msgq(sp
, M_SYSERR
, NULL
);
706 memmove(p
, front
, len
);
710 done
: if (munmap(map
, (size_t)sb
.st_size
))
711 msgq(sp
, M_SYSERR
, "munmap");
713 msgq(sp
, M_SYSERR
, "close");
718 * Binary search for "string" in memory between "front" and "back".
720 * This routine is expected to return a pointer to the start of a line at
721 * *or before* the first word matching "string". Relaxing the constraint
722 * this way simplifies the algorithm.
725 * front points to the beginning of a line at or before the first
728 * back points to the beginning of a line at or after the first
731 * Base of the Invariants.
735 * Advancing the Invariants:
737 * p = first newline after halfway point from front to back.
739 * If the string at "p" is not greater than the string to match,
740 * p is the new front. Otherwise it is the new back.
744 * The definition of the routine allows it return at any point,
745 * since front is always at or before the line to print.
747 * In fact, it returns when the chosen "p" equals "back". This
748 * implies that there exists a string is least half as long as
749 * (back - front), which in turn implies that a linear search will
750 * be no more expensive than the cost of simply printing a string or two.
752 * Trying to continue with binary search at this point would be
753 * more trouble than it's worth.
755 #define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
758 binary_search(string
, front
, back
)
759 register char *string
, *front
, *back
;
763 p
= front
+ (back
- front
) / 2;
764 SKIP_PAST_NEWLINE(p
, back
);
767 if (compare(string
, p
, back
) == GREATER
)
771 p
= front
+ (back
- front
) / 2;
772 SKIP_PAST_NEWLINE(p
, back
);
778 * Find the first line that starts with string, linearly searching from front
781 * Return NULL for no such line.
783 * This routine assumes:
785 * o front points at the first character in a line.
786 * o front is before or at the first line to be printed.
789 linear_search(string
, front
, back
)
790 char *string
, *front
, *back
;
792 while (front
< back
) {
793 switch (compare(string
, front
, back
)) {
794 case EQUAL
: /* Found it. */
797 case LESS
: /* No such string. */
800 case GREATER
: /* Keep going. */
803 SKIP_PAST_NEWLINE(front
, back
);
809 * Return LESS, GREATER, or EQUAL depending on how the string1 compares
810 * with string2 (s1 ??? s2).
812 * o Matches up to len(s1) are EQUAL.
813 * o Matches up to len(s2) are GREATER.
815 * The string "s1" is null terminated. The string s2 is '\t', space, (or
816 * "back") terminated.
819 * Reasonably modern ctags programs use tabs as separators, not spaces.
820 * However, historic programs did use spaces, and, I got complaints.
823 compare(s1
, s2
, back
)
824 register char *s1
, *s2
, *back
;
826 for (; *s1
&& s2
< back
&& (*s2
!= '\t' && *s2
!= ' '); ++s1
, ++s2
)
828 return (*s1
< *s2
? LESS
: GREATER
);
829 return (*s1
? GREATER
: s2
< back
&&
830 (*s2
!= '\t' && *s2
!= ' ') ? LESS
: EQUAL
);