Patch 8.2.4512
Problem:    The find_tags_in_file() function is much too long.
Solution:   Refactor into multiple smaller functions. (Yegappan Lakshmanan,
            closes #9892)
Files:      Filelist, src/Makefile, src/quickfix.c, src/tag.c,
            src/testdir/test83-tags2, src/testdir/test83-tags3,
            src/testdir/test_tagjump.vim


*** ../vim-8.2.4511/Filelist    2022-02-02 15:19:08.652845890 +0000
--- Filelist    2022-03-05 14:32:34.775107817 +0000
***************
*** 198,204 ****
                src/testdir/view_util.vim \
                src/testdir/test[0-9]*.ok \
                src/testdir/test77a.ok \
-               src/testdir/test83-tags? \
                src/testdir/test77a.com \
                src/testdir/test_*.vim \
                src/testdir/python2/*.py \
--- 198,203 ----
***************
*** 1000,1005 ****
--- 999,1005 ----
                runtime/doc/*-tr.1 \
                runtime/doc/*-tr.UTF-8.1 \
                runtime/lang/README.txt \
+               runtime/lang/Makefile \
                runtime/lang/menu_*.vim \
                runtime/keymap/README.txt \
                runtime/keymap/*.vim \
*** ../vim-8.2.4511/src/Makefile        2022-02-23 18:07:34.361914993 +0000
--- src/Makefile        2022-03-05 14:32:34.775107817 +0000
***************
*** 3010,3016 ****
                                 ../../testdir/sautest \
                                 ../../testdir/samples \
                                 ../../testdir/dumps \
-                                ../../testdir/test83-tags? \
                                 ../../testdir/*.ok \
                                 ../../testdir/testluaplugin \
                                 .
--- 3010,3015 ----
*** ../vim-8.2.4511/src/quickfix.c      2022-03-02 20:29:31.606116677 +0000
--- src/quickfix.c      2022-03-05 14:32:34.775107817 +0000
***************
*** 8106,8113 ****
  
        // Convert a line if 'encoding' is not utf-8 and
        // the line contains a non-ASCII character.
!       if (p_vc->vc_type != CONV_NONE
!               && has_non_ascii(IObuff))
        {
            line = string_convert(p_vc, IObuff, NULL);
            if (line == NULL)
--- 8106,8112 ----
  
        // Convert a line if 'encoding' is not utf-8 and
        // the line contains a non-ASCII character.
!       if (p_vc->vc_type != CONV_NONE && has_non_ascii(IObuff))
        {
            line = string_convert(p_vc, IObuff, NULL);
            if (line == NULL)
*** ../vim-8.2.4511/src/tag.c   2022-03-03 10:44:13.646217707 +0000
--- src/tag.c   2022-03-05 14:32:34.779107820 +0000
***************
*** 1580,1587 ****
   * State information used during a tag search
   */
  typedef struct {
!     char_u    *tag_fname;             // name of tag file
      pat_T     orgpat;                 // holds unconverted pattern info
  #ifdef FEAT_MULTI_LANG
      char_u    *help_lang_find;        // lang to be found
      int               is_txt;                 // flag of file extension
--- 1580,1590 ----
   * State information used during a tag search
   */
  typedef struct {
!     char_u    *tag_fname;             // name of the tag file
      pat_T     orgpat;                 // holds unconverted pattern info
+     int               name_only;              // get only tag names
+     int               get_searchpat;  // used for 'showfulltag'
+     int               help_only;              // only search for help tags
  #ifdef FEAT_MULTI_LANG
      char_u    *help_lang_find;        // lang to be found
      int               is_txt;                 // flag of file extension
***************
*** 1607,1613 ****
   * Initialize the state used by find_tags()
   */
      static int
! findtags_state_init(findtags_state_T *st, char_u *pat, int mincount)
  {
      int               mtt;
  
--- 1610,1620 ----
   * Initialize the state used by find_tags()
   */
      static int
! findtags_state_init(
!     findtags_state_T  *st,
!     char_u            *pat,
!     int                       flags,
!     int                       mincount)
  {
      int               mtt;
  
***************
*** 1615,1620 ****
--- 1622,1630 ----
      st->orgpat.pat = pat;
      st->orgpat.len = (int)STRLEN(pat);
      st->orgpat.regmatch.regprog = NULL;
+     st->help_only = (flags & TAG_HELP);
+     st->name_only = (flags & TAG_NAMES);
+     st->get_searchpat = FALSE;
  #ifdef FEAT_MULTI_LANG
      st->help_lang_find = NULL;
      st->is_txt = FALSE;
***************
*** 1648,1653 ****
--- 1658,2249 ----
  }
  
  /*
+  * Free the state used by find_tags()
+  */
+     static void
+ findtags_state_free(findtags_state_T *st)
+ {
+     vim_free(st->tag_fname);
+     vim_free(st->lbuf);
+     vim_regfree(st->orgpat.regmatch.regprog);
+ #ifdef FEAT_EMACS_TAGS
+     vim_free(st->ebuf);
+ #endif
+ }
+ 
+ #ifdef FEAT_MULTI_LANG
+ /*
+  * Initialize the state for searching tags in a Vim help file.
+  * Returns TRUE to process the help file and FALSE to skip the file.
+  */
+     static int
+ findtags_in_help_init(
+     findtags_state_T  *st,
+     int                       flags,
+     char_u            *help_lang,
+     int                       *help_pri)
+ {
+     int               i;
+     char_u    *s;
+ 
+     // Keep en if the file extension is .txt
+     if (st->is_txt)
+       STRCPY(help_lang, "en");
+     else
+     {
+       // Prefer help tags according to 'helplang'.  Put the
+       // two-letter language name in help_lang[].
+       i = (int)STRLEN(st->tag_fname);
+       if (i > 3 && st->tag_fname[i - 3] == '-')
+           STRCPY(help_lang, st->tag_fname + i - 2);
+       else
+           STRCPY(help_lang, "en");
+     }
+     // When searching for a specific language skip tags files
+     // for other languages.
+     if (st->help_lang_find != NULL
+           && STRICMP(help_lang, st->help_lang_find) != 0)
+       return FALSE;
+ 
+     // For CTRL-] in a help file prefer a match with the same
+     // language.
+     if ((flags & TAG_KEEP_LANG)
+           && st->help_lang_find == NULL
+           && curbuf->b_fname != NULL
+           && (i = (int)STRLEN(curbuf->b_fname)) > 4
+           && curbuf->b_fname[i - 1] == 'x'
+           && curbuf->b_fname[i - 4] == '.'
+           && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0)
+       *help_pri = 0;
+     else
+     {
+       *help_pri = 1;
+       for (s = p_hlg; *s != NUL; ++s)
+       {
+           if (STRNICMP(s, help_lang, 2) == 0)
+               break;
+           ++*help_pri;
+           if ((s = vim_strchr(s, ',')) == NULL)
+               break;
+       }
+       if (s == NULL || *s == NUL)
+       {
+           // Language not in 'helplang': use last, prefer English,
+           // unless found already.
+           ++*help_pri;
+           if (STRICMP(help_lang, "en") != 0)
+               ++*help_pri;
+       }
+     }
+ 
+     return TRUE;
+ }
+ #endif
+ 
+ #ifdef FEAT_EVAL
+ /*
+  * Use the 'tagfunc' (if configured and enabled) to get the tags.
+  * Return OK if at least 1 tag has been successfully found, NOTDONE if the
+  * 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise.
+  */
+     static int
+ findtags_apply_tfu(
+     char_u            *pat,
+     findtags_state_T  *st,
+     int                       flags,
+     char_u            *buf_ffname)
+ {
+     int         use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
+     int               retval;
+ 
+     if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL)
+       return NOTDONE;
+ 
+     tfu_in_use = TRUE;
+     retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
+                                                       flags, buf_ffname);
+     tfu_in_use = FALSE;
+ 
+     return retval;
+ }
+ #endif
+ 
+ #ifdef FEAT_EMACS_TAGS
+ /*
+  * Stack for included emacs-tags file.
+  * It has a fixed size, to truncate cyclic includes. jw
+  */
+ # define INCSTACK_SIZE 42
+ static struct
+ {
+     FILE      *fp;
+     char_u    *etag_fname;
+ } incstack[INCSTACK_SIZE];
+ static int incstack_idx = 0;  // index in incstack
+ 
+ /*
+  * Free the include tags file stack.
+  */
+     static void
+ emacs_tags_incstack_free(void)
+ {
+     while (incstack_idx)
+     {
+       --incstack_idx;
+       fclose(incstack[incstack_idx].fp);
+       incstack[incstack_idx].fp = NULL;
+       VIM_CLEAR(incstack[incstack_idx].etag_fname);
+     }
+ }
+ 
+ /*
+  * Emacs tags line with CTRL-L: New file name on next line.
+  * The file name is followed by a ','.  Remember etag file name in ebuf.
+  * Returns a FILE pointer to the tags file. If another tags file is included,
+  * then returns a pointer to the new tags file. The old file pointer is saved
+  * in incstack.
+  */
+     static FILE *
+ emacs_tags_new_filename(findtags_state_T *st, FILE *fp, int *is_etag)
+ {
+     char_u    *p;
+     char_u    *fullpath_ebuf;
+ 
+     if (vim_fgets(st->ebuf, LSIZE, fp))
+       return fp;
+ 
+     for (p = st->ebuf; *p && *p != ','; p++)
+       ;
+     *p = NUL;
+ 
+     // check for an included tags file.
+     // atoi(p+1) is the number of bytes before the next ^L unless it is an
+     // include statement. Skip the included tags file if it exceeds the
+     // maximum.
+     if (STRNCMP(p + 1, "include", 7) != 0 || incstack_idx >= INCSTACK_SIZE)
+       return fp;
+ 
+     // Save current "fp" and "tag_fname" in the stack.
+     incstack[incstack_idx].etag_fname = vim_strsave(st->tag_fname);
+     if (incstack[incstack_idx].etag_fname == NULL)
+       return fp;
+ 
+     incstack[incstack_idx].fp = fp;
+     fp = NULL;
+ 
+     // Figure out "tag_fname" and "fp" to use for
+     // included file.
+     fullpath_ebuf = expand_tag_fname(st->ebuf, st->tag_fname, FALSE);
+     if (fullpath_ebuf != NULL)
+     {
+       fp = mch_fopen((char *)fullpath_ebuf, "r");
+       if (fp != NULL)
+       {
+           if (STRLEN(fullpath_ebuf) > LSIZE)
+               semsg(_(e_tag_file_path_truncated_for_str), st->ebuf);
+           vim_strncpy(st->tag_fname, fullpath_ebuf, MAXPATHL);
+           ++incstack_idx;
+           *is_etag = 0; // we can include anything
+       }
+       vim_free(fullpath_ebuf);
+     }
+     if (fp == NULL)
+     {
+       // Can't open the included file, skip it and
+       // restore old value of "fp".
+       fp = incstack[incstack_idx].fp;
+       vim_free(incstack[incstack_idx].etag_fname);
+     }
+ 
+     return fp;
+ }
+ 
+ /*
+  * Reached the end of an emacs-style tags file. If this is an included tags
+  * file, then pop it from the incstack and continue processing the parent tags
+  * file. Otherwise, processed all the tags.
+  * Returns TRUE if an included tags file is popped and processing should
+  * continue with the parent tags file. Otherwise returns FALSE.
+  */
+     static int
+ emacs_tags_file_eof(findtags_state_T *st, FILE **fp)
+ {
+     if (!incstack_idx)        // reached end of file. stop processing.
+       return FALSE;
+ 
+     // reached the end of an included tags file. pop it.
+     --incstack_idx;
+     fclose(*fp);      // end of this file ...
+     *fp = incstack[incstack_idx].fp;
+     STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
+     vim_free(incstack[incstack_idx].etag_fname);
+ 
+     return TRUE;
+ }
+ 
+ /*
+  * Parse a line from an emacs-style tags file.
+  * Returns OK is the line is parsed successfully, otherwise FALSE.
+  */
+     static int
+ emacs_tags_parse_line(char_u *lbuf, tagptrs_T *tagp)
+ {
+     char_u    *p_7f;
+     char_u    *p;
+ 
+     // There are two formats for an emacs tag line:
+     // 1:  struct EnvBase ^?EnvBase^A139,4627
+     // 2: #define     ARPB_WILD_WORLD ^?153,5194
+     p_7f = vim_strchr(lbuf, 0x7f);
+     if (p_7f == NULL)
+     {
+ etag_fail:
+       if (vim_strchr(lbuf, '\n') != NULL)
+           return FAIL;
+ 
+       // Truncated line.  Ignore it.
+       if (p_verbose >= 5)
+       {
+           verbose_enter();
+           msg(_("Ignoring long line in tags file"));
+           verbose_leave();
+       }
+       tagp->command = lbuf;
+       tagp->tagname = lbuf;
+       tagp->tagname_end = lbuf;
+       return OK;
+     }
+ 
+     // Find ^A.  If not found the line number is after the 0x7f
+     p = vim_strchr(p_7f, Ctrl_A);
+     if (p == NULL)
+       p = p_7f + 1;
+     else
+       ++p;
+ 
+     if (!VIM_ISDIGIT(*p))         // check for start of line number
+       goto etag_fail;
+     tagp->command = p;
+ 
+     if (p[-1] == Ctrl_A)          // first format: explicit tagname given
+     {
+       tagp->tagname = p_7f + 1;
+       tagp->tagname_end = p - 1;
+     }
+     else                          // second format: isolate tagname
+     {
+       // find end of tagname
+       for (p = p_7f - 1; !vim_iswordc(*p); --p)
+           if (p == lbuf)
+               goto etag_fail;
+       tagp->tagname_end = p + 1;
+       while (p >= lbuf && vim_iswordc(*p))
+           --p;
+       tagp->tagname = p + 1;
+     }
+ 
+     return OK;
+ }
+ #endif
+ 
+ /*
+  * Parse a tags file header line in 'st->lbuf'.
+  * Returns TRUE to read the next header line and FALSE to process the line.
+  */
+     static int
+ tags_file_hdr_parse(findtags_state_T *st, vimconv_T *vcp, int *sorted_file)
+ {
+     char_u    *p;
+ 
+     if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0)
+       // Non-header item before the header, e.g. "!" itself.
+       return FALSE;
+ 
+     // Read header line.
+ #ifdef FEAT_TAG_BINS
+     if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0)
+       *sorted_file = st->lbuf[18];
+ #endif
+     if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0)
+     {
+       // Prepare to convert every line from the specified
+       // encoding to 'encoding'.
+       for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p)
+           ;
+       *p = NUL;
+       convert_setup(vcp, st->lbuf + 20, p_enc);
+     }
+ 
+     // Read the next line.  Unrecognized flags are ignored.
+     return TRUE;
+ }
+ 
+ /*
+  * Convert the encoding of a line read from a tags file in 'st->lbuf'.
+  * Converting the pattern from 'enc' to the tags file encoding doesn't work,
+  * because characters are not recognized.
+  */
+     static void
+ findtags_string_convert(findtags_state_T *st, vimconv_T *vcp)
+ {
+     char_u    *conv_line;
+     int               len;
+ 
+     conv_line = string_convert(vcp, st->lbuf, NULL);
+     if (conv_line == NULL)
+       return;
+ 
+     // Copy or swap lbuf and conv_line.
+     len = (int)STRLEN(conv_line) + 1;
+     if (len > st->lbuf_size)
+     {
+       vim_free(st->lbuf);
+       st->lbuf = conv_line;
+       st->lbuf_size = len;
+     }
+     else
+     {
+       STRCPY(st->lbuf, conv_line);
+       vim_free(conv_line);
+     }
+ }
+ 
+     static int
+ findtags_add_match(
+     findtags_state_T  *st,
+     tagptrs_T         *tagp,
+     char_u            *buf_ffname,
+     int                       flags UNUSED,
+     hash_T            *hash,
+     int                       match_re,
+     int                       match_no_ic,
+     int                       matchoff,
+     int                       is_etag UNUSED,
+     char_u            *help_lang UNUSED,
+     int                       help_pri UNUSED)
+ {
+ #ifdef FEAT_CSCOPE
+     int               use_cscope = (flags & TAG_CSCOPE);
+ #endif
+     int               mtt;
+     int               len = 0;
+     int               is_current;             // file name matches
+     int               is_static;              // current tag line is static
+     char_u    *mfp;
+     char_u    *p;
+     char_u    *s;
+ 
+ #ifdef FEAT_CSCOPE
+     if (use_cscope)
+     {
+       // Don't change the ordering, always use the same table.
+       mtt = MT_GL_OTH;
+     }
+     else
+ #endif
+     {
+       // Decide in which array to store this match.
+       is_current = test_for_current(
+ #ifdef FEAT_EMACS_TAGS
+               is_etag,
+ #endif
+               tagp->fname, tagp->fname_end, st->tag_fname,
+               buf_ffname);
+ #ifdef FEAT_EMACS_TAGS
+       is_static = FALSE;
+       if (!is_etag)   // emacs tags are never static
+ #endif
+           is_static = test_for_static(tagp);
+ 
+       // decide in which of the sixteen tables to store this
+       // match
+       if (is_static)
+       {
+           if (is_current)
+               mtt = MT_ST_CUR;
+           else
+               mtt = MT_ST_OTH;
+       }
+       else
+       {
+           if (is_current)
+               mtt = MT_GL_CUR;
+           else
+               mtt = MT_GL_OTH;
+       }
+       if (st->orgpat.regmatch.rm_ic && !match_no_ic)
+           mtt += MT_IC_OFF;
+       if (match_re)
+           mtt += MT_RE_OFF;
+     }
+ 
+     // Add the found match in ht_match[mtt] and ga_match[mtt].
+     // Store the info we need later, which depends on the kind of
+     // tags we are dealing with.
+     if (st->help_only)
+     {
+ #ifdef FEAT_MULTI_LANG
+ # define ML_EXTRA 3
+ #else
+ # define ML_EXTRA 0
+ #endif
+       // Append the help-heuristic number after the tagname, for
+       // sorting it later.  The heuristic is ignored for
+       // detecting duplicates.
+       // The format is {tagname}@{lang}NUL{heuristic}NUL
+       *tagp->tagname_end = NUL;
+       len = (int)(tagp->tagname_end - tagp->tagname);
+       mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
+       if (mfp != NULL)
+       {
+           int heuristic;
+ 
+           p = mfp;
+           STRCPY(p, tagp->tagname);
+ #ifdef FEAT_MULTI_LANG
+           p[len] = '@';
+           STRCPY(p + len + 1, help_lang);
+ #endif
+ 
+           heuristic = help_heuristic(tagp->tagname,
+                   match_re ? matchoff : 0, !match_no_ic);
+ #ifdef FEAT_MULTI_LANG
+           heuristic += help_pri;
+ #endif
+           sprintf((char *)p + len + 1 + ML_EXTRA, "%06d",
+                   heuristic);
+       }
+       *tagp->tagname_end = TAB;
+     }
+     else if (st->name_only)
+     {
+       if (st->get_searchpat)
+       {
+           char_u *temp_end = tagp->command;
+ 
+           if (*temp_end == '/')
+               while (*temp_end && *temp_end != '\r'
+                       && *temp_end != '\n'
+                       && *temp_end != '$')
+                   temp_end++;
+ 
+           if (tagp->command + 2 < temp_end)
+           {
+               len = (int)(temp_end - tagp->command - 2);
+               mfp = alloc(len + 2);
+               if (mfp != NULL)
+                   vim_strncpy(mfp, tagp->command + 2, len);
+           }
+           else
+               mfp = NULL;
+           st->get_searchpat = FALSE;
+       }
+       else
+       {
+           len = (int)(tagp->tagname_end - tagp->tagname);
+           mfp = alloc(sizeof(char_u) + len + 1);
+           if (mfp != NULL)
+               vim_strncpy(mfp, tagp->tagname, len);
+ 
+           // if wanted, re-read line to get long form too
+           if (State & INSERT)
+               st->get_searchpat = p_sft;
+       }
+     }
+     else
+     {
+       size_t tag_fname_len = STRLEN(st->tag_fname);
+ #ifdef FEAT_EMACS_TAGS
+       size_t ebuf_len = 0;
+ #endif
+ 
+       // Save the tag in a buffer.
+       // Use 0x02 to separate fields (Can't use NUL because the
+       // hash key is terminated by NUL, or Ctrl_A because that is
+       // part of some Emacs tag files -- see parse_tag_line).
+       // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
+       // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
+       // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
+       // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
+       len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3;
+ #ifdef FEAT_EMACS_TAGS
+       if (is_etag)
+       {
+           ebuf_len = STRLEN(st->ebuf);
+           len += (int)ebuf_len + 1;
+       }
+       else
+           ++len;
+ #endif
+       mfp = alloc(sizeof(char_u) + len + 1);
+       if (mfp != NULL)
+       {
+           p = mfp;
+           p[0] = mtt + 1;
+           STRCPY(p + 1, st->tag_fname);
+ #ifdef BACKSLASH_IN_FILENAME
+           // Ignore differences in slashes, avoid adding
+           // both path/file and path\file.
+           slash_adjust(p + 1);
+ #endif
+           p[tag_fname_len + 1] = TAG_SEP;
+           s = p + 1 + tag_fname_len + 1;
+ #ifdef FEAT_EMACS_TAGS
+           if (is_etag)
+           {
+               STRCPY(s, st->ebuf);
+               s[ebuf_len] = TAG_SEP;
+               s += ebuf_len + 1;
+           }
+           else
+               *s++ = TAG_SEP;
+ #endif
+           STRCPY(s, st->lbuf);
+       }
+     }
+ 
+     if (mfp != NULL)
+     {
+       hashitem_T      *hi;
+ 
+       // Don't add identical matches.
+       // Add all cscope tags, because they are all listed.
+       // "mfp" is used as a hash key, there is a NUL byte to end
+       // the part that matters for comparing, more bytes may
+       // follow after it.  E.g. help tags store the priority
+       // after the NUL.
+ #ifdef FEAT_CSCOPE
+       if (use_cscope)
+           ++*hash;
+       else
+ #endif
+           *hash = hash_hash(mfp);
+       hi = hash_lookup(&st->ht_match[mtt], mfp, *hash);
+       if (HASHITEM_EMPTY(hi))
+       {
+           if (hash_add_item(&st->ht_match[mtt], hi, mfp, *hash) == FAIL
+                   || ga_grow(&st->ga_match[mtt], 1) != OK)
+           {
+               // Out of memory! Just forget about the rest.
+               st->stop_searching = TRUE;
+               return FAIL;
+           }
+           else
+           {
+               ((char_u **)(st->ga_match[mtt].ga_data))
+                   [st->ga_match[mtt].ga_len++] = mfp;
+               st->match_count++;
+           }
+       }
+       else
+           // duplicate tag, drop it
+           vim_free(mfp);
+     }
+ 
+     return OK;
+ }
+ 
+ /*
   * Search for tags matching 'st->orgpat.pat' in the 'st->tag_fname' tags file.
   * Information needed to search for the tags is in the 'st' state structure.
   * The matching tags are returned in 'st'.
***************
*** 1662,1672 ****
  {
      FILE       *fp = NULL;
      tagptrs_T tagp;
-     int               is_static;              // current tag line is static
-     int               is_current;             // file name matches
      int               eof = FALSE;            // found end-of-file
-     char_u    *p;
-     char_u    *s;
      int               i;
  #ifdef FEAT_MULTI_LANG
      int               help_pri = 0;
--- 2258,2264 ----
***************
*** 1709,1732 ****
      int               match_re;       // match with regexp
      int               matchoff = 0;
  
- #ifdef FEAT_EMACS_TAGS
-     /*
-      * Stack for included emacs-tags file.
-      * It has a fixed size, to truncate cyclic includes. jw
-      */
- # define INCSTACK_SIZE 42
-     struct
-     {
-       FILE    *fp;
-       char_u  *etag_fname;
-     } incstack[INCSTACK_SIZE];
- 
-     int               incstack_idx = 0;       // index in incstack
      int               is_etag;                // current file is emaces style
