Expand PMF_FN_* macros.
[netbsd-mini2440.git] / dist / nvi / ex / ex_tag.c
blobec3a4cb024f47b9ce8dd93c744053352ee19ff89
1 /* $NetBSD: ex_tag.c,v 1.6 2009/01/18 03:45:50 lukem Exp $ */
3 /*-
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1992, 1993, 1994, 1995, 1996
7 * Keith Bostic. All rights reserved.
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.
15 #include "config.h"
17 #ifndef lint
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";
19 #endif /* not lint */
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
25 #include <sys/mman.h>
26 #endif
28 #include <sys/queue.h>
29 #include <sys/stat.h>
30 #include <sys/time.h>
32 #include <bitstring.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
43 #include "../common/common.h"
44 #include "../vi/vi.h"
45 #include "tag.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 *));
51 #ifdef GTAGS
52 static int getentry __P((char *, char **, char **, char **));
53 static TAGQ *gtag_slist __P((SCR *, CHAR_T *, int));
54 #endif
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 **));
65 * ex_tag_first --
66 * The tag code can be entered from main, e.g., "vi -t tag".
68 * PUBLIC: int ex_tag_first __P((SCR *, CHAR_T *));
70 int
71 ex_tag_first(SCR *sp, const CHAR_T *tagarg)
73 EXCMD cmd;
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));
80 * XXX
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))
86 return (0);
88 /* Display tags in the center of the screen. */
89 F_CLR(sp, SC_SCR_TOP);
90 F_SET(sp, SC_SCR_CENTER);
92 return (0);
95 #ifdef GTAGS
97 * ex_rtag_push -- ^]
98 * :rtag[!] [string]
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);
110 #endif
113 * ex_tag_push -- ^]
114 * :tag[!] [string]
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)
123 EX_PRIVATE *exp;
124 TAGQ *tqp;
125 unsigned long tl;
127 exp = EXP(sp);
128 switch (cmdp->argc) {
129 case 1:
130 if (exp->tag_last != NULL)
131 free(exp->tag_last);
133 if ((exp->tag_last = v_wstrdup(sp, cmdp->argv[0]->bp,
134 cmdp->argv[0]->len)) == NULL) {
135 msgq(sp, M_SYSERR, NULL);
136 return (1);
139 /* Taglength may limit the number of characters. */
140 if ((tl =
141 O_VAL(sp, O_TAGLENGTH)) != 0 && STRLEN(exp->tag_last) > tl)
142 exp->tag_last[tl] = '\0';
143 break;
144 case 0:
145 if (exp->tag_last == NULL) {
146 msgq(sp, M_ERR, "158|No previous tag entered");
147 return (1);
149 break;
150 default:
151 abort();
154 /* Get the tag information. */
155 #ifdef GTAGS
156 if (O_ISSET(sp, O_GTAGSMODE)) {
157 if ((tqp = gtag_slist(sp, exp->tag_last,
158 F_ISSET(cmdp, E_REFERENCE))) == NULL)
159 return (1);
160 } else
161 #endif
162 if ((tqp = ctag_slist(sp, exp->tag_last)) == NULL)
163 return (1);
165 if (tagq_push(sp, tqp, F_ISSET(cmdp, E_NEWSCREEN),
166 FL_ISSET(cmdp->iflags, E_C_FORCE)))
167 return 1;
169 return 0;
173 * ex_tag_next --
174 * Switch context to the next TAG.
176 * PUBLIC: int ex_tag_next __P((SCR *, EXCMD *));
179 ex_tag_next(SCR *sp, EXCMD *cmdp)
181 EX_PRIVATE *exp;
182 TAG *tp;
183 TAGQ *tqp;
184 const char *np;
185 size_t nlen;
187 exp = EXP(sp);
188 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
189 tag_msg(sp, TAG_EMPTY, NULL);
190 return (1);
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");
194 return (1);
196 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
197 return (1);
198 tqp->current = tp;
200 if (F_ISSET(tqp, TAG_CSCOPE))
201 (void)cscope_search(sp, tqp, tp);
202 else
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,
206 np, nlen);
207 msgq(sp, M_INFO, np);
209 return (0);
213 * ex_tag_prev --
214 * Switch context to the next TAG.
216 * PUBLIC: int ex_tag_prev __P((SCR *, EXCMD *));
219 ex_tag_prev(SCR *sp, EXCMD *cmdp)
221 EX_PRIVATE *exp;
222 TAG *tp;
223 TAGQ *tqp;
224 const char *np;
225 size_t nlen;
227 exp = EXP(sp);
228 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
229 tag_msg(sp, TAG_EMPTY, NULL);
230 return (0);
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");
234 return (1);
236 if (ex_tag_nswitch(sp, tp, FL_ISSET(cmdp->iflags, E_C_FORCE)))
237 return (1);
238 tqp->current = tp;
240 if (F_ISSET(tqp, TAG_CSCOPE))
241 (void)cscope_search(sp, tqp, tp);
242 else
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,
246 np, nlen);
247 msgq(sp, M_INFO, "%s", np);
249 return (0);
253 * ex_tag_nswitch --
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)
263 return (1);
265 /* If not changing files, return, we're done. */
266 if (tp->frp == sp->frp)
267 return (0);
269 /* Check for permission to leave. */
270 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
271 return (1);
273 /* Initialize the new file. */
274 if (file_init(sp, tp->frp, NULL, FS_SETALT))
275 return (1);
277 /* Display tags in the center of the screen. */
278 F_CLR(sp, SC_SCR_TOP);
279 F_SET(sp, SC_SCR_CENTER);
281 /* Switch. */
282 F_SET(sp, SC_FSWITCH);
283 return (0);
287 * ex_tag_Nswitch --
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)
295 SCR *new;
297 /* Get a file structure. */
298 if (tp->frp == NULL && (tp->frp = file_add(sp, tp->fname)) == NULL)
299 return (1);
301 /* Get a new screen. */
302 if (screen_init(sp->gp, sp, &new))
303 return (1);
304 if (vs_split(sp, new, 0)) {
305 (void)file_end(new, new->ep, 1);
306 (void)screen_end(new);
307 return (1);
310 /* Get a backing file. */
311 if (tp->frp == sp->frp) {
312 /* Copy file state. */
313 new->ep = sp->ep;
314 ++new->ep->refcnt;
315 CIRCLEQ_INSERT_HEAD(&new->ep->scrq, new, eq);
317 new->frp = tp->frp;
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);
322 return (1);
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);
332 /* Switch. */
333 sp->nextdisp = new;
334 F_SET(sp, SC_SSWITCH);
336 return (0);
340 * ex_tag_pop -- ^T
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)
350 EX_PRIVATE *exp;
351 TAGQ *tqp, *dtqp = NULL;
352 size_t arglen;
353 long off;
354 const char *arg;
355 char *p, *t;
356 size_t nlen;
358 /* Check for an empty stack. */
359 exp = EXP(sp);
360 if (exp->tq.cqh_first == (void *)&exp->tq) {
361 tag_msg(sp, TAG_EMPTY, NULL);
362 return (1);
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;
369 break;
370 case 1: /* Name or number. */
371 INT2CHAR(sp, cmdp->argv[0]->bp, cmdp->argv[0]->len+1,
372 arg, nlen);
373 off = strtol(arg, &p, 10);
374 if (*p != '\0')
375 goto filearg;
377 /* Number: pop that many queue entries. */
378 if (off < 1)
379 return (0);
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) {
384 msgq(sp, M_ERR,
385 "159|Less than %s entries on the tags stack; use :display t[ags]",
386 arg);
387 return (1);
389 dtqp = tqp;
390 break;
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)
399 continue;
400 p = tqp->current->frp->name;
401 if ((t = strrchr(p, '/')) == NULL)
402 t = p;
403 else
404 ++t;
405 if (!strncmp(arg, t, arglen))
406 break;
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]");
411 return (1);
413 if (tqp == exp->tq.cqh_first)
414 return (0);
415 break;
416 default:
417 abort();
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)
432 EX_PRIVATE *exp;
434 exp = EXP(sp);
436 /* Check for an empty stack. */
437 if (exp->tq.cqh_first == (void *)&exp->tq) {
438 tag_msg(sp, TAG_EMPTY, NULL);
439 return (1);
442 /* Return to the oldest information. */
443 return (tag_pop(sp,
444 exp->tq.cqh_last->q.cqe_prev, FL_ISSET(cmdp->iflags, E_C_FORCE)));
448 * tag_pop --
449 * Pop up to and including the specified TAGQ context.
451 static int
452 tag_pop(SCR *sp, TAGQ *dtqp, int force)
454 EX_PRIVATE *exp;
455 TAG *tp;
456 TAGQ *tqp;
458 exp = EXP(sp);
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) {
466 sp->lno = tp->lno;
467 sp->cno = tp->cno;
468 } else {
469 if (file_m1(sp, force, FS_ALL | FS_POSSIBLE))
470 return (1);
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))
476 return (1);
478 F_SET(sp, SC_FSWITCH);
481 /* Pop entries off the queue up to and including dtqp. */
482 do {
483 tqp = exp->tq.cqh_first;
484 if (tagq_free(sp, tqp))
485 return (0);
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);
495 return (0);
499 * ex_tag_display --
500 * Display the list of tags.
502 * PUBLIC: int ex_tag_display __P((SCR *));
505 ex_tag_display(SCR *sp)
507 EX_PRIVATE *exp;
508 TAG *tp;
509 TAGQ *tqp;
510 int cnt;
511 size_t len;
512 char *p;
514 exp = EXP(sp);
515 if ((tqp = exp->tq.cqh_first) == (void *)&exp->tq) {
516 tag_msg(sp, TAG_EMPTY, NULL);
517 return (0);
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.");
536 return (0);
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);
549 else
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);
556 } else
557 (void)ex_printf(sp,
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 ? " " : " ",
569 (int)len, tqp->tag);
571 (void)ex_printf(sp, "\n");
573 return (0);
577 * ex_tag_copy --
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;
586 TAGQ *aqp, *tqp;
587 TAG *ap, *tp;
588 TAGF *atfp, *tfp;
590 oexp = EXP(orig);
591 nexp = EXP(sp);
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))
597 return (1);
598 for (ap = aqp->tagq.cqh_first;
599 ap != (void *)&aqp->tagq; ap = ap->q.cqe_next) {
600 if (tag_copy(sp, ap, &tp))
601 return (1);
602 /* Set the current pointer. */
603 if (aqp->current == ap)
604 tqp->current = tp;
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))
614 return (1);
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);
623 return (1);
625 return (0);
629 * tagf_copy --
630 * Copy a TAGF structure and return it in new memory.
632 static int
633 tagf_copy(SCR *sp, TAGF *otfp, TAGF **tfpp)
635 TAGF *tfp;
637 MALLOC_RET(sp, tfp, TAGF *, sizeof(TAGF));
638 *tfp = *otfp;
640 /* XXX: Allocate as part of the TAGF structure!!! */
641 if ((tfp->name = strdup(otfp->name)) == NULL)
642 return (1);
644 *tfpp = tfp;
645 return (0);
649 * tagq_copy --
650 * Copy a TAGQ structure and return it in new memory.
652 static int
653 tagq_copy(SCR *sp, TAGQ *otqp, TAGQ **tqpp)
655 TAGQ *tqp;
656 size_t len;
658 len = sizeof(TAGQ);
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);
665 tqp->current = NULL;
666 if (otqp->tag != NULL)
667 tqp->tag = tqp->buf;
669 *tqpp = tqp;
670 return (0);
674 * tag_copy --
675 * Copy a TAG structure and return it in new memory.
677 static int
678 tag_copy(SCR *sp, TAG *otp, TAG **tpp)
680 TAG *tp;
681 size_t len;
683 len = sizeof(TAG);
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);
700 *tpp = tp;
701 return (0);
705 * tagf_free --
706 * Free a TAGF structure.
708 static int
709 tagf_free(SCR *sp, TAGF *tfp)
711 EX_PRIVATE *exp;
713 exp = EXP(sp);
714 TAILQ_REMOVE(&exp->tagfq, tfp, q);
715 free(tfp->name);
716 free(tfp);
717 return (0);
721 * tagq_free --
722 * Free a TAGQ structure (and associated TAG structures).
724 * PUBLIC: int tagq_free __P((SCR *, TAGQ *));
727 tagq_free(SCR *sp, TAGQ *tqp)
729 EX_PRIVATE *exp;
730 TAG *tp;
732 exp = EXP(sp);
733 while ((tp = tqp->tagq.cqh_first) != (void *)&tqp->tagq) {
734 CIRCLEQ_REMOVE(&tqp->tagq, tp, q);
735 free(tp);
738 * !!!
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);
744 free(tqp);
745 return (0);
749 * PUBLIC: int tagq_push __P((SCR*, TAGQ*, int, int ));
752 tagq_push(SCR *sp, TAGQ *tqp, int new_screen, int force)
754 EX_PRIVATE *exp;
755 FREF *frp;
756 TAG *rtp;
757 TAGQ *rtqp;
758 db_recno_t lno;
759 size_t cno;
760 int istmp;
761 const char *np;
762 size_t nlen;
764 exp = EXP(sp);
767 * Allocate all necessary memory before swapping screens. Initialize
768 * flags so we know what to free.
770 rtp = NULL;
771 rtqp = NULL;
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);
780 rtqp->current = rtp;
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.
788 frp = sp->frp;
789 lno = sp->lno;
790 cno = sp->cno;
791 istmp = frp == NULL || (F_ISSET(frp, FR_TMPFILE) && !new_screen);
793 /* Try to switch to the tag. */
794 if (new_screen) {
795 if (ex_tag_Nswitch(sp, tqp->tagq.cqh_first, force))
796 goto err;
798 /* Everything else gets done in the new screen. */
799 sp = sp->nextdisp;
800 exp = EXP(sp);
801 } else
802 if (ex_tag_nswitch(sp, tqp->tagq.cqh_first, force))
803 goto err;
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);
812 } else
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,
822 np, nlen);
823 msgq(sp, M_INFO, "%s", np);
827 * Move the current context from the temporary save area into the
828 * right structure.
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.
835 if (istmp) {
836 rtqp->current->frp = sp->frp;
837 rtqp->current->lno = sp->lno;
838 rtqp->current->cno = sp->cno;
839 } else {
840 rtqp->current->frp = frp;
841 rtqp->current->lno = lno;
842 rtqp->current->cno = cno;
844 return (0);
846 err:
847 alloc_err:
848 if (rtqp != NULL)
849 free(rtqp);
850 if (rtp != NULL)
851 free(rtp);
852 tagq_free(sp, tqp);
853 return (1);
857 * tag_msg
858 * A few common messages.
860 * PUBLIC: void tag_msg __P((SCR *, tagmsg_t, char *));
862 void
863 tag_msg(SCR *sp, tagmsg_t msg, char *tag)
865 switch (msg) {
866 case TAG_BADLNO:
867 msgq_str(sp, M_ERR, tag,
868 "164|%s: the tag's line number is past the end of the file");
869 break;
870 case TAG_EMPTY:
871 msgq(sp, M_INFO, "165|The tags stack is empty");
872 break;
873 case TAG_SEARCH:
874 msgq_str(sp, M_ERR, tag, "166|%s: search pattern not found");
875 break;
876 default:
877 abort();
882 * ex_tagf_alloc --
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)
890 EX_PRIVATE *exp;
891 TAGF *tfp;
892 size_t len;
893 const char *p, *t;
895 /* Free current queue. */
896 exp = EXP(sp);
897 while ((tfp = exp->tagfq.tqh_first) != NULL)
898 tagf_free(sp, tfp);
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) {
907 free(tfp);
908 return (1);
910 memcpy(tfp->name, t, len);
911 tfp->name[len] = '\0';
912 tfp->flags = 0;
913 TAILQ_INSERT_TAIL(&exp->tagfq, tfp, q);
915 t = p + 1;
917 if (*p == '\0')
918 break;
920 return (0);
922 /* Free previous queue. */
924 * ex_tag_free --
925 * Free the ex tag information.
927 * PUBLIC: int ex_tag_free __P((SCR *));
930 ex_tag_free(SCR *sp)
932 EX_PRIVATE *exp;
933 TAGF *tfp;
934 TAGQ *tqp;
936 /* Free up tag information. */
937 exp = EXP(sp);
938 while ((tqp = exp->tq.cqh_first) != (void *)&exp->tq)
939 tagq_free(sp, tqp);
940 while ((tfp = exp->tagfq.tqh_first) != NULL)
941 tagf_free(sp, tfp);
942 if (exp->tag_last != NULL)
943 free(exp->tag_last);
944 return (0);
948 * ctag_search --
949 * Search a file for a tag.
951 static int
952 ctag_search(SCR *sp, CHAR_T *search, size_t slen, char *tag)
954 MARK m;
955 char *p;
956 const char *np;
957 size_t nlen;
960 * !!!
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);
967 m.lno = atoi(np);
968 if (!db_exist(sp, m.lno)) {
969 tag_msg(sp, TAG_BADLNO, tag);
970 return (1);
972 } else {
974 * Search for the tag; cheap fallback for C functions
975 * if the name is the same but the arguments have changed.
977 m.lno = 1;
978 m.cno = 0;
979 if (f_search(sp, &m, &m,
980 search, slen, NULL,
981 SEARCH_FIRST | SEARCH_TAG | SEARCH_PARSE)) {
982 INT2CHAR(sp, search, slen, np, nlen);
983 if ((p = strrchr(np, '(')) != NULL) {
984 slen = p - np;
985 if (f_search(sp, &m, &m, search, slen,
986 NULL, SEARCH_FIRST | SEARCH_TAG))
987 goto notfound;
988 } else {
989 notfound: tag_msg(sp, TAG_SEARCH, tag);
990 return (1);
994 * !!!
995 * Historically, tags set the search direction if it wasn't
996 * already set.
998 if (sp->searchdir == NOTSET)
999 sp->searchdir = FORWARD;
1003 * !!!
1004 * Tags move to the first non-blank, NOT the search pattern start.
1006 sp->lno = m.lno;
1007 sp->cno = 0;
1008 (void)nonblank(sp, sp->lno, &sp->cno);
1009 return (0);
1012 #ifdef GTAGS
1014 * getentry --
1015 * get tag information from current line.
1017 * gtags temporary file format.
1018 * <tag> <lineno> <file> <image>
1020 * sample.
1021 * +------------------------------------------------
1022 * |main 30 main.c main(argc, argv)
1023 * |func 21 subr.c func(arg)
1025 static int
1026 getentry(char *buf, char **tag, char **file, char **line)
1028 char *p = buf;
1030 for (*tag = p; *p && !isspace((unsigned char)*p); p++) /* tag name */
1032 if (*p == 0)
1033 goto err;
1034 *p++ = 0;
1035 for (; *p && isspace((unsigned char)*p); p++) /* (skip blanks) */
1037 if (*p == 0)
1038 goto err;
1039 *line = p; /* line no */
1040 for (*line = p; *p && !isspace((unsigned char)*p); p++)
1042 if (*p == 0)
1043 goto err;
1044 *p++ = 0;
1045 for (; *p && isspace((unsigned char)*p); p++) /* (skip blanks) */
1047 if (*p == 0)
1048 goto err;
1049 *file = p; /* file name */
1050 for (*file = p; *p && !isspace((unsigned char)*p); p++)
1052 if (*p == 0)
1053 goto err;
1054 *p = 0;
1056 /* value check */
1057 if (strlen(*tag) && strlen(*line) && strlen(*file) && atoi(*line) > 0)
1058 return 1; /* OK */
1059 err:
1060 return 0; /* ERROR */
1064 * gtag_slist --
1065 * Search the list of tags files for a tag, and return tag queue.
1067 static TAGQ *
1068 gtag_slist(SCR *sp, CHAR_T *tag, int ref)
1070 TAGQ *tqp;
1071 size_t len, nlen, slen, wlen;
1072 int echk;
1073 TAG *tp;
1074 const char *np;
1075 char *name, *file, *search;
1076 char command[BUFSIZ];
1077 char buf[BUFSIZ];
1078 const CHAR_T *wp;
1079 FILE *fp;
1081 /* Allocate and initialize the tag queue structure. */
1082 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
1083 len = nlen - 1;
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",
1094 np);
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;
1099 else
1100 while (fgetc(fp) != '\n')
1102 if (getentry(buf, &name, &file, &search) == 0) {
1103 echk = 1;
1104 break;
1106 slen = strlen(search);
1107 CALLOC_GOTO(sp, tp,
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);
1118 pclose(fp);
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");
1124 free(tqp);
1125 return (NULL);
1128 tqp->current = tqp->tagq.cqh_first;
1129 return (tqp);
1131 alloc_err:
1132 return (NULL);
1134 #endif
1137 * ctag_slist --
1138 * Search the list of tags files for a tag, and return tag queue.
1140 static TAGQ *
1141 ctag_slist(SCR *sp, CHAR_T *tag)
1143 EX_PRIVATE *exp;
1144 TAGF *tfp;
1145 TAGQ *tqp;
1146 size_t len;
1147 int echk;
1148 const char *np;
1149 size_t nlen;
1151 exp = EXP(sp);
1153 /* Allocate and initialize the tag queue structure. */
1154 INT2CHAR(sp, tag, STRLEN(tag) + 1, np, nlen);
1155 len = nlen - 1;
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.
1165 for (echk = 0,
1166 tfp = exp->tagfq.tqh_first; tfp != NULL; tfp = tfp->q.tqe_next)
1167 if (ctag_sfile(sp, tfp, tqp, tqp->tag)) {
1168 echk = 1;
1169 F_SET(tfp, TAGF_ERR);
1170 } else
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");
1176 if (echk)
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);
1185 free(tqp);
1186 return (NULL);
1189 tqp->current = tqp->tagq.cqh_first;
1190 return (tqp);
1192 alloc_err:
1193 return (NULL);
1197 * ctag_sfile --
1198 * Search a tags file for a tag, adding any found to the tag queue.
1200 static int
1201 ctag_sfile(SCR *sp, TAGF *tfp, TAGQ *tqp, char *tname)
1203 struct stat sb;
1204 TAG *tp;
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;
1209 const CHAR_T *wp;
1210 size_t wlen;
1211 unsigned long tl;
1213 if ((fd = open(tfp->name, O_RDONLY, 0)) < 0) {
1214 tfp->errnum = errno;
1215 return (1);
1219 * XXX
1220 * Some old BSD systems require MAP_FILE as an argument when mapping
1221 * regular files.
1223 #ifndef MAP_FILE
1224 #define MAP_FILE 0
1225 #endif
1227 * XXX
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;
1238 (void)close(fd);
1239 return (1);
1242 tl = O_VAL(sp, O_TAGLENGTH);
1243 front = map;
1244 back = front + sb.st_size;
1245 front = binary_search(tname, front, back);
1246 front = linear_search(tname, front, back, tl);
1247 if (front == NULL)
1248 goto done;
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.
1267 for (;;) {
1268 /* Nul-terminate the end of the line. */
1269 for (p = front; p < back && *p != '\n'; ++p);
1270 if (p == back || *p != '\n')
1271 break;
1272 *p = '\0';
1274 /* Update the pointers for the next time. */
1275 t = p + 1;
1276 p = front;
1277 front = t;
1279 /* Break the line into tokens. */
1280 for (i = 0; i < 2 && (t = strsep(&p, "\t ")) != NULL; ++i)
1281 switch (i) {
1282 case 0: /* Tag. */
1283 cname = t;
1284 break;
1285 case 1: /* Filename. */
1286 name = t;
1287 nlen = strlen(name);
1288 break;
1291 /* Check for corruption. */
1292 if (i != 2 || p == NULL || t == NULL)
1293 goto corrupt;
1295 /* The rest of the string is the search pattern. */
1296 search = p;
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);
1301 if (nf1)
1302 FREE_SPACE(sp, p, 0);
1303 if (nf2)
1304 FREE_SPACE(sp, t, 0);
1305 continue;
1308 /* Check for passing the last entry. */
1309 if (tl ? strncmp(tname, cname, tl) : strcmp(tname, cname))
1310 break;
1312 /* Resolve the file name. */
1313 ctag_file(sp, tfp, name, &dname, &dlen);
1315 CALLOC_GOTO(sp, tp,
1316 TAG *, 1, sizeof(TAG) + dlen + 2 + nlen + 1 +
1317 (slen + 1) * sizeof(CHAR_T));
1318 tp->fname = (char *)tp->buf;
1319 if (dlen != 0) {
1320 memcpy(tp->fname, dname, dlen);
1321 tp->fname[dlen] = '/';
1322 ++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);
1332 alloc_err:
1333 done: if (munmap(map, (size_t)sb.st_size))
1334 msgq(sp, M_SYSERR, "munmap");
1335 if (close(fd))
1336 msgq(sp, M_SYSERR, "close");
1337 return (0);
1341 * ctag_file --
1342 * Search for the right path to this file.
1344 static void
1345 ctag_file(SCR *sp, TAGF *tfp, char *name, char **dirp, size_t *dlenp)
1347 struct stat sb;
1348 size_t len;
1349 char *p, buf[MAXPATHLEN];
1352 * !!!
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.
1359 *dlenp = 0;
1360 if (name[0] != '/' &&
1361 stat(name, &sb) && (p = strrchr(tfp->name, '/')) != NULL) {
1362 *p = '\0';
1363 len = snprintf(buf, sizeof(buf), "%s/%s", tfp->name, name);
1364 if (stat(buf, &sb) == 0) {
1365 *dirp = tfp->name;
1366 *dlenp = strlen(*dirp);
1368 *p = '/';
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.
1379 * Invariants:
1380 * front points to the beginning of a line at or before the first
1381 * matching string.
1383 * back points to the beginning of a line at or after the first
1384 * matching line.
1386 * Base of the Invariants.
1387 * front = NULL;
1388 * back = EOF;
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.
1397 * Termination:
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.
1410 #define EQUAL 0
1411 #define GREATER 1
1412 #define LESS (-1)
1414 #define SKIP_PAST_NEWLINE(p, back) while (p < back && *p++ != '\n');
1416 static char *
1417 binary_search(register char *string, register char *front, register char *back)
1419 register char *p;
1421 p = front + (back - front) / 2;
1422 SKIP_PAST_NEWLINE(p, back);
1424 while (p != back) {
1425 if (compare(string, p, back) == GREATER)
1426 front = p;
1427 else
1428 back = p;
1429 p = front + (back - front) / 2;
1430 SKIP_PAST_NEWLINE(p, back);
1432 return (front);
1436 * Find the first line that starts with string, linearly searching from front
1437 * to back.
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.
1446 static char *
1447 linear_search(char *string, char *front, char *back, unsigned long tl)
1449 char *end;
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. */
1454 return (front);
1455 case LESS: /* No such string. */
1456 return (NULL);
1457 case GREATER: /* Keep going. */
1458 break;
1460 SKIP_PAST_NEWLINE(front, back);
1462 return (NULL);
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.
1475 * !!!
1476 * Reasonably modern ctags programs use tabs as separators, not spaces.
1477 * However, historic programs did use spaces, and, I got complaints.
1479 static int
1480 compare(register char *s1, register char *s2, register char *back)
1482 for (; *s1 && s2 < back && (*s2 != '\t' && *s2 != ' '); ++s1, ++s2)
1483 if (*s1 != *s2)
1484 return (*s1 < *s2 ? LESS : GREATER);
1485 return (*s1 ? GREATER : s2 < back &&
1486 (*s2 != '\t' && *s2 != ' ') ? LESS : EQUAL);