home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / tags.c < prev    next >
C/C++ Source or Header  |  1998-09-01  |  19KB  |  912 lines

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