- #endif
  
-     char_u    *mfp;
-     int               mtt;
      hash_T    hash = 0;
  
  #ifdef FEAT_TAG_BINS
--- 2301,2308 ----
***************
*** 1736,1747 ****
  #endif
      int               line_error = FALSE;             // syntax error
      int               has_re = (flags & TAG_REGEXP);  // regexp used
-     int               help_only = (flags & TAG_HELP);
-     int               name_only = (flags & TAG_NAMES);
  #ifdef FEAT_CSCOPE
      int               use_cscope = (flags & TAG_CSCOPE);
  #endif
-     int               get_it_again = FALSE;
      vimconv_T vimconv;
  
      vimconv.vc_type = CONV_NONE;
--- 2312,2320 ----
***************
*** 1752,1761 ****
      CLEAR_FIELD(search_info);
  #endif
  
!     /*
!      * A file that doesn't exist is silently ignored.  Only when not a
!      * single file is found, an error message is given (further on).
!      */
  #ifdef FEAT_CSCOPE
      if (use_cscope)
        fp = NULL;          // avoid GCC warning
--- 2325,2332 ----
      CLEAR_FIELD(search_info);
  #endif
  
!     // A file that doesn't exist is silently ignored.  Only when not a
!     // single file is found, an error message is given (further on).
  #ifdef FEAT_CSCOPE
      if (use_cscope)
        fp = NULL;          // avoid GCC warning
