1 /*
2 * Look up vi-style tags in the file "tags".
3 * Invoked either by ":ta routine-name" or by "^]" while sitting
4 * on a string. In the latter case, the tag is the word under
5 * the cursor.
6 * written for vile.
7 *
8 * Copyright (c) 1990, 1995-2018,2019 by Paul Fox and Thomas E. Dickey
9 *
10 * $Id: tags.c,v 1.155 2019/12/19 09:37:58 bod Exp $
11 */
12 #include "estruct.h"
13 #include "edef.h"
14
15 #if OPT_TAGS
16
17 #if OPT_TAGS_CMPL
18 typedef struct {
19 /* FIXME: we could make next-tag work faster if we also hash the
20 * line-pointers for each key.
21 */
22 char *bi_key;
23 } TAGS_DATA;
24
25 #define BI_DATA TAGS_DATA
26 #include "btree.h"
27
28 #endif
29
30 #define UNTAG struct untag
31 UNTAG {
32 char *u_fname;
33 L_NUM u_lineno;
34 C_NUM u_colno;
35 UNTAG *u_stklink;
36 #if OPT_SHOW_TAGS
37 char *u_templ;
38 #endif
39 };
40
41 #define TAGHITS struct taghits
42 TAGHITS {
43 TAGHITS *link;
44 LINE *tag; /* points to tag-buffer line */
45 LINE *hit; /* points to corresponding line in source-file */
46 };
47
48 static TAGHITS *tag_hits = NULL;
49 static UNTAG *untaghead = NULL;
50 static char tagname[NFILEN + 2]; /* +2 since we may add a tab later */
51
52 #if OPT_SHOW_TAGS
53 # if OPT_UPBUFF
54 static int update_tagstack(BUFFER *bp);
55 # endif
56 #endif /* OPT_SHOW_TAGS */
57
58 /*
59 * return (in buf) the Nth whitespace
60 * separated word in "path", counting from 0
61 */
62 static void
nth_name(char * buf,const char * path,int n)63 nth_name(char *buf, const char *path, int n)
64 {
65 while (n-- > 0) {
66 path = skip_cblanks(path);
67 path = skip_ctext(path);
68 }
69 path = skip_cblanks(path);
70 while (*path && !isSpace(*path))
71 *buf++ = *path++;
72 *buf = EOS;
73 }
74
75 static BUFFER *
gettagsfile(int n,int * endofpathflagp,int * did_read)76 gettagsfile(int n, int *endofpathflagp, int *did_read)
77 {
78 #ifdef MDCHK_MODTIME
79 time_t current;
80 #endif
81 char *tagsfile;
82 BUFFER *tagbp;
83 char tagbufname[NBUFN];
84 char tagfilename[NFILEN];
85
86 *endofpathflagp = FALSE;
87 *did_read = FALSE;
88
89 (void) lsprintf(tagbufname, TAGFILE_BufName, n + 1);
90
91 /* is the buffer around? */
92 if ((tagbp = find_b_name(tagbufname)) == NULL) {
93 char *tagf = global_b_val_ptr(VAL_TAGS);
94
95 nth_name(tagfilename, tagf, n);
96 if (!doglob(tagfilename)
97 || tagfilename[0] == EOS) {
98 *endofpathflagp = TRUE;
99 return NULL;
100 }
101
102 /* look up the tags file */
103 tagsfile = cfg_locate(tagfilename, LOCATE_TAGS);
104
105 /* if it isn't around, don't sweat it */
106 if (tagsfile == NULL) {
107 return NULL;
108 }
109
110 /* find the pointer to that buffer */
111 if ((tagbp = bfind(tagbufname, BFINVS)) == NULL) {
112 mlforce("[Can't create tags buffer]");
113 return NULL;
114 }
115
116 if (readin(tagsfile, FALSE, tagbp, FALSE) != TRUE) {
117 zotbuf(tagbp);
118 return NULL;
119 }
120 set_tagsmode(tagbp);
121 *did_read = TRUE;
122 }
123 #ifdef MDCHK_MODTIME
124 /*
125 * Re-read the tags buffer if we are checking modification-times and find
126 * that the tags file's been changed.
127 */
128 if (b_val(tagbp, MDCHK_MODTIME)
129 && get_modtime(tagbp, ¤t)
130 && tagbp->b_modtime != current) {
131 if (!*did_read) {
132 if (readin(tagbp->b_fname, FALSE, tagbp, FALSE) != TRUE) {
133 zotbuf(tagbp);
134 return NULL;
135 }
136 set_tagsmode(tagbp);
137 *did_read = TRUE;
138 }
139 set_modtime(tagbp, tagbp->b_fname);
140 }
141 #endif
142 b_set_invisible(tagbp);
143 return tagbp;
144 }
145
146 #if OPT_TAGS_CMPL
147
148 static void
old_tags(BI_NODE * a)149 old_tags(BI_NODE * a)
150 {
151 beginDisplay();
152 FreeIfNeeded(BI_KEY(a));
153 FreeIfNeeded(TYPECAST(char, a));
154 endofDisplay();
155 }
156
157 static BI_NODE *
new_tags(BI_DATA * a)158 new_tags(BI_DATA * a)
159 {
160 BI_NODE *p;
161
162 beginDisplay();
163 if ((p = typecalloc(BI_NODE)) != NULL) {
164 p->value = *a;
165 if ((BI_KEY(p) = strmalloc(a->bi_key)) == NULL) {
166 old_tags(p);
167 p = NULL;
168 }
169 }
170 endofDisplay();
171
172 return p;
173 }
174
175 /*ARGSUSED*/
176 static void
dpy_tags(BI_NODE * a GCC_UNUSED,int level GCC_UNUSED)177 dpy_tags(BI_NODE * a GCC_UNUSED, int level GCC_UNUSED)
178 {
179 #if OPT_TRACE
180 while (level-- > 0)
181 TRACE((". "));
182 TRACE(("%s (%d)\n", BI_KEY(a), a->balance));
183 #endif
184 }
185
186 static void
xcg_tags(BI_NODE * a,BI_NODE * b)187 xcg_tags(BI_NODE * a, BI_NODE * b)
188 {
189 BI_DATA temp = a->value;
190 a->value = b->value;
191 b->value = temp;
192 }
193
194 #define BI_DATA0 {{0}, 0, {0}}
195 #define BI_TREE0 0, 0, BI_DATA0
196 static BI_TREE tags_tree =
197 {new_tags, old_tags, dpy_tags, xcg_tags, BI_TREE0};
198
199 /* Parse the identifier out of the given line and store it in the binary tree */
200 static void
store_tag(LINE * lp)201 store_tag(LINE *lp)
202 {
203 char my_name[sizeof(tagname)];
204 size_t len, got;
205 int c;
206
207 if (llength(lp) > 0) {
208 len = (size_t) llength(lp);
209 for (got = 0; got < len; got++) {
210 if (got >= sizeof(tagname) - 2) {
211 return; /* ignore super-long identifiers */
212 }
213 c = lgetc(lp, got);
214 if (!isGraph(c))
215 break;
216 my_name[got] = (char) c;
217 }
218 my_name[got] = EOS;
219 if (got) {
220 BI_DATA temp;
221 #ifdef MDTAGIGNORECASE
222 if (b_val(curbp, MDTAGIGNORECASE))
223 mklower(my_name);
224 #endif
225 temp.bi_key = my_name;
226 btree_insert(&tags_tree, &temp);
227 }
228 }
229 }
230
231 /* check if the binary-tree is up-to-date. If not, rebuild it. */
232 static const char **
init_tags_cmpl(char * buf,size_t cpos)233 init_tags_cmpl(char *buf, size_t cpos)
234 {
235 int tf_num;
236 BUFFER *bp;
237 LINE *lp;
238 int done;
239 int flag;
240 int obsolete = (tags_tree.count == 0);
241
242 #ifdef MDTAGIGNORECASE
243 /* If curbp's b_val(curbp,MDTAGIGNORECASE)) is different from the last
244 * time we built the tree, obsolete the tree, since the keys changed.
245 */
246 {
247 static int my_tcase = SORTOFTRUE;
248 if (b_val(curbp, MDTAGIGNORECASE) != my_tcase) {
249 my_tcase = b_val(curbp, MDTAGIGNORECASE);
250 obsolete = TRUE;
251 }
252 }
253 #endif
254 /*
255 * Check if we've already loaded all of the tags buffers. If not, we
256 * know we should build the tree. Also, if any aren't empty, we may
257 * have loaded the buffer for some other reason than tags processing.
258 */
259 if (!obsolete) {
260 for (tf_num = 0;; tf_num++) {
261 bp = gettagsfile(tf_num, &done, &flag);
262 if (!done && bp == NULL)
263 continue; /* More tag files to examine */
264 if (done || bp == NULL)
265 break;
266 (void) bsizes(bp);
267 obsolete = flag || (bp->b_linecount != 0);
268 if (obsolete)
269 break;
270 }
271 }
272
273 if (obsolete) {
274 btree_freeup(&tags_tree);
275
276 for (tf_num = 0;; tf_num++) {
277 bp = gettagsfile(tf_num, &done, &flag);
278 if (!done && bp == NULL)
279 continue; /* More tag files to examine */
280 if (done || bp == NULL)
281 break;
282 for_each_line(lp, bp)
283 store_tag(lp);
284 }
285
286 TRACE(("stored %d tags entries\n", tags_tree.count));
287 }
288
289 return btree_parray(&tags_tree, buf, cpos);
290 }
291
292 int
tags_completion(DONE_ARGS)293 tags_completion(DONE_ARGS)
294 {
295 size_t cpos = *pos;
296 int status = FALSE;
297 const char **nptr;
298
299 kbd_init(); /* nothing to erase */
300 buf[cpos] = EOS; /* terminate it for us */
301
302 beginDisplay();
303 if ((nptr = init_tags_cmpl(buf, cpos)) != NULL) {
304 status = kbd_complete(PASS_DONE_ARGS, (const char *) nptr, sizeof(*nptr));
305 free(TYPECAST(char *, nptr));
306 }
307 endofDisplay();
308 return status;
309 }
310 #else
311 #define tags_completion no_completion
312 #endif
313
314 /*
315 * Record the places we've been to during a tag-search, so we'll not push the
316 * stack just because there's repetition in the tags files. Return true if
317 * we've been here before.
318 */
319 static int
mark_tag_hit(LINE * tag,LINE * hit)320 mark_tag_hit(LINE *tag, LINE *hit)
321 {
322 TAGHITS *p;
323
324 TRACE(("mark_tag_hit %s:%d\n", curbp->b_bname, line_no(curbp, hit)));
325 for (p = tag_hits; p != NULL; p = p->link) {
326 if (p->hit == hit) {
327 TRACE(("... mark_tag_hit TRUE\n"));
328 return (p->tag == tag) ? ABORT : TRUE;
329 }
330 }
331
332 beginDisplay();
333 if ((p = typecalloc(TAGHITS)) != NULL) {
334 p->link = tag_hits;
335 p->tag = tag;
336 p->hit = hit;
337 tag_hits = p;
338 }
339 endofDisplay();
340
341 TRACE(("... mark_tag_hit FALSE\n"));
342 return FALSE;
343 }
344
345 /*
346 * Discard the list of tag-hits when we're about to begin a new tag-search.
347 */
348 static void
free_tag_hits(void)349 free_tag_hits(void)
350 {
351 TAGHITS *p;
352
353 beginDisplay();
354 while ((p = tag_hits) != NULL) {
355 tag_hits = p->link;
356 free(p);
357 }
358 endofDisplay();
359 }
360
361 static void
free_untag(UNTAG * utp)362 free_untag(UNTAG * utp)
363 {
364 if (utp != NULL) {
365 beginDisplay();
366 FreeIfNeeded(utp->u_fname);
367 #if OPT_SHOW_TAGS
368 FreeIfNeeded(utp->u_templ);
369 #endif
370 free(utp);
371 endofDisplay();
372 }
373 }
374
375 /* discard without returning anything */
376 static void
tossuntag(void)377 tossuntag(void)
378 {
379 UNTAG *utp;
380
381 if (untaghead != NULL) {
382 utp = untaghead;
383 untaghead = utp->u_stklink;
384 free_untag(utp);
385 update_scratch(TAGSTACK_BufName, update_tagstack);
386 }
387 }
388
389 /*ARGSUSED*/
390 static void
pushuntag(char * fname,L_NUM lineno,C_NUM colno,char * tag)391 pushuntag(char *fname, L_NUM lineno, C_NUM colno, char *tag)
392 {
393 UNTAG *utp;
394
395 (void) tag;
396
397 beginDisplay();
398 if ((utp = typecalloc(UNTAG)) != NULL) {
399
400 if ((utp->u_fname = strmalloc(fname)) == NULL
401 #if OPT_SHOW_TAGS
402 || (utp->u_templ = strmalloc(tag)) == NULL
403 #endif
404 ) {
405 free_untag(utp);
406 return;
407 }
408 #if OPT_VMS_PATH
409 strip_version(utp->u_fname);
410 #endif
411
412 utp->u_lineno = lineno;
413 utp->u_colno = colno;
414 utp->u_stklink = untaghead;
415 untaghead = utp;
416 update_scratch(TAGSTACK_BufName, update_tagstack);
417 }
418 endofDisplay();
419 }
420
421 static int
popuntag(char * fname,L_NUM * linenop,C_NUM * colnop)422 popuntag(char *fname, L_NUM * linenop, C_NUM * colnop)
423 {
424 UNTAG *utp;
425
426 if (untaghead) {
427 utp = untaghead;
428 untaghead = utp->u_stklink;
429 (void) vl_strncpy(fname, utp->u_fname, NFILEN);
430 *linenop = utp->u_lineno;
431 *colnop = utp->u_colno;
432 free_untag(utp);
433 update_scratch(TAGSTACK_BufName, update_tagstack);
434 return TRUE;
435 }
436 fname[0] = EOS;
437 *linenop = 0;
438 return FALSE;
439 }
440
441 /*
442 * Returns TRUE if:
443 *
444 * a) pin-tagstack mode is enabled, and
445 * b) 2 or more visible windows on screen, and
446 * c) the current tag pop/push operation can be effected (pinned) in curwin.
447 */
448 static int
pinned_tagstack(char * fname)449 pinned_tagstack(char *fname /* target of tag/push op */ )
450 {
451 int pinned = FALSE;
452
453 if (global_g_val(GMDPIN_TAGSTACK)) {
454 BUFFER *bp;
455 int nobufchg, /* TRUE if a call to swbuffer_lfl() will not
456 * change/re-read current buffer.
457 */
458 wdwcnt;
459 WINDOW *wp;
460
461 /* Don't pin tagstack if less than two visible windows on screen. */
462 wdwcnt = 0;
463 for_each_visible_window(wp)
464 wdwcnt++;
465 if (wdwcnt > 1) {
466 /*
467 * Try to display the results of this tag pop in the current
468 * window....
469 *
470 * Got an existing buffer for this filename?
471 */
472
473 if ((bp = find_b_file(fname)) != NULL) {
474 /* yes, set the current window to this buffer. */
475
476 nobufchg = (curbp == bp &&
477 DOT.l &&
478 curwp &&
479 curwp->w_bufp == bp &&
480 bp->b_active);
481
482 if (swbuffer_lfl(bp, FALSE, TRUE) != FALSE) {
483 if (nobufchg) {
484 /*
485 * in this case, DOT is changing to a new location in
486 * the same buffer. if DOT isn't currently within the
487 * bounds of its window, updpos() will barf when
488 * update() is eventually invoked. forestall this
489 * issue by forcing a window reframe.
490 */
491
492 curwp->w_flag |= WFFORCE;
493 }
494 pinned = TRUE;
495 }
496 }
497 }
498 }
499 return (pinned);
500 }
501
502 /* Jump back to the given file, line#, and column#. */
503 static int
finish_pop(char * fname,L_NUM lineno,C_NUM colno)504 finish_pop(char *fname, L_NUM lineno, C_NUM colno)
505 {
506 MARK odot;
507 int s;
508
509 if ((s = pinned_tagstack(fname)) == FALSE) {
510 /* get a window open on the file */
511
512 s = getfile(fname, FALSE);
513 }
514 if (s == TRUE) {
515 /* it's an absolute move -- remember where we are */
516 odot = DOT;
517 s = vl_gotoline(lineno);
518 /* if we moved, update the "last dot" mark */
519 if (s == TRUE) {
520 gocol(colno);
521 if (!sameline(DOT, odot))
522 curwp->w_flag = (USHORT) (curwp->w_flag & ~WFMOVE);
523 else
524 curwp->w_lastdot = odot;
525 }
526 }
527 return s;
528 }
529
530 #ifdef MDTAGIGNORECASE
531 typedef int (*CompareFunc) (const char *a, const char *b, size_t len);
532
533 static int
my_strncasecmp(const char * a,const char * b,size_t len)534 my_strncasecmp(const char *a, const char *b, size_t len)
535 {
536 int aa = EOS, bb = EOS;
537
538 while ((len != 0)
539 && ((aa = (isUpper(*a) ? toLower(*a) : *a)) != EOS)
540 && ((bb = (isUpper(*b) ? toLower(*b) : *b)) != EOS)
541 && (aa == bb)) {
542 len--;
543 a++;
544 b++;
545 }
546
547 return aa - bb;
548 }
549 #endif
550
551 /*
552 * Do exact/inexact lookup of an anchored string in a buffer.
553 * if taglen is 0, matches must be exact (i.e. all
554 * characters significant). if the user enters less than 'taglen'
555 * characters, this match must also be exact. if the user enters
556 * 'taglen' or more characters, only that many characters will be
557 * significant in the lookup.
558 */
559 static LINE *
cheap_tag_scan(LINE * oldlp,char * name,size_t taglen)560 cheap_tag_scan(LINE *oldlp, char *name, size_t taglen)
561 {
562 LINE *lp, *retlp;
563 size_t namelen = strlen(name);
564 int exact = (taglen == 0);
565 int added_tab;
566 #ifdef MDTAGIGNORECASE
567 CompareFunc compare = (b_val(curbp, MDTAGIGNORECASE)
568 ? my_strncasecmp
569 : strncmp);
570 #else
571 #define compare strncmp
572 #endif
573
574 /* force a match of the tab delimiter if we're supposed to do
575 exact matches or if we're searching for something shorter
576 than the "restricted" length */
577 if (exact || namelen < taglen) {
578 name[namelen++] = '\t';
579 name[namelen] = EOS;
580 added_tab = TRUE;
581 } else {
582 added_tab = FALSE;
583 }
584
585 retlp = NULL;
586 lp = lforw(oldlp);
587 while (lp != oldlp) {
588 if (llength(lp) > (int) namelen) {
589 if (!compare(lvalue(lp), name, namelen)) {
590 retlp = lp;
591 break;
592 }
593 }
594 lp = lforw(lp);
595 }
596 if (added_tab)
597 name[namelen - 1] = EOS;
598 return retlp;
599 }
600
601 static LINE *
cheap_buffer_scan(BUFFER * bp,char * patrn,int dir)602 cheap_buffer_scan(BUFFER *bp, char *patrn, int dir)
603 {
604 LINE *lp;
605 LINE *result = NULL;
606 regexp *exp;
607 int ic = FALSE;
608
609 if ((exp = regcomp(patrn, strlen(patrn), FALSE)) != NULL) {
610 #ifdef MDTAGIGNORECASE
611 ic = b_val(bp, MDTAGIGNORECASE);
612 #endif
613
614 TRACE(("cheap_buffer_scan '%s' %s\n",
615 patrn,
616 dir == FORWARD ? "fwd" : "bak"));
617
618 for (lp = dir == FORWARD ? lforw(buf_head(bp)) : lback(buf_head(bp));
619 lp != buf_head(bp);
620 lp = dir == FORWARD ? lforw(lp) : lback(lp)) {
621 if (lregexec(exp, lp, 0, llength(lp), ic)) {
622 result = lp;
623 break;
624 }
625 }
626
627 beginDisplay();
628 free(TYPECAST(char, exp));
629 endofDisplay();
630 }
631 return (result);
632 }
633
634 static int
tag_search(char * tag,int taglen,int initial)635 tag_search(char *tag, int taglen, int initial)
636 {
637 /* variables for 'initial'=FALSE */
638 static int tf_num;
639 static char last_bname[NBUFN];
640 #ifdef MDCHK_MODTIME
641 static time_t last_modtime;
642 #endif
643
644 static TBUFF *srchpat;
645
646 LINE *lp;
647 size_t i;
648 int status;
649 char *tfp, *lplim;
650 char tfname[NFILEN];
651 int flag;
652 L_NUM lineno;
653 C_NUM colno;
654 int changedfile;
655 MARK odot;
656 BUFFER *tagbp;
657 int nomore;
658 int gotafile = FALSE;
659 int retried = FALSE;
660
661 if (initial)
662 tf_num = 0;
663 #ifdef MDCHK_MODTIME
664 else {
665 if ((tagbp = find_b_name(last_bname)) == NULL
666 || last_modtime != tagbp->b_modtime) {
667 initial = TRUE;
668 tf_num = 0;
669 }
670 }
671 #endif
672
673 do {
674 tagbp = gettagsfile(tf_num, &nomore, &flag);
675 lp = NULL;
676 if (nomore) {
677 if (gotafile) {
678 if (initial || retried) {
679 mlwarn("[No such tag: \"%s\"]", tag);
680 return FALSE;
681 }
682 retried = TRUE;
683 tf_num = 0;
684 continue;
685 } else {
686 mlforce("[No tags file available.]");
687 return FALSE;
688 }
689 }
690
691 if (tagbp) {
692 lp = cheap_tag_scan((initial || retried
693 ? buf_head(tagbp)
694 : tagbp->b_dot.l),
695 tag, (size_t) taglen);
696 gotafile = TRUE;
697 }
698
699 tf_num++;
700
701 } while (lp == NULL);
702
703 /* Save the position in the tags-file so "next-tag" will work */
704 tf_num--;
705 (void) vl_strncpy(last_bname, tagbp->b_bname, sizeof(last_bname));
706 tagbp->b_dot.l = lp;
707 tagbp->b_dot.o = 0;
708 #ifdef MDCHK_MODTIME
709 last_modtime = tagbp->b_modtime;
710 #endif
711
712 /* Parse the line from the tags-file */
713 lplim = &lvalue(lp)[llength(lp)];
714 tfp = lvalue(lp);
715 while (tfp < lplim)
716 if (*tfp++ == '\t')
717 break;
718 if (*tfp == '\t') { /* then it's a new-fangled NeXT tags file */
719 tfp++; /* skip the tab */
720 }
721
722 i = 0;
723 if (b_val(curbp, MDTAGSRELTIV) && !is_slashc(*tfp)
724 #if OPT_MSDOS_PATH
725 && !is_msdos_drive(tfp)
726 #endif
727 ) {
728 char *first = tagbp->b_fname;
729 char *lastsl = pathleaf(tagbp->b_fname);
730 while (lastsl != first)
731 tfname[i++] = *first++;
732 }
733 while (i < (sizeof(tfname) - 2) && tfp < lplim && *tfp != '\t') {
734 tfname[i++] = *tfp++;
735 }
736 tfname[i] = EOS;
737
738 if (tfp >= lplim) {
739 mlforce("[Bad line in tags file.]");
740 return FALSE;
741 }
742
743 if (curbp) {
744 lineno = line_no(curbp, DOT.l);
745 colno = getccol(FALSE);
746 if (!isInternalName(curbp->b_fname))
747 pushuntag(curbp->b_fname, lineno, colno, tag);
748 else
749 pushuntag(curbp->b_bname, lineno, colno, tag);
750 }
751
752 changedfile = (curbp == NULL || !same_fname(tfname, curbp, TRUE));
753 if (changedfile)
754 (void) doglob(tfname);
755 if (!pinned_tagstack(tfname)) {
756 if (changedfile) {
757 status = getfile(tfname, TRUE);
758 if (status != TRUE) {
759 tossuntag();
760 return status;
761 }
762 }
763 }
764
765 /* it's an absolute move -- remember where we are */
766 odot = DOT;
767
768 tfp++; /* skip the tab */
769 if (tfp >= lplim) {
770 mlforce("[Bad line in tags file.]");
771 return FALSE;
772 }
773 if (isDigit(*tfp)) { /* then it's a line number */
774 lineno = 0;
775 while (isDigit(*tfp) && (tfp < lplim)) {
776 lineno = 10 * lineno + *tfp - '0';
777 tfp++;
778 }
779 status = gotoline(TRUE, lineno);
780 if (status != TRUE && !changedfile)
781 tossuntag();
782 } else {
783 int delim = *tfp;
784 int quoted = FALSE;
785 char *p;
786 int dir;
787
788 if (delim == '?') {
789 dir = REVERSE;
790 } else {
791 dir = FORWARD;
792 }
793 p = ++tfp; /* skip the "/" */
794
795 /* we're on the '/', so look for the matching one */
796 while (p < lplim) {
797 if (quoted) {
798 quoted = FALSE;
799 } else if (*p == BACKSLASH) {
800 quoted = TRUE;
801 } else if (*p == delim) {
802 break;
803 }
804 p++;
805 }
806 if (p >= lplim) {
807 mlforce("[Bad pattern in tags file.]");
808 return FALSE;
809 }
810
811 if ((srchpat = tb_init(&srchpat, EOS)) == NULL
812 || (srchpat = tb_bappend(&srchpat, tfp, (size_t) (p - tfp))) == NULL
813 || (srchpat = tb_append(&srchpat, EOS)) == NULL)
814 return no_memory("tags");
815
816 lp = cheap_buffer_scan(curbp, tb_values(srchpat), dir);
817 if (lp == NULL) {
818 mlwarn("[Tag not present]");
819 if (!changedfile)
820 tossuntag();
821 return FALSE;
822 }
823 DOT.l = lp;
824 curwp->w_flag |= WFMOVE;
825 (void) firstnonwhite(FALSE, 1);
826 status = TRUE;
827 }
828
829 if (status == TRUE) {
830 int s;
831 if ((s = mark_tag_hit(tagbp->b_dot.l, DOT.l)) != FALSE) {
832 if (popuntag(tfname, &lineno, &colno)) {
833 (void) finish_pop(tfname, lineno, colno);
834 }
835 return s;
836 }
837
838 /*
839 * If we've succeeded on a next-tags, adjust the stack so that
840 * it's all on the same level. A tag-pop will return to the
841 * original position.
842 */
843 if (!initial
844 && untaghead != NULL
845 && untaghead->u_stklink != 0) {
846 UNTAG *p;
847 p = untaghead;
848 untaghead = p->u_stklink;
849 free_untag(p);
850 }
851
852 if (!changedfile)
853 mlwrite("Tag \"%s\" in current buffer", tag);
854
855 /* if we moved, update the "last dot" mark */
856 if (!sameline(DOT, odot)) {
857 curwp->w_lastdot = odot;
858 }
859 }
860
861 return status;
862 }
863
864 KBD_OPTIONS
tags_kbd_options(void)865 tags_kbd_options(void)
866 {
867 KBD_OPTIONS mode = KBD_NORMAL
868 #if OPT_TAGS_CMPL
869 | KBD_MAYBEC
870 #endif
871 ;
872 #ifdef MDTAGIGNORECASE
873 if (b_val(curbp, MDTAGIGNORECASE))
874 mode |= KBD_LOWERC;
875 #endif
876 return mode;
877 }
878
879 /* ARGSUSED */
880 int
gototag(int f GCC_UNUSED,int n GCC_UNUSED)881 gototag(int f GCC_UNUSED, int n GCC_UNUSED)
882 {
883 int s;
884 int taglen;
885
886 if (clexec || isnamedcmd) {
887 if ((s = kbd_string("Tag name: ",
888 tagname, sizeof(tagname),
889 '\n', tags_kbd_options(), tags_completion)) != TRUE)
890 return (s);
891 taglen = b_val(curbp, VAL_TAGLEN);
892 } else {
893 s = screen_to_ident(tagname, sizeof(tagname));
894 taglen = 0;
895 }
896
897 if (s == TRUE) {
898 #ifdef MDTAGIGNORECASE
899 if (b_val(curbp, MDTAGIGNORECASE))
900 mklower(tagname);
901 #endif
902 free_tag_hits();
903 s = tag_search(tagname, taglen, TRUE);
904 } else
905 tagname[0] = EOS;
906 return s;
907 }
908
909 /*
910 * Continue a tag-search by looking for other references in the tags file.
911 */
912 /*ARGSUSED*/
913 int
nexttag(int f GCC_UNUSED,int n GCC_UNUSED)914 nexttag(int f GCC_UNUSED, int n GCC_UNUSED)
915 {
916 int s = FALSE;
917
918 if (tagname[0] != EOS) {
919 do {
920 s = tag_search(tagname, global_b_val(VAL_TAGLEN), FALSE);
921 } while (s == SORTOFTRUE);
922 if (s == ABORT)
923 mlwarn("[No more matches]");
924 }
925 return s;
926 }
927
928 int
untagpop(int f,int n)929 untagpop(int f, int n)
930 {
931 L_NUM lineno = 0;
932 C_NUM colno = 0;
933 char fname[NFILEN];
934 int s;
935
936 n = need_a_count(f, n, 1);
937
938 while (n && popuntag(fname, &lineno, &colno))
939 n--;
940 if (lineno && fname[0]) {
941 s = finish_pop(fname, lineno, colno);
942 } else {
943 mlwarn("[No stacked un-tags]");
944 s = FALSE;
945 }
946 return s;
947 }
948
949 #if OPT_SHOW_TAGS
950 /*ARGSUSED*/
951 static void
maketagslist(int value GCC_UNUSED,void * dummy GCC_UNUSED)952 maketagslist(int value GCC_UNUSED, void *dummy GCC_UNUSED)
953 {
954 UNTAG *utp;
955 int n;
956 int taglen = global_b_val(VAL_TAGLEN);
957 char temp[NFILEN];
958
959 if (taglen == 0) {
960 for (utp = untaghead; utp != NULL; utp = utp->u_stklink) {
961 n = (int) strlen(utp->u_templ);
962 if (n > taglen)
963 taglen = n;
964 }
965 }
966 if (taglen < 10)
967 taglen = 10;
968
969 bprintf(" %*s FROM line in file\n", taglen, "TO tag");
970 bprintf(" ");
971 bpadc('-', taglen);
972 bprintf(" --------- ");
973 bpadc('-', 30);
974
975 for (utp = untaghead, n = 0; utp != NULL; utp = utp->u_stklink)
976 bprintf("\n %2d %*s %8d %s",
977 ++n,
978 taglen, utp->u_templ,
979 utp->u_lineno,
980 shorten_path(vl_strncpy(temp, utp->u_fname, sizeof(temp)),
981 TRUE));
982 }
983
984 #if OPT_UPBUFF
985 /* ARGSUSED */
986 static int
update_tagstack(BUFFER * bp GCC_UNUSED)987 update_tagstack(BUFFER *bp GCC_UNUSED)
988 {
989 return showtagstack(FALSE, 1);
990 }
991 #endif
992
993 /*
994 * Display the contents of the tag-stack
995 */
996 /*ARGSUSED*/
997 int
showtagstack(int f,int n GCC_UNUSED)998 showtagstack(int f, int n GCC_UNUSED)
999 {
1000 return liststuff(TAGSTACK_BufName, FALSE, maketagslist, f, (void *) 0);
1001 }
1002 #endif /* OPT_SHOW_TAGS */
1003
1004 #if NO_LEAKS
1005 void
tags_leaks(void)1006 tags_leaks(void)
1007 {
1008 L_NUM lineno;
1009 C_NUM colno;
1010 char fname[NFILEN];
1011
1012 free_tag_hits();
1013 while (popuntag(fname, &lineno, &colno)) ;
1014 #if OPT_TAGS_CMPL
1015 btree_freeup(&tags_tree);
1016 #endif
1017 }
1018 #endif
1019
1020 #endif /* OPT_TAGS */
1021