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.
15 #include <sys/types.h>
17 #include <sys/queue.h>
20 #include <bitstring.h>
31 #include "../common/common.h"
35 static char *binary_search(char *, char *, char *);
36 static int compare(char *, char *, char *);
37 static void ctag_file(SCR *, TAGF *, char *, char **, size_t *);
38 static int ctag_search(SCR *, CHAR_T *, size_t, char *);
39 static int ctag_sfile(SCR *, TAGF *, TAGQ *, char *);
40 static TAGQ *ctag_slist(SCR *, CHAR_T *);
41 static char *linear_search(char *, char *, char *, long);
42 static int tag_copy(SCR *, TAG *, TAG **);
43 static int tag_pop(SCR *, TAGQ *, int);
44 static int tagf_copy(SCR *, TAGF *, TAGF **);
45 static int tagf_free(SCR *, TAGF *);
46 static int tagq_copy(SCR *, TAGQ *, TAGQ **);
50 * The tag code can be entered from main, e.g., "vi -t tag".
52 * PUBLIC: int ex_tag_first(SCR *, CHAR_T *);
55 ex_tag_first(SCR *sp, CHAR_T *tagarg)
59 /* Build an argument for the ex :tag command. */
60 ex_cinit(sp, &cmd, C_TAG, 0, OOBLNO, OOBLNO, 0);
61 argv_exp0(sp, &cmd, tagarg, STRLEN(tagarg));
65 * Historic vi went ahead and created a temporary file when it failed
66 * to find the tag. We match historic practice, but don't distinguish
67 * between real error and failure to find the tag.
69 if (ex_tag_push(sp, &cmd))
72 /* Display tags in the center of the screen. */
73 F_CLR(sp, SC_SCR_TOP);
74 F_SET(sp, SC_SCR_CENTER);
83 * Enter a new TAGQ context based on a ctag string.
85 * PUBLIC: int ex_tag_push(SCR *, EXCMD *);
88 ex_tag_push(SCR *sp, EXCMD *cmdp)
99 if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp,
100 cmdp->argv[0]->len)) == NULL) {
101 msgq(sp, M_SYSERR, NULL);
105 /* Taglength may limit the number of characters. */
107 O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl)
108 exp->tag_last[tl] = '\0';
111 if (exp->tag_last == NULL) {
112 msgq(sp, M_ERR, "158|No previous tag entered");
120 /* Get the tag information. */
121 if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
124 if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN),
125 FL_ISSET(cmdp->iflags, E_C_FORCE)))
133 * Switch context to the next TAG.
135 * PUBLIC: int ex_tag_next(SCR *, EXCMD *);
138 ex_tag_next(SCR *sp, EXCMD *cmdp)
147 if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) {
148 tag_msg(sp, TAG_EMPTY, NULL);
151 if ((tp = TAILQ_NEXT(tqp->current, q)) == NULL) {
152 msgq(sp, M_ERR, "282|Already at the last tag of this group");
155 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
159 if (F_ISSET(tqp, TAG_CSCOPE))
160 (void)cscope_search(sp, tqp, tp);
162 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
163 if (tqp->current->msg) {
164 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
166 msgq(sp, M_INFO, "%s", np);
173 * Switch context to the next TAG.
175 * PUBLIC: int ex_tag_prev(SCR *, EXCMD *);
178 ex_tag_prev(SCR *sp, EXCMD *cmdp)
187 if ((tqp = TAILQ_FIRST(exp->tq)) == NULL) {
188 tag_msg(sp, TAG_EMPTY, NULL);
191 if ((tp = TAILQ_PREV(tqp->current, _tagqh, q)) == NULL) {
192 msgq(sp, M_ERR, "255|Already at the first tag of this group");
195 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
199 if (F_ISSET(tqp, TAG_CSCOPE))
200 (void)cscope_search(sp, tqp, tp);
202 (void)ctag_search(sp, tp->search, tp->slen, tqp->tag);
203 if (tqp->current->msg) {
204 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
206 msgq(sp, M_INFO, "%s", np);
213 * Switch context to the specified TAG.
215 * PUBLIC: int ex_tag_nswitch(SCR *, TAG *, int);
218 ex_tag_nswitch(SCR *sp, TAG *tp, int force)
220 /* Get a file structure. */
221 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
224 /* If not changing files, return, we're done. */
225 if (tp->frp == sp->frp)
228 /* Check for permission to leave. */
229 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
232 /* Initialize the new file. */
233 if (file_init(sp, tp->frp, NULL, FS_SETALT))
236 /* Display tags in the center of the screen. */
237 F_CLR(sp, SC_SCR_TOP);
238 F_SET(sp, SC_SCR_CENTER);
241 F_SET(sp, SC_FSWITCH);
247 * Switch context to the specified TAG in a new screen.
249 * PUBLIC: int ex_tag_Nswitch(SCR *, TAG *, int);
252 ex_tag_Nswitch(SCR *sp, TAG *tp, int force)
256 /* Get a file structure. */
257 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
260 /* Get a new screen. */
261 if (screen_init(sp->gp, sp, &new))
263 if (vs_split(sp, new, 0)) {
264 (void)file_end(new, new->ep, 1);
265 (void)screen_end(new);
269 /* Get a backing file. */
270 if (tp->frp == sp->frp) {
271 /* Copy file state. */
276 new->frp->flags = sp->frp->flags;
277 } else if (file_init(new, tp->frp, NULL, force)) {
278 (void)vs_discard(new, NULL);
279 (void)screen_end(new);
283 /* Create the argument list. */
284 new->cargv = new->argv = ex_buildargv(sp, NULL, tp->frp->name);
286 /* Display tags in the center of the screen. */
287 F_CLR(new, SC_SCR_TOP);
288 F_SET(new, SC_SCR_CENTER);
292 F_SET(sp, SC_SSWITCH);
299 * :tagp[op][!] [number | file]
301 * Pop to a previous TAGQ context.
303 * PUBLIC: int ex_tag_pop(SCR *, EXCMD *);
306 ex_tag_pop(SCR *sp, EXCMD *cmdp)
315 /* Check for an empty stack. */
317 if (TAILQ_EMPTY(exp->tq)) {
318 tag_msg(sp, TAG_EMPTY, NULL);
322 /* Find the last TAG structure that we're going to DISCARD! */
323 switch (cmdp->argc) {
324 case 0: /* Pop one tag. */
325 dtqp = TAILQ_FIRST(exp->tq);
327 case 1: /* Name or number. */
328 INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1,
330 off = strtol(arg, &p, 10);
334 /* Number: pop that many queue entries. */
337 TAILQ_FOREACH(tqp, exp->tq, q)
338 if (--off <= 1) break;
341 "159|Less than %s entries on the tags stack; use :display t[ags]",
348 /* File argument: pop to that queue entry. */
349 filearg: arglen = strlen(arg);
350 for (tqp = TAILQ_FIRST(exp->tq); tqp;
351 dtqp = tqp, tqp = TAILQ_NEXT(tqp, q)) {
352 /* Don't pop to the current file. */
353 if (tqp == TAILQ_FIRST(exp->tq))
355 p = tqp->current->frp->name;
356 if ((t = strrchr(p, '/')) == NULL)
360 if (!strncmp(arg, t, arglen))
364 msgq_str(sp, M_ERR, arg,
365 "160|No file %s on the tags stack to return to; use :display t[ags]");
368 if (tqp == TAILQ_FIRST(exp->tq))
375 return (tag_pop(sp, dtqp, FL_ISSET(cmdp->iflags, E_C_FORCE)));
379 * ex_tag_top -- :tagt[op][!]
380 * Clear the tag stack.
382 * PUBLIC: int ex_tag_top(SCR *, EXCMD *);
385 ex_tag_top(SCR *sp, EXCMD *cmdp)
391 /* Check for an empty stack. */
392 if (TAILQ_EMPTY(exp->tq)) {
393 tag_msg(sp, TAG_EMPTY, NULL);
397 /* Return to the oldest information. */
398 return (tag_pop(sp, TAILQ_PREV(TAILQ_LAST(exp->tq, _tqh), _tqh, q),
399 FL_ISSET(cmdp->iflags, E_C_FORCE)));
404 * Pop up to and including the specified TAGQ context.
407 tag_pop(SCR *sp, TAGQ *dtqp, int force)
416 * Update the cursor from the saved TAG information of the TAG
417 * structure we're moving to.
419 tp = TAILQ_NEXT(dtqp, q)->current;
420 if (tp->frp == sp->frp) {
424 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
427 tp->frp->lno = tp->lno;
428 tp->frp->cno = tp->cno;
429 F_SET(sp->frp, FR_CURSORSET);
430 if (file_init(sp, tp->frp, NULL, FS_SETALT))
433 F_SET(sp, SC_FSWITCH);
436 /* Pop entries off the queue up to and including dtqp. */
438 tqp = TAILQ_FIRST(exp->tq);
439 if (tagq_free(sp, tqp))
441 } while (tqp != dtqp);
444 * If only a single tag left, we've returned to the first tag point,
445 * and the stack is now empty.
447 if (TAILQ_NEXT(TAILQ_FIRST(exp->tq), q) == NULL)
448 tagq_free(sp, TAILQ_FIRST(exp->tq));
455 * Display the list of tags.
457 * PUBLIC: int ex_tag_display(SCR *);
460 ex_tag_display(SCR *sp)
470 if (TAILQ_EMPTY(exp->tq)) {
471 tag_msg(sp, TAG_EMPTY, NULL);
476 * We give the file name 20 columns and the search string the rest.
477 * If there's not enough room, we don't do anything special, it's
478 * not worth the effort, it just makes the display more confusing.
480 * We also assume that characters in file names map 1-1 to printing
481 * characters. This might not be true, but I don't think it's worth
482 * fixing. (The obvious fix is to pass the filenames through the
483 * msg_print function.)
485 #define L_NAME 30 /* Name. */
486 #define L_SLOP 4 /* Leading number plus trailing *. */
487 #define L_SPACE 5 /* Spaces after name, before tag. */
488 #define L_TAG 20 /* Tag. */
489 if (sp->cols <= L_NAME + L_SLOP) {
490 msgq(sp, M_ERR, "292|Display too small.");
495 * Display the list of tags for each queue entry. The first entry
496 * is numbered, and the current tag entry has an asterisk appended.
498 for (cnt = 1, tqp = TAILQ_FIRST(exp->tq); !INTERRUPTED(sp) &&
499 tqp != NULL; ++cnt, tqp = TAILQ_NEXT(tqp, q))
500 TAILQ_FOREACH(tp, tqp->tagq, q) {
501 if (tp == TAILQ_FIRST(tqp->tagq))
502 (void)ex_printf(sp, "%2d ", cnt);
504 (void)ex_printf(sp, " ");
505 p = tp->frp == NULL ? tp->fname : tp->frp->name;
506 if ((len = strlen(p)) > L_NAME) {
507 len = len - (L_NAME - 4);
508 (void)ex_printf(sp, " ... %*.*s",
509 L_NAME - 4, L_NAME - 4, p + len);
512 " %*.*s", L_NAME, L_NAME, p);
513 if (tqp->current == tp)
514 (void)ex_printf(sp, "*");
516 if (tp == TAILQ_FIRST(tqp->tagq) && tqp->tag != NULL &&
517 (sp->cols - L_NAME) >= L_TAG + L_SPACE) {
518 len = strlen(tqp->tag);
519 if (len > sp->cols - (L_NAME + L_SPACE))
520 len = sp->cols - (L_NAME + L_SPACE);
521 (void)ex_printf(sp, "%s%.*s",
522 tqp->current == tp ? " " : " ",
525 (void)ex_printf(sp, "\n");
532 * Copy a screen's tag structures.
534 * PUBLIC: int ex_tag_copy(SCR *, SCR *);
537 ex_tag_copy(SCR *orig, SCR *sp)
539 EX_PRIVATE *oexp, *nexp;
547 /* Copy tag queue and tags stack. */
548 TAILQ_FOREACH(aqp, oexp->tq, q) {
549 if (tagq_copy(sp, aqp, &tqp))
551 TAILQ_FOREACH(ap, aqp->tagq, q) {
552 if (tag_copy(sp, ap, &tp))
554 /* Set the current pointer. */
555 if (aqp->current == ap)
557 TAILQ_INSERT_TAIL(tqp->tagq, tp, q);
559 TAILQ_INSERT_TAIL(nexp->tq, tqp, q);
562 /* Copy list of tag files. */
563 TAILQ_FOREACH(atfp, oexp->tagfq, q) {
564 if (tagf_copy(sp, atfp, &tfp))
566 TAILQ_INSERT_TAIL(nexp->tagfq, tfp, q);
569 /* Copy the last tag. */
570 if (oexp->tag_last != NULL &&
571 (nexp->tag_last = v_wstrdup(sp, oexp->tag_last,
572 STRLEN(oexp->tag_last))) == NULL) {
573 msgq(sp, M_SYSERR, NULL);
581 * Copy a TAGF structure and return it in new memory.
584 tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp)
588 MALLOC_RET(sp, tfp, sizeof(TAGF));
591 /* XXX: Allocate as part of the TAGF structure!!! */
592 if ((tfp->name = strdup(otfp->name)) == NULL) {
603 * Copy a TAGQ structure and return it in new memory.
606 tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp)
612 if (otqp->tag != NULL)
613 len += otqp->tlen + 1;
614 MALLOC_RET(sp, tqp, len);
615 memcpy(tqp, otqp, len);
617 TAILQ_INIT(tqp->tagq);
619 if (otqp->tag != NULL)
628 * Copy a TAG structure and return it in new memory.
631 tag_copy(SCR *sp, TAG *otp, TAG **tpp)
637 if (otp->fname != NULL)
638 len += otp->fnlen + 1;
639 if (otp->search != NULL)
640 len += otp->slen + 1;
641 if (otp->msg != NULL)
642 len += otp->mlen + 1;
643 MALLOC_RET(sp, tp, len);
644 memcpy(tp, otp, len);
646 if (otp->fname != NULL)
647 tp->fname = (char *)tp->buf;
648 if (otp->search != NULL)
649 tp->search = tp->buf + (otp->search - otp->buf);
650 if (otp->msg != NULL)
651 tp->msg = tp->buf + (otp->msg - otp->buf);
659 * Free a TAGF structure.
662 tagf_free(SCR *sp, TAGF *tfp)
667 TAILQ_REMOVE(exp->tagfq, tfp, q);
675 * Free a TAGQ structure (and associated TAG structures).
677 * PUBLIC: int tagq_free(SCR *, TAGQ *);
680 tagq_free(SCR *sp, TAGQ *tqp)
686 while ((tp = TAILQ_FIRST(tqp->tagq)) != NULL) {
687 TAILQ_REMOVE(tqp->tagq, tp, q);
692 * If allocated and then the user failed to switch files, the TAGQ
693 * structure was never attached to any list.
695 if (TAILQ_ENTRY_ISVALID(tqp, q))
696 TAILQ_REMOVE(exp->tq, tqp, q);
702 * PUBLIC: int tagq_push(SCR*, TAGQ*, int, int );
705 tagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force)
720 * Allocate all necessary memory before swapping screens. Initialize
721 * flags so we know what to free.
725 if (TAILQ_EMPTY(exp->tq)) {
726 /* Initialize the `local context' tag queue structure. */
727 CALLOC_GOTO(sp, rtqp, 1, sizeof(TAGQ));
728 TAILQ_INIT(rtqp->tagq);
730 /* Initialize and link in its tag structure. */
731 CALLOC_GOTO(sp, rtp, 1, sizeof(TAG));
732 TAILQ_INSERT_HEAD(rtqp->tagq, rtp, q);
737 * Stick the current context information in a convenient place, we're
738 * about to lose it. Note, if we're called on editor startup, there
739 * will be no FREF structure.
744 istmp = frp == NULL ||
745 (F_ISSET(frp, FR_TMPFILE) && !new_screen);
747 /* Try to switch to the preset tag. */
749 if (ex_tag_Nswitch(sp, tqp->current, force))
752 /* Everything else gets done in the new screen. */
756 if (ex_tag_nswitch(sp, tqp->current, force))
760 * If this is the first tag, put a `current location' queue entry
761 * in place, so we can pop all the way back to the current mark.
762 * Note, it doesn't point to much of anything, it's a placeholder.
764 if (TAILQ_EMPTY(exp->tq)) {
765 TAILQ_INSERT_HEAD(exp->tq, rtqp, q);
767 rtqp = TAILQ_FIRST(exp->tq);
769 /* Link the new TAGQ structure into place. */
770 TAILQ_INSERT_HEAD(exp->tq, tqp, q);
772 (void)ctag_search(sp,
773 tqp->current->search, tqp->current->slen, tqp->tag);
774 if (tqp->current->msg) {
775 INT2CHAR(sp, tqp->current->msg, tqp->current->mlen + 1,
777 msgq(sp, M_INFO, "%s", np);
781 * Move the current context from the temporary save area into the
784 * If we were in a temporary file, we don't have a context to which
785 * we can return, so just make it be the same as what we're moving
786 * to. It will be a little odd that ^T doesn't change anything, but
787 * I don't think it's a big deal.
790 rtqp->current->frp = sp->frp;
791 rtqp->current->lno = sp->lno;
792 rtqp->current->cno = sp->cno;
794 rtqp->current->frp = frp;
795 rtqp->current->lno = lno;
796 rtqp->current->cno = cno;
810 * A few common messages.
812 * PUBLIC: void tag_msg(SCR *, tagmsg_t, char *);
815 tag_msg(SCR *sp, tagmsg_t msg, char *tag)
819 msgq_str(sp, M_ERR, tag,
820 "164|%s: the tag's line number is past the end of the file");
823 msgq(sp, M_INFO, "165|The tags stack is empty");
826 msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
835 * Create a new list of ctag files.
837 * PUBLIC: int ex_tagf_alloc(SCR *, char *);
840 ex_tagf_alloc(SCR *sp, char *str)
847 /* Free current queue. */
849 while ((tfp = TAILQ_FIRST(exp->tagfq)) != NULL)
852 /* Create new queue. */
853 for (p = t = str;; ++p) {
854 if (*p == '\0' || cmdskip(*p)) {
856 MALLOC_RET(sp, tfp, sizeof(TAGF));
857 MALLOC(sp, tfp->name, len + 1);
858 if (tfp->name == NULL) {
862 memcpy(tfp->name, t, len);
863 tfp->name[len] = '\0';
865 TAILQ_INSERT_TAIL(exp->tagfq, tfp, q);
874 /* Free previous queue. */
877 * Free the ex tag information.
879 * PUBLIC: int ex_tag_free(SCR *);
888 /* Free up tag information. */
890 while ((tqp = TAILQ_FIRST(exp->tq)) != NULL)
892 while ((tfp = TAILQ_FIRST(exp->tagfq)) != NULL)
900 * Search a file for a tag.
903 ctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag)
912 * The historic tags file format (from a long, long time ago...)
913 * used a line number, not a search string. I got complaints, so
914 * people are still using the format. POSIX 1003.2 permits it.
916 if (ISDIGIT(search[0])) {
917 INT2CHAR(sp, search, slen+1, np, nlen);
919 if (!db_exist(sp, m.lno)) {
920 tag_msg(sp, TAG_BADLNO, tag);
925 * Search for the tag; cheap fallback for C functions
926 * if the name is the same but the arguments have changed.
930 if (f_search(sp, &m, &m,
931 search, slen, NULL, SEARCH_FILE | SEARCH_TAG)) {
932 INT2CHAR(sp, search, slen, np, nlen);
933 if ((p = strrchr(np, '(')) != NULL) {
935 if (f_search(sp, &m, &m, search, slen,
936 NULL, SEARCH_FILE | SEARCH_TAG))
939 notfound: tag_msg(sp, TAG_SEARCH, tag);
945 * Historically, tags set the search direction if it wasn't
948 if (sp->searchdir == NOTSET)
949 sp->searchdir = FORWARD;
954 * Tags move to the first non-blank, NOT the search pattern start.
958 (void)nonblank(sp, sp->lno, &sp->cno);
964 * Search the list of tags files for a tag, and return tag queue.
967 ctag_slist(SCR *sp, CHAR_T *tag)
979 /* Allocate and initialize the tag queue structure. */
980 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
982 CALLOC_GOTO(sp, tqp, 1, sizeof(TAGQ) + len + 1);
983 TAILQ_INIT(tqp->tagq);
985 memcpy(tqp->tag, np, (tqp->tlen = len) + 1);
988 * Find the tag, only display missing file messages once, and
989 * then only if we didn't find the tag.
991 TAILQ_FOREACH(tfp, exp->tagfq, q)
992 if (ctag_sfile(sp, tfp, tqp, tqp->tag)) {
994 F_SET(tfp, TAGF_ERR);
996 F_CLR(tfp, TAGF_ERR | TAGF_ERR_WARN);
998 /* Check to see if we found anything. */
999 if (TAILQ_EMPTY(tqp->tagq)) {
1000 msgq_str(sp, M_ERR, tqp->tag, "162|%s: tag not found");
1002 TAILQ_FOREACH(tfp, exp->tagfq, q)
1003 if (F_ISSET(tfp, TAGF_ERR) &&
1004 !F_ISSET(tfp, TAGF_ERR_WARN)) {
1005 errno = tfp->errnum;
1006 msgq_str(sp, M_SYSERR, tfp->name, "%s");
1007 F_SET(tfp, TAGF_ERR_WARN);
1021 * Search a tags file for a tag, adding any found to the tag queue.
1024 ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname)
1028 size_t dlen, nlen = 0, slen;
1029 int fd, i, nf1, nf2;
1030 char *back, *front, *map, *p, *search, *t;
1031 char *cname = NULL, *dname = NULL, *name = NULL;
1036 if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
1037 tfp->errnum = errno;
1041 if (fstat(fd, &sb) != 0 ||
1042 (map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE,
1043 MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
1044 tfp->errnum = errno;
1049 tl = O_VAL(sp, O_TAGLENGTH);
1051 back = front + sb.st_size;
1052 front = binary_search(tname, front, back);
1053 front = linear_search(tname, front, back, tl);
1058 * Initialize and link in the tag structure(s). The historic ctags
1059 * file format only permitted a single tag location per tag. The
1060 * obvious extension to permit multiple tags locations per tag is to
1061 * output multiple records in the standard format. Unfortunately,
1062 * this won't work correctly with historic ex/vi implementations,
1063 * because their binary search assumes that there's only one record
1064 * per tag, and so will use a random tag entry if there si more than
1065 * one. This code handles either format.
1067 * The tags file is in the following format:
1069 * <tag> <filename> <line number> | <pattern>
1071 * Figure out how long everything is so we can allocate in one swell
1072 * foop, but discard anything that looks wrong.
1075 /* Nul-terminate the end of the line. */
1076 for (p = front; p < back && *p != '\n'; ++p);
1077 if (p == back || *p != '\n')
1081 /* Update the pointers for the next time. */
1086 /* Break the line into tokens. */
1087 for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
1092 case 1: /* Filename. */
1094 nlen = strlen(name);
1098 /* Check for corruption. */
1099 if (i != 2 || p == NULL || t == NULL)
1102 /* The rest of the string is the search pattern. */
1104 if ((slen = strlen(p)) == 0) {
1105 corrupt: p = msg_print(sp, tname, &nf1);
1106 t = msg_print(sp, tfp->name, &nf2);
1107 msgq(sp, M_ERR, "163|%s: corrupted tag in %s", p, t);
1109 FREE_SPACE(sp, p, 0);
1111 FREE_SPACE(sp, t, 0);
1115 /* Check for passing the last entry. */
1116 if (tl ? strncmp(tname, cname, tl) : strcmp(tname, cname))
1119 /* Resolve the file name. */
1120 ctag_file(sp, tfp, name, &dname, &dlen);
1122 CALLOC_GOTO(sp, tp, 1,
1123 sizeof(TAG) + dlen + 2 + nlen + 1 + (slen + 1) * sizeof(CHAR_T));
1124 tp->fname = (char *)tp->buf;
1125 if (dlen == 1 && *dname == '.')
1127 else if (dlen != 0) {
1128 memcpy(tp->fname, dname, dlen);
1129 tp->fname[dlen] = '/';
1132 memcpy(tp->fname + dlen, name, nlen + 1);
1133 tp->fnlen = dlen + nlen;
1134 tp->search = (CHAR_T*)(tp->fname + tp->fnlen + 1);
1135 CHAR2INT(sp, search, slen + 1, wp, wlen);
1136 MEMCPY(tp->search, wp, (tp->slen = slen) + 1);
1137 TAILQ_INSERT_TAIL(tqp->tagq, tp, q);
1139 /* Try to preset the tag within the current file. */
1140 if (sp->frp != NULL && sp->frp->name != NULL &&
1141 tqp->current == NULL && !strcmp(tp->fname, sp->frp->name))
1145 if (tqp->current == NULL)
1146 tqp->current = TAILQ_FIRST(tqp->tagq);
1149 done: if (munmap(map, sb.st_size))
1150 msgq(sp, M_SYSERR, "munmap");
1152 msgq(sp, M_SYSERR, "close");
1158 * Search for the right path to this file.
1161 ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp)
1168 * If the tag file path is a relative path, see if it exists. If it
1169 * doesn't, look relative to the tags file path. It's okay for a tag
1170 * file to not exist, and historically, vi simply displayed a "new"
1171 * file. However, if the path exists relative to the tag file, it's
1172 * pretty clear what's happening, so we may as well get it right.
1175 if (name[0] != '/' &&
1176 stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
1178 if ((buf = join(tfp->name, name)) == NULL) {
1179 msgq(sp, M_SYSERR, NULL);
1182 if (stat(buf, &sb) == 0) {
1184 *dlenp = strlen(*dirp);
1192 * Binary search for "string" in memory between "front" and "back".
1194 * This routine is expected to return a pointer to the start of a line at
1195 * *or before* the first word matching "string". Relaxing the constraint
1196 * this way simplifies the algorithm.
1199 * front points to the beginning of a line at or before the first
1202 * back points to the beginning of a line at or after the first
1205 * Base of the Invariants.
1209 * Advancing the Invariants:
1211 * p = first newline after halfway point from front to back.
1213 * If the string at "p" is not greater than the string to match,
1214 * p is the new front. Otherwise it is the new back.
1218 * The definition of the routine allows it return at any point,
1219 * since front is always at or before the line to print.
1221 * In fact, it returns when the chosen "p" equals "back". This
1222 * implies that there exists a string is least half as long as
1223 * (back - front), which in turn implies that a linear search will
1224 * be no more expensive than the cost of simply printing a string or two.
1226 * Trying to continue with binary search at this point would be
1227 * more trouble than it's worth.
1233 #define SKIP_PAST_NEWLINE(p, back) \
1234 while (p < back && *p++ != '\n') continue;
1237 binary_search(char *string, char *front, char *back)
1241 p = front + (back - front) / 2;
1242 SKIP_PAST_NEWLINE(p, back);
1245 if (compare(string, p, back) == GREATER)
1249 p = front + (back - front) / 2;
1250 SKIP_PAST_NEWLINE(p, back);
1256 * Find the first line that starts with string, linearly searching from front
1259 * Return NULL for no such line.
1261 * This routine assumes:
1263 * o front points at the first character in a line.
1264 * o front is before or at the first line to be printed.
1267 linear_search(char *string, char *front, char *back, long tl)
1270 while (front < back) {
1271 end = tl && back-front > tl ? front+tl : back;
1272 switch (compare(string, front, end)) {
1273 case EQUAL: /* Found it. */
1275 case LESS: /* No such string. */
1277 case GREATER: /* Keep going. */
1280 SKIP_PAST_NEWLINE(front, back);
1286 * Return LESS, GREATER, or EQUAL depending on how the string1 compares
1287 * with string2 (s1 ??? s2).
1289 * o Matches up to len(s1) are EQUAL.
1290 * o Matches up to len(s2) are GREATER.
1292 * The string "s1" is null terminated. The string s2 is '\t', space, (or
1293 * "back") terminated.
1296 * Reasonably modern ctags programs use tabs as separators, not spaces.
1297 * However, historic programs did use spaces, and, I got complaints.
1300 compare(char *s1, char *s2, char *back)
1302 for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
1304 return (*s1 < *s2 ? LESS : GREATER);
1305 return (*s1 ? GREATER : s2 < back &&
1306 (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);