***************
*** 1765,1819 ****
  #ifdef FEAT_MULTI_LANG
        if (curbuf->b_help)
        {
!           // Keep en if the file extension is .txt
!           if (st->is_txt)
!               STRCPY(help_lang, "en");
!           else
!           {
!               // Prefer help tags according to 'helplang'.  Put the
!               // two-letter language name in help_lang[].
!               i = (int)STRLEN(st->tag_fname);
!               if (i > 3 && st->tag_fname[i - 3] == '-')
!                   STRCPY(help_lang, st->tag_fname + i - 2);
!               else
!                   STRCPY(help_lang, "en");
!           }
!           // When searching for a specific language skip tags files
!           // for other languages.
!           if (st->help_lang_find != NULL
!                   && STRICMP(help_lang, st->help_lang_find) != 0)
                return OK;
- 
-           // For CTRL-] in a help file prefer a match with the same
-           // language.
-           if ((flags & TAG_KEEP_LANG)
-                   && st->help_lang_find == NULL
-                   && curbuf->b_fname != NULL
-                   && (i = (int)STRLEN(curbuf->b_fname)) > 4
-                   && curbuf->b_fname[i - 1] == 'x'
-                   && curbuf->b_fname[i - 4] == '.'
-                   && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0)
-               help_pri = 0;
-           else
-           {
-               help_pri = 1;
-               for (s = p_hlg; *s != NUL; ++s)
-               {
-                   if (STRNICMP(s, help_lang, 2) == 0)
-                       break;
-                   ++help_pri;
-                   if ((s = vim_strchr(s, ',')) == NULL)
-                       break;
-               }
-               if (s == NULL || *s == NUL)
-               {
-                   // Language not in 'helplang': use last, prefer English,
-                   // unless found already.
-                   ++help_pri;
-                   if (STRICMP(help_lang, "en") != 0)
-                       ++help_pri;
-               }
-           }
        }
  #endif
  
--- 2336,2343 ----
  #ifdef FEAT_MULTI_LANG
        if (curbuf->b_help)
        {
!           if (!findtags_in_help_init(st, flags, help_lang, &help_pri))
                return OK;
        }
  #endif
  
***************
*** 1827,1842 ****
            verbose_leave();
        }
      }
!     st->did_open = TRUE;    // remember that we found at least one file
  
!     state = TS_START;   // we're at the start of the file
! #ifdef FEAT_EMACS_TAGS
!     is_etag = 0;          // default is: not emacs style
! #endif
  
!     /*
!      * Read and parse the lines in the file one by one
!      */
      for (;;)
      {
  #ifdef FEAT_TAG_BINS
--- 2351,2362 ----
            verbose_leave();
        }
      }
!     st->did_open = TRUE;      // remember that we found at least one file
  
!     state = TS_START;         // we're at the start of the file
!     is_etag = 0;              // default is: not emacs style
  
!     // Read and parse the lines in the file one by one
      for (;;)
      {
  #ifdef FEAT_TAG_BINS
***************
*** 1860,1871 ****
            st->stop_searching = TRUE;
            break;
        }
!       if (get_it_again)
            goto line_read_in;
  #ifdef FEAT_TAG_BINS
!       /*
!        * For binary search: compute the next offset to use.
!        */
        if (state == TS_BINARY)
        {
            offset = search_info.low_offset + ((search_info.high_offset
--- 2380,2389 ----
            st->stop_searching = TRUE;
            break;
        }
!       if (st->get_searchpat)
            goto line_read_in;
  #ifdef FEAT_TAG_BINS
!       // For binary search: compute the next offset to use.
        if (state == TS_BINARY)
        {
            offset = search_info.low_offset + ((search_info.high_offset
***************
*** 1952,2001 ****
            if (eof)
            {
  #ifdef FEAT_EMACS_TAGS
!               if (incstack_idx)       // this was an included file
                {
!                   --incstack_idx;
!                   fclose(fp); // end of this file ...
!                   fp = incstack[incstack_idx].fp;
!                   STRCPY(st->tag_fname, incstack[incstack_idx].etag_fname);
!                   vim_free(incstack[incstack_idx].etag_fname);
                    is_etag = 1;        // (only etags can include)
!                   continue;   // ... continue with parent file
                }
-               else
  #endif
!                   break;                          // end of file
            }
        }
  line_read_in:
  
        if (vimconv.vc_type != CONV_NONE)
!       {
!           char_u      *conv_line;
!           int len;
! 
!           // Convert every line.  Converting the pattern from 'enc' to
!           // the tags file encoding doesn't work, because characters are
!           // not recognized.
!           conv_line = string_convert(&vimconv, st->lbuf, NULL);
!           if (conv_line != NULL)
!           {
!               // Copy or swap lbuf and conv_line.
!               len = (int)STRLEN(conv_line) + 1;
!               if (len > st->lbuf_size)
!               {
!                   vim_free(st->lbuf);
!                   st->lbuf = conv_line;
!                   st->lbuf_size = len;
!               }
!               else
!               {
!                   STRCPY(st->lbuf, conv_line);
!                   vim_free(conv_line);
!               }
!           }
!       }
! 
  
  #ifdef FEAT_EMACS_TAGS
        /*
--- 2470,2490 ----
            if (eof)
            {
  #ifdef FEAT_EMACS_TAGS
!               if (emacs_tags_file_eof(st, &fp) == TRUE)
                {
!                   // an included tags file. Continue processing the parent
!                   // tags file.
                    is_etag = 1;        // (only etags can include)
!                   continue;
                }
  #endif
!               break;                  // end of file
            }
        }
  line_read_in:
  
        if (vimconv.vc_type != CONV_NONE)
!           findtags_string_convert(st, &vimconv);
  
  #ifdef FEAT_EMACS_TAGS
        /*
***************
*** 2011,2066 ****
        {
            is_etag = 1;                // in case at the start
            state = TS_LINEAR;
!           if (!vim_fgets(st->ebuf, LSIZE, fp))
!           {
!               for (p = st->ebuf; *p && *p != ','; p++)
!                   ;
!               *p = NUL;
! 
!               /*
!                * atoi(p+1) is the number of bytes before the next ^L
!                * unless it is an include statement.
!                */
!               if (STRNCMP(p + 1, "include", 7) == 0
!                       && incstack_idx < INCSTACK_SIZE)
!               {
!                   // Save current "fp" and "tag_fname" in the stack.
!                   if ((incstack[incstack_idx].etag_fname =
!                               vim_strsave(st->tag_fname)) != NULL)
!                   {
!                       char_u *fullpath_ebuf;
! 
!                       incstack[incstack_idx].fp = fp;
!                       fp = NULL;
! 
!                       // Figure out "tag_fname" and "fp" to use for
!                       // included file.
!                       fullpath_ebuf = expand_tag_fname(st->ebuf,
!                               st->tag_fname, FALSE);
!                       if (fullpath_ebuf != NULL)
!                       {
!                           fp = mch_fopen((char *)fullpath_ebuf, "r");
!                           if (fp != NULL)
!                           {
!                               if (STRLEN(fullpath_ebuf) > LSIZE)
!                                   semsg(_(e_tag_file_path_truncated_for_str), 
st->ebuf);
!                               vim_strncpy(st->tag_fname, fullpath_ebuf,
!                                       MAXPATHL);
!                               ++incstack_idx;
!                               is_etag = 0; // we can include anything
!                           }
!                           vim_free(fullpath_ebuf);
!                       }
!                       if (fp == NULL)
!                       {
!                           // Can't open the included file, skip it and
!                           // restore old value of "fp".
!                           fp = incstack[incstack_idx].fp;
!                           vim_free(incstack[incstack_idx].etag_fname);
!                       }
!                   }
!               }
!           }
            continue;
        }
  #endif
--- 2500,2506 ----
        {
            is_etag = 1;                // in case at the start
            state = TS_LINEAR;
!           fp = emacs_tags_new_filename(st, fp, &is_etag);
            continue;
        }
  #endif
***************
*** 2076,2104 ****
            if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0
                    || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1])))
            {
!               if (STRNCMP(st->lbuf, "!_TAG_", 6) != 0)
!                   // Non-header item before the header, e.g. "!" itself.
!                   goto parse_line;
! 
!               /*
!                * Read header line.
!                */
! #ifdef FEAT_TAG_BINS
!               if (STRNCMP(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0)
!                   tag_file_sorted = st->lbuf[18];
! #endif
!               if (STRNCMP(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0)
!               {
!                   // Prepare to convert every line from the specified
!                   // encoding to 'encoding'.
!                   for (p = st->lbuf + 20; *p > ' ' && *p < 127; ++p)
!                       ;
!                   *p = NUL;
!                   convert_setup(&vimconv, st->lbuf + 20, p_enc);
!               }
  
!               // Read the next line.  Unrecognized flags are ignored.
!               continue;
            }
  
            // Headers ends.
--- 2516,2526 ----
            if (STRNCMP(st->lbuf, "!_TAG_", 6) <= 0
                    || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1])))
            {
!               if (tags_file_hdr_parse(st, &vimconv, &tag_file_sorted))
!                   // Read the next line.  Unrecognized flags are ignored.
!                   continue;
  
!               goto parse_line;
            }
  
            // Headers ends.
***************
*** 2338,2348 ****
             */
            tagp.fname = tagp.tagname_end + 1;
            tagp.fname_end = vim_strchr(tagp.fname, TAB);
-           tagp.command = tagp.fname_end + 1;
            if (tagp.fname_end == NULL)
                i = FAIL;
            else
                i = OK;
        }
        else
            i = parse_tag_line(st->lbuf,
--- 2760,2772 ----
             */
            tagp.fname = tagp.tagname_end + 1;
            tagp.fname_end = vim_strchr(tagp.fname, TAB);
            if (tagp.fname_end == NULL)
                i = FAIL;
            else
+           {
+               tagp.command = tagp.fname_end + 1;
                i = OK;
+           }
        }
        else
            i = parse_tag_line(st->lbuf,
***************
*** 2409,2634 ****
            match_re = TRUE;
        }
  
!       /*
!        * If a match is found, add it to ht_match[] and ga_match[].
!        */
        if (match)
        {
!           int len = 0;
! 
! #ifdef FEAT_CSCOPE
!           if (use_cscope)
!           {
!               // Don't change the ordering, always use the same table.
!               mtt = MT_GL_OTH;
!           }
!           else
! #endif
!           {
!               // Decide in which array to store this match.
!               is_current = test_for_current(
! #ifdef FEAT_EMACS_TAGS
                        is_etag,
- #endif
-                       tagp.fname, tagp.fname_end, st->tag_fname,
-                       buf_ffname);
- #ifdef FEAT_EMACS_TAGS
-               is_static = FALSE;
-               if (!is_etag)   // emacs tags are never static
- #endif
-                   is_static = test_for_static(&tagp);
- 
-               // decide in which of the sixteen tables to store this
-               // match
-               if (is_static)
-               {
-                   if (is_current)
-                       mtt = MT_ST_CUR;
-                   else
-                       mtt = MT_ST_OTH;
-               }
-               else
-               {
-                   if (is_current)
-                       mtt = MT_GL_CUR;
-                   else
-                       mtt = MT_GL_OTH;
-               }
-               if (st->orgpat.regmatch.rm_ic && !match_no_ic)
-                   mtt += MT_IC_OFF;
-               if (match_re)
-                   mtt += MT_RE_OFF;
-           }
- 
-           /*
-            * Add the found match in ht_match[mtt] and ga_match[mtt].
-            * Store the info we need later, which depends on the kind of
-            * tags we are dealing with.
-            */
-           if (help_only)
-           {
  #ifdef FEAT_MULTI_LANG
! # define ML_EXTRA 3
  #else
! # define ML_EXTRA 0
  #endif
!               /*
!                * Append the help-heuristic number after the tagname, for
!                * sorting it later.  The heuristic is ignored for
!                * detecting duplicates.
!                * The format is {tagname}@{lang}NUL{heuristic}NUL
!                */
!               *tagp.tagname_end = NUL;
!               len = (int)(tagp.tagname_end - tagp.tagname);
!               mfp = alloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
!               if (mfp != NULL)
!               {
!                   int heuristic;
! 
!                   p = mfp;
!                   STRCPY(p, tagp.tagname);
! #ifdef FEAT_MULTI_LANG
!                   p[len] = '@';
!                   STRCPY(p + len + 1, help_lang);
! #endif
! 
!                   heuristic = help_heuristic(tagp.tagname,
!                           match_re ? matchoff : 0, !match_no_ic);
! #ifdef FEAT_MULTI_LANG
!                   heuristic += help_pri;
! #endif
!                   sprintf((char *)p + len + 1 + ML_EXTRA, "%06d",
!                           heuristic);
!               }
!               *tagp.tagname_end = TAB;
!           }
!           else if (name_only)
!           {
!               if (get_it_again)
!               {
!                   char_u *temp_end = tagp.command;
! 
!                   if (*temp_end == '/')
!                       while (*temp_end && *temp_end != '\r'
!                               && *temp_end != '\n'
!                               && *temp_end != '$')
!                           temp_end++;
! 
!                   if (tagp.command + 2 < temp_end)
!                   {
!                       len = (int)(temp_end - tagp.command - 2);
!                       mfp = alloc(len + 2);
!                       if (mfp != NULL)
!                           vim_strncpy(mfp, tagp.command + 2, len);
!                   }
!                   else
!                       mfp = NULL;
!                   get_it_again = FALSE;
!               }
!               else
!               {
!                   len = (int)(tagp.tagname_end - tagp.tagname);
!                   mfp = alloc(sizeof(char_u) + len + 1);
!                   if (mfp != NULL)
!                       vim_strncpy(mfp, tagp.tagname, len);
! 
!                   // if wanted, re-read line to get long form too
!                   if (State & INSERT)
!                       get_it_again = p_sft;
!               }
!           }
!           else
!           {
!               size_t tag_fname_len = STRLEN(st->tag_fname);
! #ifdef FEAT_EMACS_TAGS
!               size_t ebuf_len = 0;
! #endif
! 
!               // Save the tag in a buffer.
!               // Use 0x02 to separate fields (Can't use NUL because the
!               // hash key is terminated by NUL, or Ctrl_A because that is
!               // part of some Emacs tag files -- see parse_tag_line).
!               // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
!               // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
!               // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
!               // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
!               len = (int)tag_fname_len + (int)STRLEN(st->lbuf) + 3;
! #ifdef FEAT_EMACS_TAGS
!               if (is_etag)
!               {
!                   ebuf_len = STRLEN(st->ebuf);
!                   len += (int)ebuf_len + 1;
!               }
!               else
!                   ++len;
! #endif
!               mfp = alloc(sizeof(char_u) + len + 1);
!               if (mfp != NULL)
!               {
!                   p = mfp;
!                   p[0] = mtt + 1;
!                   STRCPY(p + 1, st->tag_fname);
! #ifdef BACKSLASH_IN_FILENAME
!                   // Ignore differences in slashes, avoid adding
!                   // both path/file and path\file.
!                   slash_adjust(p + 1);
! #endif
!                   p[tag_fname_len + 1] = TAG_SEP;
!                   s = p + 1 + tag_fname_len + 1;
! #ifdef FEAT_EMACS_TAGS
!                   if (is_etag)
!                   {
!                       STRCPY(s, st->ebuf);
!                       s[ebuf_len] = TAG_SEP;
!                       s += ebuf_len + 1;
!                   }
!                   else
!                       *s++ = TAG_SEP;
! #endif
!                   STRCPY(s, st->lbuf);
!               }
!           }
! 
!           if (mfp != NULL)
!           {
!               hashitem_T      *hi;
! 
!               /*
!                * Don't add identical matches.
!                * Add all cscope tags, because they are all listed.
!                * "mfp" is used as a hash key, there is a NUL byte to end
!                * the part that matters for comparing, more bytes may
!                * follow after it.  E.g. help tags store the priority
!                * after the NUL.
!                */
! #ifdef FEAT_CSCOPE
!               if (use_cscope)
!                   hash++;
!               else
! #endif
!                   hash = hash_hash(mfp);
!               hi = hash_lookup(&st->ht_match[mtt], mfp, hash);
!               if (HASHITEM_EMPTY(hi))
!               {
!                   if (hash_add_item(&st->ht_match[mtt], hi, mfp, hash)
!                           == FAIL
!                           || ga_grow(&st->ga_match[mtt], 1) != OK)
!                   {
!                       // Out of memory! Just forget about the rest.
!                       st->stop_searching = TRUE;
!                       break;
!                   }
!                   else
!                   {
!                       ((char_u **)(st->ga_match[mtt].ga_data))
!                           [st->ga_match[mtt].ga_len++] = mfp;
!                       st->match_count++;
!                   }
!               }
!               else
!                   // duplicate tag, drop it
!                   vim_free(mfp);
!           }
        }
  #ifdef FEAT_CSCOPE
        if (use_cscope && eof)
--- 2833,2851 ----
            match_re = TRUE;
        }
  
!       // If a match is found, add it to ht_match[] and ga_match[].
        if (match)
        {
!           if (findtags_add_match(st, &tagp, buf_ffname, flags, &hash,
!                       match_re, match_no_ic, matchoff,
                        is_etag,
  #ifdef FEAT_MULTI_LANG
!                       help_lang, help_pri
  #else
!                       (char_u *)"", 0
  #endif
!                       ) == FAIL)
!               break;
        }
  #ifdef FEAT_CSCOPE
        if (use_cscope && eof)
***************
*** 2652,2663 ****
  #endif
        fclose(fp);
  #ifdef FEAT_EMACS_TAGS
!     while (incstack_idx)
!     {
!       --incstack_idx;
!       fclose(incstack[incstack_idx].fp);
!       vim_free(incstack[incstack_idx].etag_fname);
!     }
  #endif
      if (vimconv.vc_type != CONV_NONE)
        convert_setup(&vimconv, NULL, NULL);
--- 2869,2875 ----
  #endif
        fclose(fp);
  #ifdef FEAT_EMACS_TAGS
!     emacs_tags_incstack_free();
  #endif
      if (vimconv.vc_type != CONV_NONE)
        convert_setup(&vimconv, NULL, NULL);
***************
*** 2687,2694 ****
  findtags_copy_matches(
      findtags_state_T  *st,
      char_u            ***matchesp,
!     int                       *num_matches,
!     int                       name_only)
  {
      char_u    **matches;
      int               mtt;
--- 2899,2905 ----
  findtags_copy_matches(
      findtags_state_T  *st,
      char_u            ***matchesp,
!     int                       *num_matches)
  {
      char_u    **matches;
      int               mtt;
***************
*** 2710,2716 ****
                vim_free(mfp);
            else
            {
!               if (!name_only)
                {
                    // Change mtt back to zero-based.
                    *mfp = *mfp - 1;
--- 2921,2927 ----
                vim_free(mfp);
            else
            {
!               if (!st->name_only)
                {
                    // Change mtt back to zero-based.
                    *mfp = *mfp - 1;
***************
*** 2789,2804 ****
                                                // find all matching tags
  #endif
      int               has_re = (flags & TAG_REGEXP);  // regexp used
-     int               help_only = (flags & TAG_HELP);
-     int               name_only = (flags & TAG_NAMES);
      int               noic = (flags & TAG_NOIC);
  #ifdef FEAT_CSCOPE
      int               use_cscope = (flags & TAG_CSCOPE);
  #endif
      int               verbose = (flags & TAG_VERBOSE);
- #ifdef FEAT_EVAL
-     int         use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
- #endif
      int               save_p_ic = p_ic;
  
      /*
--- 3000,3010 ----
***************
*** 2816,2822 ****
  
      help_save = curbuf->b_help;
  
!     if (findtags_state_init(&st, pat, mincount) == FAIL)
        goto findtag_end;
  
  #ifdef FEAT_CSCOPE
--- 3022,3028 ----
  
      help_save = curbuf->b_help;
  
!     if (findtags_state_init(&st, pat, flags, mincount) == FAIL)
        goto findtag_end;
  
  #ifdef FEAT_CSCOPE
***************
*** 2826,2838 ****
      /*
       * Initialize a few variables
       */
!     if (help_only)                            // want tags from help file
        curbuf->b_help = TRUE;                  // will be restored later
  #ifdef FEAT_CSCOPE
      else if (use_cscope)
      {
        // Make sure we don't mix help and cscope, confuses Coverity.
!       help_only = FALSE;
        curbuf->b_help = FALSE;
      }
  #endif
--- 3032,3044 ----
      /*
       * Initialize a few variables
       */
!     if (st.help_only)                         // want tags from help file
        curbuf->b_help = TRUE;                  // will be restored later
  #ifdef FEAT_CSCOPE
      else if (use_cscope)
      {
        // Make sure we don't mix help and cscope, confuses Coverity.
!       st.help_only = FALSE;
        curbuf->b_help = FALSE;
      }
  #endif
***************
*** 2867,2881 ****
        goto findtag_end;
  
  #ifdef FEAT_EVAL
!     if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use)
!     {
!       tfu_in_use = TRUE;
!       retval = find_tagfunc_tags(pat, &st.ga_match[0], &st.match_count,
!                                                          flags, buf_ffname);
!       tfu_in_use = FALSE;
!       if (retval != NOTDONE)
!           goto findtag_end;
!     }
  #endif
  
      /*
--- 3073,3094 ----
        goto findtag_end;
  
  #ifdef FEAT_EVAL
!     retval = findtags_apply_tfu(pat, &st, flags, buf_ffname);
!     if (retval != NOTDONE)
!       goto findtag_end;
! 
!     // re-initialize the default return value
!     retval = FAIL;
! #endif
! 
! #ifdef FEAT_MULTI_LANG
!     // Set a flag if the file extension is .txt
!     if ((flags & TAG_KEEP_LANG)
!           && st.help_lang_find == NULL
!           && curbuf->b_fname != NULL
!           && (i = (int)STRLEN(curbuf->b_fname)) > 4
!           && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0)
!       st.is_txt = TRUE;
  #endif
  
      /*
***************
*** 2888,2902 ****
       * When the tag file is case-fold sorted, it is either one or the other.
       * Only ignore case when TAG_NOIC not used or 'ignorecase' set.
       */
- #ifdef FEAT_MULTI_LANG
-     // Set a flag if the file extension is .txt
-     if ((flags & TAG_KEEP_LANG)
-           && st.help_lang_find == NULL
-           && curbuf->b_fname != NULL
-           && (i = (int)STRLEN(curbuf->b_fname)) > 4
-           && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0)
-       st.is_txt = TRUE;
- #endif
  #ifdef FEAT_TAG_BINS
      st.orgpat.regmatch.rm_ic = ((p_ic || !noic)
                        && (findall || st.orgpat.headlen == 0 || !p_tbs));
--- 3101,3106 ----
***************
*** 2959,2970 ****
      }
  
  findtag_end:
!     vim_free(st.tag_fname);
!     vim_free(st.lbuf);
!     vim_regfree(st.orgpat.regmatch.regprog);
! #ifdef FEAT_EMACS_TAGS
!     vim_free(st.ebuf);
! #endif
  
      /*
       * Move the matches from the ga_match[] arrays into one list of
--- 3163,3169 ----
      }
  
  findtag_end:
!     findtags_state_free(&st);
  
      /*
       * Move the matches from the ga_match[] arrays into one list of
***************
*** 2973,2979 ****
      if (retval == FAIL)
        st.match_count = 0;
  
!     findtags_copy_matches(&st, matchesp, num_matches, name_only);
  
      curbuf->b_help = help_save;
  #ifdef FEAT_MULTI_LANG
--- 3172,3178 ----
      if (retval == FAIL)
        st.match_count = 0;
  
!     findtags_copy_matches(&st, matchesp, num_matches);
  
      curbuf->b_help = help_save;
  #ifdef FEAT_MULTI_LANG
***************
*** 3194,3286 ****
      char_u    *p;
  
  #ifdef FEAT_EMACS_TAGS
-     char_u    *p_7f;
- 
      if (is_etag)
!     {
!       /*
!        * There are two formats for an emacs tag line:
!        * 1:  struct EnvBase ^?EnvBase^A139,4627
!        * 2: #define   ARPB_WILD_WORLD ^?153,5194
!        */
!       p_7f = vim_strchr(lbuf, 0x7f);
!       if (p_7f == NULL)
!       {
! etag_fail:
!           if (vim_strchr(lbuf, '\n') == NULL)
!           {
!               // Truncated line.  Ignore it.
!               if (p_verbose >= 5)
!               {
!                   verbose_enter();
!                   msg(_("Ignoring long line in tags file"));
!                   verbose_leave();
!               }
!               tagp->command = lbuf;
!               tagp->tagname = lbuf;
!               tagp->tagname_end = lbuf;
!               return OK;
!           }
!           return FAIL;
!       }
! 
!       // Find ^A.  If not found the line number is after the 0x7f
!       p = vim_strchr(p_7f, Ctrl_A);
!       if (p == NULL)
!           p = p_7f + 1;
!       else
!           ++p;
! 
!       if (!VIM_ISDIGIT(*p))       // check for start of line number
!           goto etag_fail;
!       tagp->command = p;
! 
! 
!       if (p[-1] == Ctrl_A)        // first format: explicit tagname given
!       {
!           tagp->tagname = p_7f + 1;
!           tagp->tagname_end = p - 1;
!       }
!       else                        // second format: isolate tagname
!       {
!           // find end of tagname
!           for (p = p_7f - 1; !vim_iswordc(*p); --p)
!               if (p == lbuf)
!                   goto etag_fail;
!           tagp->tagname_end = p + 1;
!           while (p >= lbuf && vim_iswordc(*p))
!               --p;
!           tagp->tagname = p + 1;
!       }
!     }
!     else      // not an Emacs tag
!     {
  #endif
-       // Isolate the tagname, from lbuf up to the first white
-       tagp->tagname = lbuf;
-       p = vim_strchr(lbuf, TAB);
-       if (p == NULL)
-           return FAIL;
-       tagp->tagname_end = p;
  
!       // Isolate file name, from first to second white space
!       if (*p != NUL)
!           ++p;
!       tagp->fname = p;
!       p = vim_strchr(p, TAB);
!       if (p == NULL)
!           return FAIL;
!       tagp->fname_end = p;
  
!       // find start of search command, after second white space
!       if (*p != NUL)
!           ++p;
!       if (*p == NUL)
!           return FAIL;
!       tagp->command = p;
! #ifdef FEAT_EMACS_TAGS
!     }
! #endif
  
      return OK;
  }
--- 3393,3425 ----
      char_u    *p;
  
  #ifdef FEAT_EMACS_TAGS
      if (is_etag)
!       // emacs-style tag file
!       return emacs_tags_parse_line(lbuf, tagp);
  #endif
  
!     // Isolate the tagname, from lbuf up to the first white
!     tagp->tagname = lbuf;
!     p = vim_strchr(lbuf, TAB);
!     if (p == NULL)
!       return FAIL;
!     tagp->tagname_end = p;
  
!     // Isolate file name, from first to second white space
!     if (*p != NUL)
!       ++p;
!     tagp->fname = p;
!     p = vim_strchr(p, TAB);
!     if (p == NULL)
!       return FAIL;
!     tagp->fname_end = p;
! 
!     // find start of search command, after second white space
!     if (*p != NUL)
!       ++p;
!     if (*p == NUL)
!       return FAIL;
!     tagp->command = p;
  
      return OK;
  }
*** ../vim-8.2.4511/src/testdir/test83-tags2    2011-10-12 18:34:15.000000000 
+0100
--- src/testdir/test83-tags2    1970-01-01 00:00:00.000000000 +0000
***************
*** 1,2 ****
- !_TAG_FILE_ENCODING   cp932   //
- ‚`‚a‚b        Xtags2.txt      /‚`‚a‚b
--- 0 ----
*** ../vim-8.2.4511/src/testdir/test83-tags3    2011-10-12 18:35:42.000000000 
+0100
--- src/testdir/test83-tags3    1970-01-01 00:00:00.000000000 +0000
***************
*** 1,102 ****
- !_TAG_FILE_SORTED     1       //
- !_TAG_FILE_ENCODING   cp932   //
- abc1  Xtags3.txt      /‚`‚a‚b
- abc2  Xtags3.txt      /‚`‚a‚b
- abc3  Xtags3.txt      /‚`‚a‚b
- abc4  Xtags3.txt      /‚`‚a‚b
- abc5  Xtags3.txt      /‚`‚a‚b
- abc6  Xtags3.txt      /‚`‚a‚b
- abc7  Xtags3.txt      /‚`‚a‚b
- abc8  Xtags3.txt      /‚`‚a‚b
- abc9  Xtags3.txt      /‚`‚a‚b
- abc10 Xtags3.txt      /‚`‚a‚b
- abc11 Xtags3.txt      /‚`‚a‚b
- abc12 Xtags3.txt      /‚`‚a‚b
- abc13 Xtags3.txt      /‚`‚a‚b
- abc14 Xtags3.txt      /‚`‚a‚b
- abc15 Xtags3.txt      /‚`‚a‚b
- abc16 Xtags3.txt      /‚`‚a‚b
- abc17 Xtags3.txt      /‚`‚a‚b
- abc18 Xtags3.txt      /‚`‚a‚b
- abc19 Xtags3.txt      /‚`‚a‚b
- abc20 Xtags3.txt      /‚`‚a‚b
- abc21 Xtags3.txt      /‚`‚a‚b
- abc22 Xtags3.txt      /‚`‚a‚b
- abc23 Xtags3.txt      /‚`‚a‚b
- abc24 Xtags3.txt      /‚`‚a‚b
- abc25 Xtags3.txt      /‚`‚a‚b
- abc26 Xtags3.txt      /‚`‚a‚b
- abc27 Xtags3.txt      /‚`‚a‚b
- abc28 Xtags3.txt      /‚`‚a‚b
- abc29 Xtags3.txt      /‚`‚a‚b
- abc30 Xtags3.txt      /‚`‚a‚b
- abc31 Xtags3.txt      /‚`‚a‚b
- abc32 Xtags3.txt      /‚`‚a‚b
- abc33 Xtags3.txt      /‚`‚a‚b
- abc34 Xtags3.txt      /‚`‚a‚b
- abc35 Xtags3.txt      /‚`‚a‚b
- abc36 Xtags3.txt      /‚`‚a‚b
- abc37 Xtags3.txt      /‚`‚a‚b
- abc38 Xtags3.txt      /‚`‚a‚b
- abc39 Xtags3.txt      /‚`‚a‚b
- abc40 Xtags3.txt      /‚`‚a‚b
- abc41 Xtags3.txt      /‚`‚a‚b
- abc42 Xtags3.txt      /‚`‚a‚b
- abc43 Xtags3.txt      /‚`‚a‚b
- abc44 Xtags3.txt      /‚`‚a‚b
- abc45 Xtags3.txt      /‚`‚a‚b
- abc46 Xtags3.txt      /‚`‚a‚b
- abc47 Xtags3.txt      /‚`‚a‚b
- abc48 Xtags3.txt      /‚`‚a‚b
- abc49 Xtags3.txt      /‚`‚a‚b
- abc50 Xtags3.txt      /‚`‚a‚b
- abc51 Xtags3.txt      /‚`‚a‚b
- abc52 Xtags3.txt      /‚`‚a‚b
- abc53 Xtags3.txt      /‚`‚a‚b
- abc54 Xtags3.txt      /‚`‚a‚b
- abc55 Xtags3.txt      /‚`‚a‚b
- abc56 Xtags3.txt      /‚`‚a‚b
- abc57 Xtags3.txt      /‚`‚a‚b
- abc58 Xtags3.txt      /‚`‚a‚b
- abc59 Xtags3.txt      /‚`‚a‚b
- abc60 Xtags3.txt      /‚`‚a‚b
- abc61 Xtags3.txt      /‚`‚a‚b
- abc62 Xtags3.txt      /‚`‚a‚b
- abc63 Xtags3.txt      /‚`‚a‚b
- abc64 Xtags3.txt      /‚`‚a‚b
- abc65 Xtags3.txt      /‚`‚a‚b
- abc66 Xtags3.txt      /‚`‚a‚b
- abc67 Xtags3.txt      /‚`‚a‚b
- abc68 Xtags3.txt      /‚`‚a‚b
- abc69 Xtags3.txt      /‚`‚a‚b
- abc70 Xtags3.txt      /‚`‚a‚b
- abc71 Xtags3.txt      /‚`‚a‚b
- abc72 Xtags3.txt      /‚`‚a‚b
- abc73 Xtags3.txt      /‚`‚a‚b
- abc74 Xtags3.txt      /‚`‚a‚b
- abc75 Xtags3.txt      /‚`‚a‚b
- abc76 Xtags3.txt      /‚`‚a‚b
- abc77 Xtags3.txt      /‚`‚a‚b
- abc78 Xtags3.txt      /‚`‚a‚b
- abc79 Xtags3.txt      /‚`‚a‚b
- abc80 Xtags3.txt      /‚`‚a‚b
- abc81 Xtags3.txt      /‚`‚a‚b
- abc82 Xtags3.txt      /‚`‚a‚b
- abc83 Xtags3.txt      /‚`‚a‚b
- abc84 Xtags3.txt      /‚`‚a‚b
- abc85 Xtags3.txt      /‚`‚a‚b
- abc86 Xtags3.txt      /‚`‚a‚b
- abc87 Xtags3.txt      /‚`‚a‚b
- abc88 Xtags3.txt      /‚`‚a‚b
- abc89 Xtags3.txt      /‚`‚a‚b
- abc90 Xtags3.txt      /‚`‚a‚b
- abc91 Xtags3.txt      /‚`‚a‚b
- abc92 Xtags3.txt      /‚`‚a‚b
- abc93 Xtags3.txt      /‚`‚a‚b
- abc94 Xtags3.txt      /‚`‚a‚b
- abc95 Xtags3.txt      /‚`‚a‚b
- abc96 Xtags3.txt      /‚`‚a‚b
- abc97 Xtags3.txt      /‚`‚a‚b
- abc98 Xtags3.txt      /‚`‚a‚b
- abc99 Xtags3.txt      /‚`‚a‚b
- abc100        Xtags3.txt      /‚`‚a‚b
--- 0 ----
*** ../vim-8.2.4511/src/testdir/test_tagjump.vim        2022-03-02 
20:29:31.610116664 +0000
--- src/testdir/test_tagjump.vim        2022-03-05 14:32:34.779107820 +0000
***************
*** 230,236 ****
  endfunc
  
  " Tests for tag search with !_TAG_FILE_ENCODING.
- " Depends on the test83-tags2 and test83-tags3 files.
  func Test_tag_file_encoding()
    if has('vms')
      throw 'Skipped: does not work on VMS'
--- 230,235 ----
***************
*** 262,279 ****
  
    " case2:
    new
!   set tags=test83-tags2
    tag /.BC
    call assert_equal('Xtags2.txt', expand('%:t'))
    call assert_equal('ABC', getline('.'))
    close
  
    " case3:
    new
!   set tags=test83-tags3
    tag abc50
    call assert_equal('Xtags3.txt', expand('%:t'))
    call assert_equal('ABC', getline('.'))
    close
  
    set tags&
--- 261,291 ----
  
    " case2:
    new
!   let content = ['!_TAG_FILE_ENCODING cp932   //',
!         \ "\x82`\x82a\x82b    Xtags2.txt      /\x82`\x82a\x82b"]
!   call writefile(content, 'Xtags')
!   set tags=Xtags
    tag /.BC
    call assert_equal('Xtags2.txt', expand('%:t'))
    call assert_equal('ABC', getline('.'))
+   call delete('Xtags')
    close
  
    " case3:
    new
!   let contents = [
!       \ "!_TAG_FILE_SORTED    1       //",
!       \ "!_TAG_FILE_ENCODING  cp932   //"]
!   for i in range(1, 100)
!       call add(contents, 'abc' .. i
!             \ .. "    Xtags3.txt      /\x82`\x82a\x82b")
!   endfor
!   call writefile(contents, 'Xtags')
!   set tags=Xtags
    tag abc50
    call assert_equal('Xtags3.txt', expand('%:t'))
    call assert_equal('ABC', getline('.'))
+   call delete('Xtags')
    close
  
    set tags&
***************
*** 324,329 ****
--- 336,342 ----
          \ ], 'Xtags2')
    tag main
    call assert_equal(2, line('.'))
+   call assert_fails('tag bar', 'E426:')
  
    " corrupted tag line
    call writefile([
***************
*** 349,354 ****
--- 362,388 ----
        \ ], 'Xtags')
    call assert_fails('tag foo', 'E431:')
  
+   " end of file after a CTRL-L line
+   call writefile([
+       \ "\x0c",
+         \ "Xmain.c,64",
+         \ "void foo() {}\x7ffoo\x011,0",
+       \ "\x0c",
+       \ ], 'Xtags')
+   call assert_fails('tag main', 'E426:')
+ 
+   " error in an included tags file
+   call writefile([
+         \ "\x0c",
+         \ "Xtags2,include"
+         \ ], 'Xtags')
+   call writefile([
+         \ "\x0c",
+         \ "Xmain.c,64",
+         \ "void foo() {}",
+         \ ], 'Xtags2')
+   call assert_fails('tag foo', 'E431:')
+ 
    call delete('Xtags')
    call delete('Xtags2')
    call delete('Xmain.c')
***************
*** 1432,1437 ****
--- 1466,1476 ----
          \ "foo Xfile 1"], 'Xtags')
    call assert_fails('tag foo', 'E431:')
  
+   " file name and search pattern are not separated by a tab
+   call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+         \ "foo\tXfile 1;"], 'Xtags')
+   call assert_fails('tag foo', 'E431:')
+ 
    call delete('Xtags')
    call delete('Xfile')
    set tags&
*** ../vim-8.2.4511/src/version.c       2022-03-05 13:45:52.736741405 +0000
--- src/version.c       2022-03-05 14:34:27.099161708 +0000
***************
*** 756,757 ****
--- 756,759 ----
  {   /* Add new patch number below this line */
+ /**/
+     4512,
  /**/

-- 
hundred-and-one symptoms of being an internet addict:
171. You invent another person and chat with yourself in empty chat rooms.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220305143557.815981C0403%40moolenaar.net.

Raspunde prin e-mail lui