neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

tag.c (110101B)


      1 // Code to handle tags and the tag stack
      2 
      3 #include <assert.h>
      4 #include <ctype.h>
      5 #include <inttypes.h>
      6 #include <stdbool.h>
      7 #include <stdio.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "nvim/ascii_defs.h"
     12 #include "nvim/autocmd.h"
     13 #include "nvim/autocmd_defs.h"
     14 #include "nvim/buffer.h"
     15 #include "nvim/buffer_defs.h"
     16 #include "nvim/charset.h"
     17 #include "nvim/cmdexpand.h"
     18 #include "nvim/cmdexpand_defs.h"
     19 #include "nvim/cursor.h"
     20 #include "nvim/drawscreen.h"
     21 #include "nvim/errors.h"
     22 #include "nvim/eval.h"
     23 #include "nvim/eval/typval.h"
     24 #include "nvim/eval/vars.h"
     25 #include "nvim/ex_cmds.h"
     26 #include "nvim/ex_cmds_defs.h"
     27 #include "nvim/ex_docmd.h"
     28 #include "nvim/file_search.h"
     29 #include "nvim/fileio.h"
     30 #include "nvim/fold.h"
     31 #include "nvim/garray.h"
     32 #include "nvim/garray_defs.h"
     33 #include "nvim/gettext_defs.h"
     34 #include "nvim/globals.h"
     35 #include "nvim/hashtab.h"
     36 #include "nvim/hashtab_defs.h"
     37 #include "nvim/help.h"
     38 #include "nvim/highlight_defs.h"
     39 #include "nvim/input.h"
     40 #include "nvim/insexpand.h"
     41 #include "nvim/macros_defs.h"
     42 #include "nvim/mark.h"
     43 #include "nvim/mark_defs.h"
     44 #include "nvim/mbyte.h"
     45 #include "nvim/mbyte_defs.h"
     46 #include "nvim/memory.h"
     47 #include "nvim/message.h"
     48 #include "nvim/move.h"
     49 #include "nvim/option.h"
     50 #include "nvim/option_defs.h"
     51 #include "nvim/option_vars.h"
     52 #include "nvim/optionstr.h"
     53 #include "nvim/os/fs.h"
     54 #include "nvim/os/input.h"
     55 #include "nvim/os/os_defs.h"
     56 #include "nvim/os/time.h"
     57 #include "nvim/path.h"
     58 #include "nvim/pos_defs.h"
     59 #include "nvim/quickfix.h"
     60 #include "nvim/regexp.h"
     61 #include "nvim/regexp_defs.h"
     62 #include "nvim/runtime.h"
     63 #include "nvim/search.h"
     64 #include "nvim/state_defs.h"
     65 #include "nvim/strings.h"
     66 #include "nvim/tag.h"
     67 #include "nvim/types_defs.h"
     68 #include "nvim/ui.h"
     69 #include "nvim/vim_defs.h"
     70 #include "nvim/window.h"
     71 
     72 // Structure to hold pointers to various items in a tag line.
     73 typedef struct {
     74  // filled in by parse_tag_line():
     75  char *tagname;        // start of tag name (skip "file:")
     76  char *tagname_end;    // char after tag name
     77  char *fname;          // first char of file name
     78  char *fname_end;      // char after file name
     79  char *command;        // first char of command
     80  // filled in by parse_match():
     81  char *command_end;    // first char after command
     82  char *tag_fname;      // file name of the tags file. This is used
     83  // when 'tr' is set.
     84  char *tagkind;          // "kind:" value
     85  char *tagkind_end;      // end of tagkind
     86  char *user_data;        // user_data string
     87  char *user_data_end;    // end of user_data
     88  linenr_T tagline;       // "line:" value
     89 } tagptrs_T;
     90 
     91 // Structure to hold info about the tag pattern being used.
     92 typedef struct {
     93  char *pat;            // the pattern
     94  int len;              // length of pat[]
     95  char *head;           // start of pattern head
     96  int headlen;          // length of head[]
     97  regmatch_T regmatch;  // regexp program, may be NULL
     98 } pat_T;
     99 
    100 // The matching tags are first stored in one of the hash tables.  In
    101 // which one depends on the priority of the match.
    102 // ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
    103 // At the end, the matches from ga_match[] are concatenated, to make a list
    104 // sorted on priority.
    105 enum {
    106  MT_ST_CUR = 0,  // static match in current file
    107  MT_GL_CUR = 1,  // global match in current file
    108  MT_GL_OTH = 2,  // global match in other file
    109  MT_ST_OTH = 3,  // static match in other file
    110  MT_IC_OFF = 4,  // add for icase match
    111  MT_RE_OFF = 8,  // add for regexp match
    112  MT_MASK = 7,    // mask for printing priority
    113  MT_COUNT = 16,
    114 };
    115 
    116 static char *mt_names[MT_COUNT/2] =
    117 { "FSC", "F C", "F  ", "FS ", " SC", "  C", "   ", " S " };
    118 
    119 #define NOTAGFILE       99              // return value for jumpto_tag
    120 static char *nofile_fname = NULL;       // fname for NOTAGFILE error
    121 
    122 /// Return values used when reading lines from a tags file.
    123 typedef enum {
    124  TAGS_READ_SUCCESS = 1,
    125  TAGS_READ_EOF,
    126  TAGS_READ_IGNORE,
    127 } tags_read_status_T;
    128 
    129 /// States used during a tags search
    130 typedef enum {
    131  TS_START,         ///< at start of file
    132  TS_LINEAR,        ///< linear searching forward, till EOF
    133  TS_BINARY,        ///< binary searching
    134  TS_SKIP_BACK,     ///< skipping backwards
    135  TS_STEP_FORWARD,  ///< stepping forwards
    136 } tagsearch_state_T;
    137 
    138 /// Binary search file offsets in a tags file
    139 typedef struct {
    140  off_T low_offset;        ///< offset for first char of first line that
    141                           ///< could match
    142  off_T high_offset;       ///< offset of char after last line that could
    143                           ///< match
    144  off_T curr_offset;       ///< Current file offset in search range
    145  off_T curr_offset_used;  ///< curr_offset used when skipping back
    146  off_T match_offset;      ///< Where the binary search found a tag
    147  int low_char;            ///< first char at low_offset
    148  int high_char;           ///< first char at high_offset
    149 } tagsearch_info_T;
    150 
    151 /// Return values used when matching tags against a pattern.
    152 typedef enum {
    153  TAG_MATCH_SUCCESS = 1,
    154  TAG_MATCH_FAIL,
    155  TAG_MATCH_STOP,
    156  TAG_MATCH_NEXT,
    157 } tagmatch_status_T;
    158 
    159 /// Arguments used for matching tags read from a tags file against a pattern.
    160 typedef struct {
    161  int matchoff;      ///< tag match offset
    162  bool match_re;     ///< true if the tag matches a regexp
    163  bool match_no_ic;  ///< true if the tag matches with case
    164  bool has_re;       ///< regular expression used
    165  bool sortic;       ///< tags file sorted ignoring case (foldcase)
    166  bool sort_error;   ///< tags file not sorted
    167 } findtags_match_args_T;
    168 
    169 /// State information used during a tag search
    170 typedef struct {
    171  tagsearch_state_T state;       ///< tag search state
    172  bool stop_searching;           ///< stop when match found or error
    173  pat_T *orgpat;                 ///< holds unconverted pattern info
    174  char *lbuf;                    ///< line buffer
    175  int lbuf_size;                 ///< length of lbuf
    176  char *tag_fname;               ///< name of the tag file
    177  FILE *fp;                      ///< current tags file pointer
    178  int flags;                     ///< flags used for tag search
    179  int tag_file_sorted;           ///< !_TAG_FILE_SORTED value
    180  bool get_searchpat;            ///< used for 'showfulltag'
    181  bool help_only;                ///< only search for help tags
    182  bool did_open;                 ///< did open a tag file
    183  int mincount;                  ///< MAXCOL: find all matches
    184                                 ///< other: minimal number of matches
    185  bool linear;                   ///< do a linear search
    186  vimconv_T vimconv;
    187  char help_lang[3];             ///< lang of current tags file
    188  int help_pri;                  ///< help language priority
    189  char *help_lang_find;          ///< lang to be found
    190  bool is_txt;                   ///< flag of file extension
    191  int match_count;               ///< number of matches found
    192  garray_T ga_match[MT_COUNT];   ///< stores matches in sequence
    193  hashtab_T ht_match[MT_COUNT];  ///< stores matches by key
    194 } findtags_state_T;
    195 
    196 #include "tag.c.generated.h"
    197 
    198 static const char e_tag_stack_empty[]
    199  = N_("E73: Tag stack empty");
    200 static const char e_tag_not_found_str[]
    201  = N_("E426: Tag not found: %s");
    202 static const char e_at_bottom_of_tag_stack[]
    203  = N_("E555: At bottom of tag stack");
    204 static const char e_at_top_of_tag_stack[]
    205  = N_("E556: At top of tag stack");
    206 static const char e_cannot_modify_tag_stack_within_tagfunc[]
    207  = N_("E986: Cannot modify the tag stack within tagfunc");
    208 static const char e_invalid_return_value_from_tagfunc[]
    209  = N_("E987: Invalid return value from tagfunc");
    210 static const char e_window_unexpectedly_close_while_searching_for_tags[]
    211  = N_("E1299: Window unexpectedly closed while searching for tags");
    212 
    213 static char *tagmatchname = NULL;   // name of last used tag
    214 
    215 // Tag for preview window is remembered separately, to avoid messing up the
    216 // normal tagstack.
    217 static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
    218 
    219 static bool tfu_in_use = false;  // disallow recursive call of tagfunc
    220 static Callback tfu_cb;          // 'tagfunc' callback function
    221 
    222 // Used instead of NUL to separate tag fields in the growarrays.
    223 #define TAG_SEP 0x02
    224 
    225 /// Reads the 'tagfunc' option value and convert that to a callback value.
    226 /// Invoked when the 'tagfunc' option is set. The option value can be a name of
    227 /// a function (string), or function(<name>) or funcref(<name>) or a lambda.
    228 const char *did_set_tagfunc(optset_T *args)
    229 {
    230  buf_T *buf = (buf_T *)args->os_buf;
    231  int retval;
    232 
    233  if (args->os_flags & OPT_LOCAL) {
    234    retval = option_set_callback_func(args->os_newval.string.data, &buf->b_tfu_cb);
    235  } else {
    236    retval = option_set_callback_func(args->os_newval.string.data, &tfu_cb);
    237    if (retval == OK && !(args->os_flags & OPT_GLOBAL)) {
    238      set_buflocal_tfu_callback(buf);
    239    }
    240  }
    241 
    242  return retval == FAIL ? e_invarg : NULL;
    243 }
    244 
    245 #if defined(EXITFREE)
    246 void free_tagfunc_option(void)
    247 {
    248  callback_free(&tfu_cb);
    249 }
    250 #endif
    251 
    252 /// Mark the global 'tagfunc' callback with "copyID" so that it is not garbage
    253 /// collected.
    254 bool set_ref_in_tagfunc(int copyID)
    255 {
    256  return set_ref_in_callback(&tfu_cb, copyID, NULL, NULL);
    257 }
    258 
    259 /// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
    260 /// callback for 'buf'.
    261 void set_buflocal_tfu_callback(buf_T *buf)
    262 {
    263  callback_free(&buf->b_tfu_cb);
    264  if (tfu_cb.type != kCallbackNone) {
    265    callback_copy(&buf->b_tfu_cb, &tfu_cb);
    266  }
    267 }
    268 
    269 /// Jump to tag; handling of tag commands and tag stack
    270 ///
    271 /// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack
    272 ///
    273 /// type == DT_TAG:      ":tag [tag]", jump to newer position or same tag again
    274 /// type == DT_HELP:     like DT_TAG, but don't use regexp.
    275 /// type == DT_POP:      ":pop" or CTRL-T, jump to old position
    276 /// type == DT_NEXT:     jump to next match of same tag
    277 /// type == DT_PREV:     jump to previous match of same tag
    278 /// type == DT_FIRST:    jump to first match of same tag
    279 /// type == DT_LAST:     jump to last match of same tag
    280 /// type == DT_SELECT:   ":tselect [tag]", select tag from a list of all matches
    281 /// type == DT_JUMP:     ":tjump [tag]", jump to tag or select tag from a list
    282 /// type == DT_LTAG:     use location list for displaying tag matches
    283 /// type == DT_FREE:     free cached matches
    284 ///
    285 /// @param tag  tag (pattern) to jump to
    286 /// @param forceit  :ta with !
    287 /// @param verbose  print "tag not found" message
    288 void do_tag(char *tag, int type, int count, int forceit, bool verbose)
    289 {
    290  taggy_T *tagstack = curwin->w_tagstack;
    291  int tagstackidx = curwin->w_tagstackidx;
    292  int tagstacklen = curwin->w_tagstacklen;
    293  int cur_match = 0;
    294  int cur_fnum = curbuf->b_fnum;
    295  int oldtagstackidx = tagstackidx;
    296  int prevtagstackidx = tagstackidx;
    297  bool new_tag = false;
    298  bool no_regexp = false;
    299  int error_cur_match = 0;
    300  bool save_pos = false;
    301  fmark_T saved_fmark;
    302  int new_num_matches;
    303  char **new_matches;
    304  bool use_tagstack;
    305  bool skip_msg = false;
    306  char *buf_ffname = curbuf->b_ffname;  // name for priority computation
    307  bool use_tfu = true;
    308  char *tofree = NULL;
    309 
    310  // remember the matches for the last used tag
    311  static int num_matches = 0;
    312  static int max_num_matches = 0;             // limit used for match search
    313  static char **matches = NULL;
    314  static int flags;
    315 
    316 #ifdef EXITFREE
    317  if (type == DT_FREE) {
    318    // remove the list of matches
    319    FreeWild(num_matches, matches);
    320    num_matches = 0;
    321    return;
    322  }
    323 #endif
    324 
    325  if (tfu_in_use) {
    326    emsg(_(e_cannot_modify_tag_stack_within_tagfunc));
    327    return;
    328  }
    329 
    330  if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) {
    331    return;
    332  }
    333 
    334  if (type == DT_HELP) {
    335    type = DT_TAG;
    336    no_regexp = true;
    337    use_tfu = false;
    338  }
    339 
    340  int prev_num_matches = num_matches;
    341  free_string_option(nofile_fname);
    342  nofile_fname = NULL;
    343 
    344  clearpos(&saved_fmark.mark);          // shutup gcc 4.0
    345  saved_fmark.fnum = 0;
    346  saved_fmark.view = (fmarkv_T)INIT_FMARKV;
    347 
    348  // Don't add a tag to the tagstack if 'tagstack' has been reset.
    349  assert(tag != NULL);
    350  if (!p_tgst && *tag != NUL) {
    351    use_tagstack = false;
    352    new_tag = true;
    353    if (g_do_tagpreview != 0) {
    354      tagstack_clear_entry(&ptag_entry);
    355      ptag_entry.tagname = xstrdup(tag);
    356    }
    357  } else {
    358    if (g_do_tagpreview != 0) {
    359      use_tagstack = false;
    360    } else {
    361      use_tagstack = true;
    362    }
    363 
    364    // new pattern, add to the tag stack
    365    if (*tag != NUL
    366        && (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
    367            || type == DT_LTAG)) {
    368      if (g_do_tagpreview != 0) {
    369        if (ptag_entry.tagname != NULL
    370            && strcmp(ptag_entry.tagname, tag) == 0) {
    371          // Jumping to same tag: keep the current match, so that
    372          // the CursorHold autocommand example works.
    373          cur_match = ptag_entry.cur_match;
    374          cur_fnum = ptag_entry.cur_fnum;
    375        } else {
    376          tagstack_clear_entry(&ptag_entry);
    377          ptag_entry.tagname = xstrdup(tag);
    378        }
    379      } else {
    380        // If the last used entry is not at the top, delete all tag
    381        // stack entries above it.
    382        while (tagstackidx < tagstacklen) {
    383          tagstack_clear_entry(&tagstack[--tagstacklen]);
    384        }
    385 
    386        // if the tagstack is full: remove oldest entry
    387        if (++tagstacklen > TAGSTACKSIZE) {
    388          tagstacklen = TAGSTACKSIZE;
    389          tagstack_clear_entry(&tagstack[0]);
    390          for (int i = 1; i < tagstacklen; i++) {
    391            tagstack[i - 1] = tagstack[i];
    392          }
    393          tagstack[--tagstackidx].user_data = NULL;
    394        }
    395 
    396        // put the tag name in the tag stack
    397        tagstack[tagstackidx].tagname = xstrdup(tag);
    398 
    399        curwin->w_tagstacklen = tagstacklen;
    400 
    401        save_pos = true;                // save the cursor position below
    402      }
    403 
    404      new_tag = true;
    405    } else {
    406      if (g_do_tagpreview != 0 ? ptag_entry.tagname == NULL
    407                               : tagstacklen == 0) {
    408        // empty stack
    409        emsg(_(e_tag_stack_empty));
    410        goto end_do_tag;
    411      }
    412 
    413      if (type == DT_POP) {             // go to older position
    414        const bool old_KeyTyped = KeyTyped;
    415        if ((tagstackidx -= count) < 0) {
    416          emsg(_(e_at_bottom_of_tag_stack));
    417          if (tagstackidx + count == 0) {
    418            // We did [num]^T from the bottom of the stack
    419            tagstackidx = 0;
    420            goto end_do_tag;
    421          }
    422          // We weren't at the bottom of the stack, so jump all the
    423          // way to the bottom now.
    424          tagstackidx = 0;
    425        } else if (tagstackidx >= tagstacklen) {        // count == 0?
    426          emsg(_(e_at_top_of_tag_stack));
    427          goto end_do_tag;
    428        }
    429 
    430        // Make a copy of the fmark, autocommands may invalidate the
    431        // tagstack before it's used.
    432        saved_fmark = tagstack[tagstackidx].fmark;
    433        if (saved_fmark.fnum != curbuf->b_fnum) {
    434          // Jump to other file. If this fails (e.g. because the
    435          // file was changed) keep original position in tag stack.
    436          if (buflist_getfile(saved_fmark.fnum, saved_fmark.mark.lnum,
    437                              GETF_SETMARK, forceit) == FAIL) {
    438            tagstackidx = oldtagstackidx;              // back to old posn
    439            goto end_do_tag;
    440          }
    441          // A BufReadPost autocommand may jump to the '" mark, but
    442          // we don't what that here.
    443          curwin->w_cursor.lnum = saved_fmark.mark.lnum;
    444        } else {
    445          setpcmark();
    446          curwin->w_cursor.lnum = saved_fmark.mark.lnum;
    447        }
    448        curwin->w_cursor.col = saved_fmark.mark.col;
    449        curwin->w_set_curswant = true;
    450        if (jop_flags & kOptJopFlagView) {
    451          mark_view_restore(&saved_fmark);
    452        }
    453        check_cursor(curwin);
    454        if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
    455          foldOpenCursor();
    456        }
    457 
    458        // remove the old list of matches
    459        FreeWild(num_matches, matches);
    460        num_matches = 0;
    461        tag_freematch();
    462        goto end_do_tag;
    463      }
    464 
    465      if (type == DT_TAG
    466          || type == DT_LTAG) {
    467        if (g_do_tagpreview != 0) {
    468          cur_match = ptag_entry.cur_match;
    469          cur_fnum = ptag_entry.cur_fnum;
    470        } else {
    471          // ":tag" (no argument): go to newer pattern
    472          save_pos = true;              // save the cursor position below
    473          if ((tagstackidx += count - 1) >= tagstacklen) {
    474            // Beyond the last one, just give an error message and
    475            // go to the last one.  Don't store the cursor
    476            // position.
    477            tagstackidx = tagstacklen - 1;
    478            emsg(_(e_at_top_of_tag_stack));
    479            save_pos = false;
    480          } else if (tagstackidx < 0) {         // must have been count == 0
    481            emsg(_(e_at_bottom_of_tag_stack));
    482            tagstackidx = 0;
    483            goto end_do_tag;
    484          }
    485          cur_match = tagstack[tagstackidx].cur_match;
    486          cur_fnum = tagstack[tagstackidx].cur_fnum;
    487        }
    488        new_tag = true;
    489      } else {                                // go to other matching tag
    490        // Save index for when selection is cancelled.
    491        prevtagstackidx = tagstackidx;
    492 
    493        if (g_do_tagpreview != 0) {
    494          cur_match = ptag_entry.cur_match;
    495          cur_fnum = ptag_entry.cur_fnum;
    496        } else {
    497          if (--tagstackidx < 0) {
    498            tagstackidx = 0;
    499          }
    500          cur_match = tagstack[tagstackidx].cur_match;
    501          cur_fnum = tagstack[tagstackidx].cur_fnum;
    502        }
    503        switch (type) {
    504        case DT_FIRST:
    505          cur_match = count - 1; break;
    506        case DT_SELECT:
    507        case DT_JUMP:
    508        case DT_LAST:
    509          cur_match = MAXCOL - 1; break;
    510        case DT_NEXT:
    511          cur_match += count; break;
    512        case DT_PREV:
    513          cur_match -= count; break;
    514        }
    515        if (cur_match >= MAXCOL) {
    516          cur_match = MAXCOL - 1;
    517        } else if (cur_match < 0) {
    518          emsg(_("E425: Cannot go before first matching tag"));
    519          skip_msg = true;
    520          cur_match = 0;
    521          cur_fnum = curbuf->b_fnum;
    522        }
    523      }
    524    }
    525 
    526    if (g_do_tagpreview != 0) {
    527      if (type != DT_SELECT && type != DT_JUMP) {
    528        ptag_entry.cur_match = cur_match;
    529        ptag_entry.cur_fnum = cur_fnum;
    530      }
    531    } else {
    532      // For ":tag [arg]" or ":tselect" remember position before the jump.
    533      saved_fmark = tagstack[tagstackidx].fmark;
    534      if (save_pos) {
    535        tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
    536        tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum;
    537        tagstack[tagstackidx].fmark.view = mark_view_make(curwin->w_topline, curwin->w_cursor);
    538      }
    539 
    540      // Curwin will change in the call to jumpto_tag() if ":stag" was
    541      // used or an autocommand jumps to another window; store value of
    542      // tagstackidx now.
    543      curwin->w_tagstackidx = tagstackidx;
    544      if (type != DT_SELECT && type != DT_JUMP) {
    545        curwin->w_tagstack[tagstackidx].cur_match = cur_match;
    546        curwin->w_tagstack[tagstackidx].cur_fnum = cur_fnum;
    547      }
    548    }
    549  }
    550 
    551  // When not using the current buffer get the name of buffer "cur_fnum".
    552  // Makes sure that the tag order doesn't change when using a remembered
    553  // position for "cur_match".
    554  if (cur_fnum != curbuf->b_fnum) {
    555    buf_T *buf = buflist_findnr(cur_fnum);
    556 
    557    if (buf != NULL) {
    558      buf_ffname = buf->b_ffname;
    559    }
    560  }
    561 
    562  // Repeat searching for tags, when a file has not been found.
    563  while (true) {
    564    char *name;
    565 
    566    // When desired match not found yet, try to find it (and others).
    567    if (use_tagstack) {
    568      // make a copy, the tagstack may change in 'tagfunc'
    569      name = xstrdup(tagstack[tagstackidx].tagname);
    570      xfree(tofree);
    571      tofree = name;
    572    } else if (g_do_tagpreview != 0) {
    573      name = ptag_entry.tagname;
    574    } else {
    575      name = tag;
    576    }
    577    bool other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0);
    578    if (new_tag
    579        || (cur_match >= num_matches && max_num_matches != MAXCOL)
    580        || other_name) {
    581      if (other_name) {
    582        xfree(tagmatchname);
    583        tagmatchname = xstrdup(name);
    584      }
    585 
    586      if (type == DT_SELECT || type == DT_JUMP
    587          || type == DT_LTAG) {
    588        cur_match = MAXCOL - 1;
    589      }
    590      max_num_matches = type == DT_TAG ? MAXCOL : cur_match + 1;
    591 
    592      // when the argument starts with '/', use it as a regexp
    593      if (!no_regexp && *name == '/') {
    594        flags = TAG_REGEXP;
    595        name++;
    596      } else {
    597        flags = TAG_NOIC;
    598      }
    599 
    600      flags |= verbose ? TAG_VERBOSE : 0;
    601      flags |= !use_tfu ? TAG_NO_TAGFUNC : 0;
    602 
    603      if (find_tags(name, &new_num_matches, &new_matches, flags,
    604                    max_num_matches, buf_ffname) == OK
    605          && new_num_matches < max_num_matches) {
    606        max_num_matches = MAXCOL;  // If less than max_num_matches
    607                                   // found: all matches found.
    608      }
    609 
    610      // A tag function may do anything, which may cause various
    611      // information to become invalid.  At least check for the tagstack
    612      // to still be the same.
    613      if (tagstack != curwin->w_tagstack) {
    614        emsg(_(e_window_unexpectedly_close_while_searching_for_tags));
    615        FreeWild(new_num_matches, new_matches);
    616        break;
    617      }
    618 
    619      // If there already were some matches for the same name, move them
    620      // to the start.  Avoids that the order changes when using
    621      // ":tnext" and jumping to another file.
    622      if (!new_tag && !other_name) {
    623        int idx = 0;
    624        tagptrs_T tagp, tagp2;
    625 
    626        // Find the position of each old match in the new list.  Need
    627        // to use parse_match() to find the tag line.
    628        for (int j = 0; j < num_matches; j++) {
    629          parse_match(matches[j], &tagp);
    630          for (int i = idx; i < new_num_matches; i++) {
    631            parse_match(new_matches[i], &tagp2);
    632            if (strcmp(tagp.tagname, tagp2.tagname) == 0) {
    633              char *p = new_matches[i];
    634              for (int k = i; k > idx; k--) {
    635                new_matches[k] = new_matches[k - 1];
    636              }
    637              new_matches[idx++] = p;
    638              break;
    639            }
    640          }
    641        }
    642      }
    643      FreeWild(num_matches, matches);
    644      num_matches = new_num_matches;
    645      matches = new_matches;
    646    }
    647 
    648    if (num_matches <= 0) {
    649      if (verbose) {
    650        semsg(_(e_tag_not_found_str), name);
    651      }
    652      g_do_tagpreview = 0;
    653    } else {
    654      bool ask_for_selection = false;
    655 
    656      if (type == DT_TAG && *tag != NUL) {
    657        // If a count is supplied to the ":tag <name>" command, then
    658        // jump to count'th matching tag.
    659        cur_match = count > 0 ? count - 1 : 0;
    660      } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) {
    661        print_tag_list(new_tag, use_tagstack, num_matches, matches);
    662        ask_for_selection = true;
    663      } else if (type == DT_LTAG) {
    664        if (add_llist_tags(tag, num_matches, matches) == FAIL) {
    665          goto end_do_tag;
    666        }
    667 
    668        cur_match = 0;                  // Jump to the first tag
    669      }
    670 
    671      if (ask_for_selection) {
    672        // Ask to select a tag from the list.
    673        int i = prompt_for_input(NULL, 0, false, NULL);
    674        if (i <= 0 || i > num_matches || got_int) {
    675          // no valid choice: don't change anything
    676          if (use_tagstack) {
    677            tagstack[tagstackidx].fmark = saved_fmark;
    678            tagstackidx = prevtagstackidx;
    679          }
    680          break;
    681        }
    682        cur_match = i - 1;
    683      }
    684 
    685      if (cur_match >= num_matches) {
    686        // Avoid giving this error when a file wasn't found and we're
    687        // looking for a match in another file, which wasn't found.
    688        // There will be an emsg("file doesn't exist") below then.
    689        if ((type == DT_NEXT || type == DT_FIRST)
    690            && nofile_fname == NULL) {
    691          if (num_matches == 1) {
    692            emsg(_("E427: There is only one matching tag"));
    693          } else {
    694            emsg(_("E428: Cannot go beyond last matching tag"));
    695          }
    696          skip_msg = true;
    697        }
    698        cur_match = num_matches - 1;
    699      }
    700      if (use_tagstack) {
    701        tagptrs_T tagp2;
    702 
    703        tagstack[tagstackidx].cur_match = cur_match;
    704        tagstack[tagstackidx].cur_fnum = cur_fnum;
    705 
    706        // store user-provided data originating from tagfunc
    707        if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK
    708            && tagp2.user_data) {
    709          XFREE_CLEAR(tagstack[tagstackidx].user_data);
    710          tagstack[tagstackidx].user_data =
    711            xmemdupz(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data));
    712        }
    713 
    714        tagstackidx++;
    715      } else if (g_do_tagpreview != 0) {
    716        ptag_entry.cur_match = cur_match;
    717        ptag_entry.cur_fnum = cur_fnum;
    718      }
    719 
    720      // Only when going to try the next match, report that the previous
    721      // file didn't exist.  Otherwise an emsg() is given below.
    722      if (nofile_fname != NULL && error_cur_match != cur_match) {
    723        smsg(0, _("File \"%s\" does not exist"), nofile_fname);
    724      }
    725 
    726      bool ic = (matches[cur_match][0] & MT_IC_OFF);
    727      if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP
    728          && (num_matches > 1 || ic)
    729          && !skip_msg) {
    730        // Give an indication of the number of matching tags
    731        snprintf(IObuff, sizeof(IObuff), _("tag %d of %d%s"),
    732                 cur_match + 1,
    733                 num_matches,
    734                 max_num_matches != MAXCOL ? _(" or more") : "");
    735        if (ic) {
    736          xstrlcat(IObuff, _("  Using tag with different case!"), IOSIZE);
    737        }
    738        if ((num_matches > prev_num_matches || new_tag)
    739            && num_matches > 1) {
    740          msg(IObuff, ic ? HLF_W : 0);
    741          msg_scroll = true;  // Don't overwrite this message.
    742        } else {
    743          give_warning(IObuff, ic, true);
    744        }
    745        if (ic && !msg_scrolled && msg_silent == 0) {
    746          msg_delay(1007, true);
    747        }
    748      }
    749 
    750      // Let the SwapExists event know what tag we are jumping to.
    751      vim_snprintf(IObuff, IOSIZE, ":ta %s\r", name);
    752      set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1);
    753 
    754      // Jump to the desired match.
    755      int i = jumpto_tag(matches[cur_match], forceit, true);
    756 
    757      set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
    758 
    759      if (i == NOTAGFILE) {
    760        // File not found: try again with another matching tag
    761        if ((type == DT_PREV && cur_match > 0)
    762            || ((type == DT_TAG || type == DT_NEXT
    763                 || type == DT_FIRST)
    764                && (max_num_matches != MAXCOL
    765                    || cur_match < num_matches - 1))) {
    766          error_cur_match = cur_match;
    767          if (use_tagstack) {
    768            tagstackidx--;
    769          }
    770          if (type == DT_PREV) {
    771            cur_match--;
    772          } else {
    773            type = DT_NEXT;
    774            cur_match++;
    775          }
    776          continue;
    777        }
    778        semsg(_("E429: File \"%s\" does not exist"), nofile_fname);
    779      } else {
    780        // We may have jumped to another window, check that
    781        // tagstackidx is still valid.
    782        if (use_tagstack && tagstackidx > curwin->w_tagstacklen) {
    783          tagstackidx = curwin->w_tagstackidx;
    784        }
    785      }
    786    }
    787    break;
    788  }
    789 
    790 end_do_tag:
    791  // Only store the new index when using the tagstack and it's valid.
    792  if (use_tagstack && tagstackidx <= curwin->w_tagstacklen) {
    793    curwin->w_tagstackidx = tagstackidx;
    794  }
    795  postponed_split = 0;          // don't split next time
    796  g_do_tagpreview = 0;          // don't do tag preview next time
    797  xfree(tofree);
    798 }
    799 
    800 // List all the matching tags.
    801 static void print_tag_list(bool new_tag, bool use_tagstack, int num_matches, char **matches)
    802 {
    803  taggy_T *tagstack = curwin->w_tagstack;
    804  int tagstackidx = curwin->w_tagstackidx;
    805  tagptrs_T tagp;
    806 
    807  // Assume that the first match indicates how long the tags can
    808  // be, and align the file names to that.
    809  parse_match(matches[0], &tagp);
    810  int taglen = MAX((int)(tagp.tagname_end - tagp.tagname + 2), 18);
    811  if (taglen > Columns - 25) {
    812    taglen = MAXCOL;
    813  }
    814  if (msg_col == 0) {
    815    msg_didout = false;     // overwrite previous message
    816  }
    817  msg_ext_set_kind("confirm");
    818  msg_start();
    819  msg_puts_hl(_("  # pri kind tag"), HLF_T, false);
    820  msg_clr_eos();
    821  taglen_advance(taglen);
    822  msg_puts_hl(_("file\n"), HLF_T, false);
    823 
    824  for (int i = 0; i < num_matches && !got_int; i++) {
    825    parse_match(matches[i], &tagp);
    826    if (!new_tag && (
    827                     (g_do_tagpreview != 0
    828                      && i == ptag_entry.cur_match)
    829                     || (use_tagstack
    830                         && i == tagstack[tagstackidx].cur_match))) {
    831      *IObuff = '>';
    832    } else {
    833      *IObuff = ' ';
    834    }
    835    vim_snprintf(IObuff + 1, IOSIZE - 1,
    836                 "%2d %s ", i + 1,
    837                 mt_names[matches[i][0] & MT_MASK]);
    838    msg_puts(IObuff);
    839    if (tagp.tagkind != NULL) {
    840      msg_outtrans_len(tagp.tagkind, (int)(tagp.tagkind_end - tagp.tagkind), 0, false);
    841    }
    842    msg_advance(13);
    843    msg_outtrans_len(tagp.tagname, (int)(tagp.tagname_end - tagp.tagname), HLF_T, false);
    844    msg_putchar(' ');
    845    taglen_advance(taglen);
    846 
    847    // Find out the actual file name. If it is long, truncate
    848    // it and put "..." in the middle
    849    const char *p = tag_full_fname(&tagp);
    850    if (p != NULL) {
    851      msg_outtrans(p, HLF_D, false);
    852      XFREE_CLEAR(p);
    853    }
    854    if (msg_col > 0) {
    855      msg_putchar('\n');
    856    }
    857    if (got_int) {
    858      break;
    859    }
    860    msg_advance(15);
    861 
    862    // print any extra fields
    863    const char *command_end = tagp.command_end;
    864    if (command_end != NULL) {
    865      p = command_end + 3;
    866      while (*p && *p != '\r' && *p != '\n') {
    867        while (*p == TAB) {
    868          p++;
    869        }
    870 
    871        // skip "file:" without a value (static tag)
    872        if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
    873          p += 5;
    874          continue;
    875        }
    876        // skip "kind:<kind>" and "<kind>"
    877        if (p == tagp.tagkind
    878            || (p + 5 == tagp.tagkind
    879                && strncmp(p, "kind:", 5) == 0)) {
    880          p = tagp.tagkind_end;
    881          continue;
    882        }
    883        // print all other extra fields
    884        int hl_id = HLF_CM;
    885        while (*p && *p != '\r' && *p != '\n') {
    886          if (msg_col + ptr2cells(p) >= Columns) {
    887            msg_putchar('\n');
    888            if (got_int) {
    889              break;
    890            }
    891            msg_advance(15);
    892          }
    893          p = msg_outtrans_one(p, hl_id, false);
    894          if (*p == TAB) {
    895            msg_puts_hl(" ", hl_id, false);
    896            break;
    897          }
    898          if (*p == ':') {
    899            hl_id = 0;
    900          }
    901        }
    902      }
    903      if (msg_col > 15) {
    904        msg_putchar('\n');
    905        if (got_int) {
    906          break;
    907        }
    908        msg_advance(15);
    909      }
    910    } else {
    911      for (p = tagp.command;
    912           *p && *p != '\r' && *p != '\n';
    913           p++) {}
    914      command_end = p;
    915    }
    916 
    917    // Put the info (in several lines) at column 15.
    918    // Don't display "/^" and "?^".
    919    p = tagp.command;
    920    if (*p == '/' || *p == '?') {
    921      p++;
    922      if (*p == '^') {
    923        p++;
    924      }
    925    }
    926    // Remove leading whitespace from pattern
    927    while (p != command_end && ascii_isspace(*p)) {
    928      p++;
    929    }
    930 
    931    while (p != command_end) {
    932      if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) {
    933        msg_putchar('\n');
    934      }
    935      if (got_int) {
    936        break;
    937      }
    938      msg_advance(15);
    939 
    940      // skip backslash used for escaping a command char or
    941      // a backslash
    942      if (*p == '\\' && (*(p + 1) == *tagp.command
    943                         || *(p + 1) == '\\')) {
    944        p++;
    945      }
    946 
    947      if (*p == TAB) {
    948        msg_putchar(' ');
    949        p++;
    950      } else {
    951        p = msg_outtrans_one(p, 0, false);
    952      }
    953 
    954      // don't display the "$/;\"" and "$?;\""
    955      if (p == command_end - 2 && *p == '$'
    956          && *(p + 1) == *tagp.command) {
    957        break;
    958      }
    959      // don't display matching '/' or '?'
    960      if (p == command_end - 1 && *p == *tagp.command
    961          && (*p == '/' || *p == '?')) {
    962        break;
    963      }
    964    }
    965    if (msg_col && (!ui_has(kUIMessages) || i < num_matches - 1)) {
    966      msg_putchar('\n');
    967    }
    968    os_breakcheck();
    969  }
    970  if (got_int) {
    971    got_int = false;        // only stop the listing
    972  }
    973 }
    974 
    975 /// Add the matching tags to the location list for the current
    976 /// window.
    977 static int add_llist_tags(char *tag, int num_matches, char **matches)
    978 {
    979  char tag_name[128 + 1];
    980  tagptrs_T tagp;
    981 
    982  char *fname = xmalloc(MAXPATHL + 1);
    983  char *cmd = xmalloc(CMDBUFFSIZE + 1);
    984  list_T *list = tv_list_alloc(0);
    985 
    986  for (int i = 0; i < num_matches; i++) {
    987    dict_T *dict;
    988 
    989    parse_match(matches[i], &tagp);
    990 
    991    // Save the tag name
    992    int len = MIN((int)(tagp.tagname_end - tagp.tagname), 128);
    993    xmemcpyz(tag_name, tagp.tagname, (size_t)len);
    994    tag_name[len] = NUL;
    995 
    996    // Save the tag file name
    997    char *p = tag_full_fname(&tagp);
    998    if (p == NULL) {
    999      continue;
   1000    }
   1001    xstrlcpy(fname, p, MAXPATHL);
   1002    XFREE_CLEAR(p);
   1003 
   1004    // Get the line number or the search pattern used to locate
   1005    // the tag.
   1006    linenr_T lnum = 0;
   1007    if (isdigit((uint8_t)(*tagp.command))) {
   1008      // Line number is used to locate the tag
   1009      lnum = atoi(tagp.command);
   1010    } else {
   1011      // Search pattern is used to locate the tag
   1012 
   1013      // Locate the end of the command
   1014      char *cmd_start = tagp.command;
   1015      char *cmd_end = tagp.command_end;
   1016      if (cmd_end == NULL) {
   1017        for (p = tagp.command;
   1018             *p && *p != '\r' && *p != '\n'; p++) {}
   1019        cmd_end = p;
   1020      }
   1021 
   1022      // Now, cmd_end points to the character after the
   1023      // command. Adjust it to point to the last
   1024      // character of the command.
   1025      cmd_end--;
   1026 
   1027      // Skip the '/' and '?' characters at the
   1028      // beginning and end of the search pattern.
   1029      if (*cmd_start == '/' || *cmd_start == '?') {
   1030        cmd_start++;
   1031      }
   1032 
   1033      if (*cmd_end == '/' || *cmd_end == '?') {
   1034        cmd_end--;
   1035      }
   1036 
   1037      len = 0;
   1038      cmd[0] = NUL;
   1039 
   1040      // If "^" is present in the tag search pattern, then
   1041      // copy it first.
   1042      if (*cmd_start == '^') {
   1043        STRCPY(cmd, "^");
   1044        cmd_start++;
   1045        len++;
   1046      }
   1047 
   1048      // Precede the tag pattern with \V to make it very
   1049      // nomagic.
   1050      strcat(cmd, "\\V");
   1051      len += 2;
   1052 
   1053      int cmd_len = MIN((int)(cmd_end - cmd_start + 1), CMDBUFFSIZE - 5);
   1054      snprintf(cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
   1055               "%.*s", cmd_len, cmd_start);
   1056      len += cmd_len;
   1057 
   1058      if (cmd[len - 1] == '$') {
   1059        // Replace '$' at the end of the search pattern
   1060        // with '\$'
   1061        cmd[len - 1] = '\\';
   1062        cmd[len] = '$';
   1063        len++;
   1064      }
   1065 
   1066      cmd[len] = NUL;
   1067    }
   1068 
   1069    dict = tv_dict_alloc();
   1070    tv_list_append_dict(list, dict);
   1071 
   1072    tv_dict_add_str(dict, S_LEN("text"), tag_name);
   1073    tv_dict_add_str(dict, S_LEN("filename"), fname);
   1074    tv_dict_add_nr(dict, S_LEN("lnum"), lnum);
   1075    if (lnum == 0) {
   1076      tv_dict_add_str(dict, S_LEN("pattern"), cmd);
   1077    }
   1078  }
   1079 
   1080  vim_snprintf(IObuff, IOSIZE, "ltag %s", tag);
   1081  set_errorlist(curwin, list, ' ', IObuff, NULL);
   1082 
   1083  tv_list_free(list);
   1084  XFREE_CLEAR(fname);
   1085  XFREE_CLEAR(cmd);
   1086 
   1087  return OK;
   1088 }
   1089 
   1090 // Free cached tags.
   1091 void tag_freematch(void)
   1092 {
   1093  XFREE_CLEAR(tagmatchname);
   1094 }
   1095 
   1096 static void taglen_advance(int l)
   1097 {
   1098  if (l == MAXCOL) {
   1099    msg_putchar('\n');
   1100    msg_advance(24);
   1101  } else {
   1102    msg_advance(13 + l);
   1103  }
   1104 }
   1105 
   1106 // Print the tag stack
   1107 void do_tags(exarg_T *eap)
   1108 {
   1109  taggy_T *tagstack = curwin->w_tagstack;
   1110  int tagstackidx = curwin->w_tagstackidx;
   1111  int tagstacklen = curwin->w_tagstacklen;
   1112 
   1113  // Highlight title
   1114  msg_puts_title(_("\n  # TO tag         FROM line  in file/text"));
   1115  for (int i = 0; i < tagstacklen; i++) {
   1116    if (tagstack[i].tagname != NULL) {
   1117      char *name = fm_getname(&(tagstack[i].fmark), 30);
   1118      if (name == NULL) {           // file name not available
   1119        continue;
   1120      }
   1121 
   1122      msg_putchar('\n');
   1123      vim_snprintf(IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR "  ",
   1124                   i == tagstackidx ? '>' : ' ',
   1125                   i + 1,
   1126                   tagstack[i].cur_match + 1,
   1127                   tagstack[i].tagname,
   1128                   tagstack[i].fmark.mark.lnum);
   1129      msg_outtrans(IObuff, 0, false);
   1130      msg_outtrans(name, tagstack[i].fmark.fnum == curbuf->b_fnum ? HLF_D : 0, false);
   1131      xfree(name);
   1132    }
   1133  }
   1134  if (tagstackidx == tagstacklen) {     // idx at top of stack
   1135    msg_puts("\n>");
   1136  }
   1137 }
   1138 
   1139 // Compare two strings, for length "len", ignoring case the ASCII way.
   1140 // return 0 for match, < 0 for smaller, > 0 for bigger
   1141 // Make sure case is folded to uppercase in comparison (like for 'sort -f')
   1142 static int tag_strnicmp(char *s1, char *s2, size_t len)
   1143 {
   1144  while (len > 0) {
   1145    int i = TOUPPER_ASC((uint8_t)(*s1)) - TOUPPER_ASC((uint8_t)(*s2));
   1146    if (i != 0) {
   1147      return i;                         // this character different
   1148    }
   1149    if (*s1 == NUL) {
   1150      break;                            // strings match until NUL
   1151    }
   1152    s1++;
   1153    s2++;
   1154    len--;
   1155  }
   1156  return 0;                             // strings match
   1157 }
   1158 
   1159 // Extract info from the tag search pattern "pats->pat".
   1160 static void prepare_pats(pat_T *pats, bool has_re)
   1161 {
   1162  pats->head = pats->pat;
   1163  pats->headlen = pats->len;
   1164  if (has_re) {
   1165    // When the pattern starts with '^' or "\\<", binary searching can be
   1166    // used (much faster).
   1167    if (pats->pat[0] == '^') {
   1168      pats->head = pats->pat + 1;
   1169    } else if (pats->pat[0] == '\\' && pats->pat[1] == '<') {
   1170      pats->head = pats->pat + 2;
   1171    }
   1172    if (pats->head == pats->pat) {
   1173      pats->headlen = 0;
   1174    } else {
   1175      for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) {
   1176        if (vim_strchr(magic_isset() ? ".[~*\\$" : "\\$",
   1177                       (uint8_t)pats->head[pats->headlen]) != NULL) {
   1178          break;
   1179        }
   1180      }
   1181    }
   1182    if (p_tl != 0 && pats->headlen > p_tl) {    // adjust for 'taglength'
   1183      pats->headlen = (int)p_tl;
   1184    }
   1185  }
   1186 
   1187  if (has_re) {
   1188    pats->regmatch.regprog = vim_regcomp(pats->pat, magic_isset() ? RE_MAGIC : 0);
   1189  } else {
   1190    pats->regmatch.regprog = NULL;
   1191  }
   1192 }
   1193 
   1194 /// Call the user-defined function to generate a list of tags used by
   1195 /// find_tags().
   1196 ///
   1197 /// Return OK if at least 1 tag has been successfully found,
   1198 /// NOTDONE if the function returns v:null, and FAIL otherwise.
   1199 ///
   1200 /// @param pat  pattern supplied to the user-defined function
   1201 /// @param ga  the tags will be placed here
   1202 /// @param match_count  here the number of tags found will be placed
   1203 /// @param flags  flags from find_tags (TAG_*)
   1204 /// @param buf_ffname  name of buffer for priority
   1205 static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flags, char *buf_ffname)
   1206 {
   1207  int ntags = 0;
   1208  typval_T args[4];
   1209  typval_T rettv;
   1210  char flagString[4];
   1211  taggy_T *tag = NULL;
   1212 
   1213  if (curwin->w_tagstacklen > 0) {
   1214    if (curwin->w_tagstackidx == curwin->w_tagstacklen) {
   1215      tag = &curwin->w_tagstack[curwin->w_tagstackidx - 1];
   1216    } else {
   1217      tag = &curwin->w_tagstack[curwin->w_tagstackidx];
   1218    }
   1219  }
   1220 
   1221  if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) {
   1222    return FAIL;
   1223  }
   1224 
   1225  args[0].v_type = VAR_STRING;
   1226  args[0].vval.v_string = pat;
   1227  args[1].v_type = VAR_STRING;
   1228  args[1].vval.v_string = flagString;
   1229 
   1230  // create 'info' dict argument
   1231  dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
   1232  if (!(flags & TAG_INS_COMP) && tag != NULL && tag->user_data != NULL) {
   1233    tv_dict_add_str(d, S_LEN("user_data"), tag->user_data);
   1234  }
   1235  if (buf_ffname != NULL) {
   1236    tv_dict_add_str(d, S_LEN("buf_ffname"), buf_ffname);
   1237  }
   1238 
   1239  d->dv_refcount++;
   1240  args[2].v_type = VAR_DICT;
   1241  args[2].vval.v_dict = d;
   1242 
   1243  args[3].v_type = VAR_UNKNOWN;
   1244 
   1245  vim_snprintf(flagString, sizeof(flagString),
   1246               "%s%s%s",
   1247               g_tag_at_cursor ? "c" : "",
   1248               flags & TAG_INS_COMP ? "i" : "",
   1249               flags & TAG_REGEXP ? "r" : "");
   1250 
   1251  pos_T save_pos = curwin->w_cursor;
   1252  int result = callback_call(&curbuf->b_tfu_cb, 3, args, &rettv);
   1253  curwin->w_cursor = save_pos;  // restore the cursor position
   1254  check_cursor(curwin);         // make sure cursor position is valid
   1255  d->dv_refcount--;
   1256 
   1257  if (result == FAIL) {
   1258    return FAIL;
   1259  }
   1260  if (rettv.v_type == VAR_SPECIAL && rettv.vval.v_special == kSpecialVarNull) {
   1261    tv_clear(&rettv);
   1262    return NOTDONE;
   1263  }
   1264  if (rettv.v_type != VAR_LIST || !rettv.vval.v_list) {
   1265    tv_clear(&rettv);
   1266    emsg(_(e_invalid_return_value_from_tagfunc));
   1267    return FAIL;
   1268  }
   1269  list_T *taglist = rettv.vval.v_list;
   1270 
   1271  TV_LIST_ITER_CONST(taglist, li, {
   1272    char *res_name;
   1273    char *res_fname;
   1274    char *res_cmd;
   1275    char *res_kind;
   1276    bool has_extra = false;
   1277    int name_only = flags & TAG_NAMES;
   1278 
   1279    if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) {
   1280      emsg(_(e_invalid_return_value_from_tagfunc));
   1281      break;
   1282    }
   1283 
   1284    size_t len = 2;
   1285    res_name = NULL;
   1286    res_fname = NULL;
   1287    res_cmd = NULL;
   1288    res_kind = NULL;
   1289 
   1290    TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
   1291      const char *dict_key = di->di_key;
   1292      typval_T *tv = &di->di_tv;
   1293 
   1294      if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
   1295        continue;
   1296      }
   1297 
   1298      len += strlen(tv->vval.v_string) + 1;   // Space for "\tVALUE"
   1299      if (!strcmp(dict_key, "name")) {
   1300        res_name = tv->vval.v_string;
   1301        continue;
   1302      }
   1303      if (!strcmp(dict_key, "filename")) {
   1304        res_fname = tv->vval.v_string;
   1305        continue;
   1306      }
   1307      if (!strcmp(dict_key, "cmd")) {
   1308        res_cmd = tv->vval.v_string;
   1309        continue;
   1310      }
   1311      has_extra = true;
   1312      if (!strcmp(dict_key, "kind")) {
   1313        res_kind = tv->vval.v_string;
   1314        continue;
   1315      }
   1316      // Other elements will be stored as "\tKEY:VALUE"
   1317      // Allocate space for the key and the colon
   1318      len += strlen(dict_key) + 1;
   1319    });
   1320 
   1321    if (has_extra) {
   1322      len += 2;  // need space for ;"
   1323    }
   1324 
   1325    if (!res_name || !res_fname || !res_cmd) {
   1326      emsg(_(e_invalid_return_value_from_tagfunc));
   1327      break;
   1328    }
   1329 
   1330    char *const mfp = name_only ? xstrdup(res_name) : xmalloc(len + 2);
   1331 
   1332    if (!name_only) {
   1333      char *p = mfp;
   1334 
   1335      *p++ = MT_GL_OTH + 1;   // mtt
   1336      *p++ = TAG_SEP;     // no tag file name
   1337 
   1338      STRCPY(p, res_name);
   1339      p += strlen(p);
   1340 
   1341      *p++ = TAB;
   1342      STRCPY(p, res_fname);
   1343      p += strlen(p);
   1344 
   1345      *p++ = TAB;
   1346      STRCPY(p, res_cmd);
   1347      p += strlen(p);
   1348 
   1349      if (has_extra) {
   1350        STRCPY(p, ";\"");
   1351        p += strlen(p);
   1352 
   1353        if (res_kind) {
   1354          *p++ = TAB;
   1355          STRCPY(p, res_kind);
   1356          p += strlen(p);
   1357        }
   1358 
   1359        TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
   1360          const char *dict_key = di->di_key;
   1361          typval_T *tv = &di->di_tv;
   1362          if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
   1363            continue;
   1364          }
   1365 
   1366          if (!strcmp(dict_key, "name")) {
   1367            continue;
   1368          }
   1369          if (!strcmp(dict_key, "filename")) {
   1370            continue;
   1371          }
   1372          if (!strcmp(dict_key, "cmd")) {
   1373            continue;
   1374          }
   1375          if (!strcmp(dict_key, "kind")) {
   1376            continue;
   1377          }
   1378 
   1379          *p++ = TAB;
   1380          STRCPY(p, dict_key);
   1381          p += strlen(p);
   1382          STRCPY(p, ":");
   1383          p += strlen(p);
   1384          STRCPY(p, tv->vval.v_string);
   1385          p += strlen(p);
   1386        });
   1387      }
   1388    }
   1389 
   1390    // Add all matches because tagfunc should do filtering.
   1391    ga_grow(ga, 1);
   1392    ((char **)(ga->ga_data))[ga->ga_len++] = (char *)mfp;
   1393    ntags++;
   1394    result = OK;
   1395  });
   1396 
   1397  tv_clear(&rettv);
   1398 
   1399  *match_count = ntags;
   1400  return result;
   1401 }
   1402 
   1403 /// Initialize the state used by find_tags()
   1404 static void findtags_state_init(findtags_state_T *st, char *pat, int flags, int mincount)
   1405 {
   1406  st->tag_fname = xmalloc(MAXPATHL + 1);
   1407  st->fp = NULL;
   1408  st->orgpat = xmalloc(sizeof(pat_T));
   1409  st->orgpat->pat = pat;
   1410  st->orgpat->len = (int)strlen(pat);
   1411  st->orgpat->regmatch.regprog = NULL;
   1412  st->flags = flags;
   1413  st->tag_file_sorted = NUL;
   1414  st->help_lang_find = NULL;
   1415  st->is_txt = false;
   1416  st->did_open = false;
   1417  st->help_only = (flags & TAG_HELP);
   1418  st->get_searchpat = false;
   1419  st->help_lang[0] = NUL;
   1420  st->help_pri = 0;
   1421  st->mincount = mincount;
   1422  st->lbuf_size = LSIZE;
   1423  st->lbuf = xmalloc((size_t)st->lbuf_size);
   1424  st->match_count = 0;
   1425  st->stop_searching = false;
   1426 
   1427  for (int mtt = 0; mtt < MT_COUNT; mtt++) {
   1428    ga_init(&st->ga_match[mtt], sizeof(char *), 100);
   1429    hash_init(&st->ht_match[mtt]);
   1430  }
   1431 }
   1432 
   1433 /// Free the state used by find_tags()
   1434 static void findtags_state_free(findtags_state_T *st)
   1435 {
   1436  xfree(st->tag_fname);
   1437  xfree(st->lbuf);
   1438  vim_regfree(st->orgpat->regmatch.regprog);
   1439  xfree(st->orgpat);
   1440 }
   1441 
   1442 /// Initialize the language and priority used for searching tags in a Vim help
   1443 /// file.
   1444 /// Returns true to process the help file for tags and false to skip the file.
   1445 static bool findtags_in_help_init(findtags_state_T *st)
   1446 {
   1447  int i;
   1448 
   1449  // Keep "en" as the language if the file extension is ".txt"
   1450  if (st->is_txt) {
   1451    STRCPY(st->help_lang, "en");
   1452  } else {
   1453    // Prefer help tags according to 'helplang'.  Put the two-letter
   1454    // language name in help_lang[].
   1455    i = (int)strlen(st->tag_fname);
   1456    if (i > 3 && st->tag_fname[i - 3] == '-') {
   1457      xmemcpyz(st->help_lang, st->tag_fname + i - 2, 2);
   1458    } else {
   1459      STRCPY(st->help_lang, "en");
   1460    }
   1461  }
   1462  // When searching for a specific language skip tags files for other
   1463  // languages.
   1464  if (st->help_lang_find != NULL
   1465      && STRICMP(st->help_lang, st->help_lang_find) != 0) {
   1466    return false;
   1467  }
   1468 
   1469  // For CTRL-] in a help file prefer a match with the same language.
   1470  if ((st->flags & TAG_KEEP_LANG)
   1471      && st->help_lang_find == NULL
   1472      && curbuf->b_fname != NULL
   1473      && (i = (int)strlen(curbuf->b_fname)) > 4
   1474      && curbuf->b_fname[i - 1] == 'x'
   1475      && curbuf->b_fname[i - 4] == '.'
   1476      && STRNICMP(curbuf->b_fname + i - 3, st->help_lang, 2) == 0) {
   1477    st->help_pri = 0;
   1478  } else {
   1479    st->help_pri = 1;
   1480    char *s;
   1481    for (s = p_hlg; *s != NUL; s++) {
   1482      if (STRNICMP(s, st->help_lang, 2) == 0) {
   1483        break;
   1484      }
   1485      st->help_pri++;
   1486      if ((s = vim_strchr(s, ',')) == NULL) {
   1487        break;
   1488      }
   1489    }
   1490    if (s == NULL || *s == NUL) {
   1491      // Language not in 'helplang': use last, prefer English, unless
   1492      // found already.
   1493      st->help_pri++;
   1494      if (STRICMP(st->help_lang, "en") != 0) {
   1495        st->help_pri++;
   1496      }
   1497    }
   1498  }
   1499 
   1500  return true;
   1501 }
   1502 
   1503 /// Use the function set in 'tagfunc' (if configured and enabled) to get the
   1504 /// tags.
   1505 /// Return OK if at least 1 tag has been successfully found, NOTDONE if the
   1506 /// 'tagfunc' is not used, still executing or the 'tagfunc' returned v:null and
   1507 /// FAIL otherwise.
   1508 static int findtags_apply_tfu(findtags_state_T *st, char *pat, char *buf_ffname)
   1509 {
   1510  const bool use_tfu = ((st->flags & TAG_NO_TAGFUNC) == 0);
   1511 
   1512  if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) {
   1513    return NOTDONE;
   1514  }
   1515 
   1516  tfu_in_use = true;
   1517  int retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
   1518                                 st->flags, buf_ffname);
   1519  tfu_in_use = false;
   1520 
   1521  return retval;
   1522 }
   1523 
   1524 /// Read the next line from a tags file.
   1525 /// Returns TAGS_READ_SUCCESS if a tags line is successfully read and should be
   1526 /// processed.
   1527 /// Returns TAGS_READ_EOF if the end of file is reached.
   1528 /// Returns TAGS_READ_IGNORE if the current line should be ignored (used when
   1529 /// reached end of a emacs included tags file)
   1530 static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p)
   1531 {
   1532  bool eof;
   1533 
   1534  // For binary search: compute the next offset to use.
   1535  if (st->state == TS_BINARY) {
   1536    off_T offset = sinfo_p->low_offset + ((sinfo_p->high_offset - sinfo_p->low_offset) / 2);
   1537    if (offset == sinfo_p->curr_offset) {
   1538      return TAGS_READ_EOF;  // End the binary search without a match.
   1539    } else {
   1540      sinfo_p->curr_offset = offset;
   1541    }
   1542  } else if (st->state == TS_SKIP_BACK) {
   1543    // Skipping back (after a match during binary search).
   1544    sinfo_p->curr_offset -= st->lbuf_size * 2;
   1545    if (sinfo_p->curr_offset < 0) {
   1546      sinfo_p->curr_offset = 0;
   1547      fseek(st->fp, 0, SEEK_SET);
   1548      st->state = TS_STEP_FORWARD;
   1549    }
   1550  }
   1551 
   1552  // When jumping around in the file, first read a line to find the
   1553  // start of the next line.
   1554  if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
   1555    // Adjust the search file offset to the correct position
   1556    sinfo_p->curr_offset_used = sinfo_p->curr_offset;
   1557    vim_ignored = vim_fseek(st->fp, sinfo_p->curr_offset, SEEK_SET);
   1558    eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
   1559    if (!eof && sinfo_p->curr_offset != 0) {
   1560      sinfo_p->curr_offset = vim_ftell(st->fp);
   1561      if (sinfo_p->curr_offset == sinfo_p->high_offset) {
   1562        // oops, gone a bit too far; try from low offset
   1563        vim_ignored = vim_fseek(st->fp, sinfo_p->low_offset, SEEK_SET);
   1564        sinfo_p->curr_offset = sinfo_p->low_offset;
   1565      }
   1566      eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
   1567    }
   1568    // skip empty and blank lines
   1569    while (!eof && vim_isblankline(st->lbuf)) {
   1570      sinfo_p->curr_offset = vim_ftell(st->fp);
   1571      eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
   1572    }
   1573    if (eof) {
   1574      // Hit end of file.  Skip backwards.
   1575      st->state = TS_SKIP_BACK;
   1576      sinfo_p->match_offset = vim_ftell(st->fp);
   1577      sinfo_p->curr_offset = sinfo_p->curr_offset_used;
   1578      return TAGS_READ_IGNORE;
   1579    }
   1580  } else {
   1581    // Not jumping around in the file: Read the next line.
   1582 
   1583    // skip empty and blank lines
   1584    do {
   1585      eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
   1586    } while (!eof && vim_isblankline(st->lbuf));
   1587 
   1588    if (eof) {
   1589      return TAGS_READ_EOF;
   1590    }
   1591  }
   1592 
   1593  return TAGS_READ_SUCCESS;
   1594 }
   1595 
   1596 /// Parse a tags file header line in "st->lbuf".
   1597 /// Returns true if the current line in st->lbuf is not a tags header line and
   1598 /// should be parsed as a regular tag line. Returns false if the line is a
   1599 /// header line and the next header line should be read.
   1600 static bool findtags_hdr_parse(findtags_state_T *st)
   1601 {
   1602  // Header lines in a tags file start with "!_TAG_"
   1603  if (strncmp(st->lbuf, "!_TAG_", 6) != 0) {
   1604    // Non-header item before the header, e.g. "!" itself.
   1605    return true;
   1606  }
   1607 
   1608  // Process the header line.
   1609  if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
   1610    st->tag_file_sorted = (uint8_t)st->lbuf[18];
   1611  }
   1612  if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
   1613    // Prepare to convert every line from the specified encoding to
   1614    // 'encoding'.
   1615    char *p;
   1616    for (p = st->lbuf + 20; *p > ' ' && *p < 127; p++) {}
   1617    *p = NUL;
   1618    convert_setup(&st->vimconv, st->lbuf + 20, p_enc);
   1619  }
   1620 
   1621  // Read the next line.  Unrecognized flags are ignored.
   1622  return false;
   1623 }
   1624 
   1625 /// Handler to initialize the state when starting to process a new tags file.
   1626 /// Called in the TS_START state when finding tags from a tags file.
   1627 /// Returns true if the line read from the tags file should be parsed and
   1628 /// false if the line should be ignored.
   1629 static bool findtags_start_state_handler(findtags_state_T *st, bool *sortic,
   1630                                         tagsearch_info_T *sinfo_p)
   1631 {
   1632  const bool noic = (st->flags & TAG_NOIC);
   1633 
   1634  // The header ends when the line sorts below "!_TAG_".  When case is
   1635  // folded lower case letters sort before "_".
   1636  if (strncmp(st->lbuf, "!_TAG_", 6) <= 0
   1637      || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) {
   1638    return findtags_hdr_parse(st);
   1639  }
   1640 
   1641  // Headers ends.
   1642 
   1643  // When there is no tag head, or ignoring case, need to do a
   1644  // linear search.
   1645  // When no "!_TAG_" is found, default to binary search.  If
   1646  // the tag file isn't sorted, the second loop will find it.
   1647  // When "!_TAG_FILE_SORTED" found: start binary search if
   1648  // flag set.
   1649  if (st->linear) {
   1650    st->state = TS_LINEAR;
   1651  } else if (st->tag_file_sorted == NUL) {
   1652    st->state = TS_BINARY;
   1653  } else if (st->tag_file_sorted == '1') {
   1654    st->state = TS_BINARY;
   1655  } else if (st->tag_file_sorted == '2') {
   1656    st->state = TS_BINARY;
   1657    *sortic = true;
   1658    st->orgpat->regmatch.rm_ic = (p_ic || !noic);
   1659  } else {
   1660    st->state = TS_LINEAR;
   1661  }
   1662 
   1663  if (st->state == TS_BINARY && st->orgpat->regmatch.rm_ic && !*sortic) {
   1664    // Binary search won't work for ignoring case, use linear
   1665    // search.
   1666    st->linear = true;
   1667    st->state = TS_LINEAR;
   1668  }
   1669 
   1670  // When starting a binary search, get the size of the file and
   1671  // compute the first offset.
   1672  if (st->state == TS_BINARY) {
   1673    if (vim_fseek(st->fp, 0, SEEK_END) != 0) {
   1674      // can't seek, don't use binary search
   1675      st->state = TS_LINEAR;
   1676    } else {
   1677      // Get the tag file size.
   1678      // Don't use lseek(), it doesn't work
   1679      // properly on MacOS Catalina.
   1680      const off_T filesize = vim_ftell(st->fp);
   1681      vim_ignored = vim_fseek(st->fp, 0, SEEK_SET);
   1682 
   1683      // Calculate the first read offset in the file.  Start
   1684      // the search in the middle of the file.
   1685      sinfo_p->low_offset = 0;
   1686      sinfo_p->low_char = 0;
   1687      sinfo_p->high_offset = filesize;
   1688      sinfo_p->curr_offset = 0;
   1689      sinfo_p->high_char = 0xff;
   1690    }
   1691    return false;
   1692  }
   1693 
   1694  return true;
   1695 }
   1696 
   1697 /// Parse a tag line read from a tags file.
   1698 /// Also compares the tag name in "tagpp->tagname" with a search pattern in
   1699 /// "st->orgpat->head" as a quick check if the tag may match.
   1700 /// Returns:
   1701 /// - TAG_MATCH_SUCCESS if the tag may match
   1702 /// - TAG_MATCH_FAIL if the tag doesn't match
   1703 /// - TAG_MATCH_NEXT to look for the next matching tag (used in a binary search)
   1704 /// - TAG_MATCH_STOP if all the tags are processed without a match.
   1705 /// Uses the values in "margs" for doing the comparison.
   1706 static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp,
   1707                                             findtags_match_args_T *margs,
   1708                                             tagsearch_info_T *sinfo_p)
   1709 {
   1710  int status;
   1711 
   1712  // Figure out where the different strings are in this line.
   1713  // For "normal" tags: Do a quick check if the tag matches.
   1714  // This speeds up tag searching a lot!
   1715  if (st->orgpat->headlen) {
   1716    CLEAR_FIELD(*tagpp);
   1717    tagpp->tagname = st->lbuf;
   1718    tagpp->tagname_end = vim_strchr(st->lbuf, TAB);
   1719    if (tagpp->tagname_end == NULL) {
   1720      // Corrupted tag line.
   1721      return TAG_MATCH_FAIL;
   1722    }
   1723 
   1724    // Skip this line if the length of the tag is different and
   1725    // there is no regexp, or the tag is too short.
   1726    int cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
   1727    if (p_tl != 0 && cmplen > p_tl) {  // adjust for 'taglength'
   1728      cmplen = (int)p_tl;
   1729    }
   1730    if ((st->flags & TAG_REGEXP) && st->orgpat->headlen < cmplen) {
   1731      cmplen = st->orgpat->headlen;
   1732    } else if (st->state == TS_LINEAR && st->orgpat->headlen != cmplen) {
   1733      return TAG_MATCH_NEXT;
   1734    }
   1735 
   1736    if (st->state == TS_BINARY) {
   1737      int tagcmp;
   1738      // Simplistic check for unsorted tags file.
   1739      int i = (uint8_t)tagpp->tagname[0];
   1740      if (margs->sortic) {
   1741        i = TOUPPER_ASC(tagpp->tagname[0]);
   1742      }
   1743      if (i < sinfo_p->low_char || i > sinfo_p->high_char) {
   1744        margs->sort_error = true;
   1745      }
   1746 
   1747      // Compare the current tag with the searched tag.
   1748      if (margs->sortic) {
   1749        tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat->head,
   1750                              (size_t)cmplen);
   1751      } else {
   1752        tagcmp = strncmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen);
   1753      }
   1754 
   1755      // A match with a shorter tag means to search forward.
   1756      // A match with a longer tag means to search backward.
   1757      if (tagcmp == 0) {
   1758        if (cmplen < st->orgpat->headlen) {
   1759          tagcmp = -1;
   1760        } else if (cmplen > st->orgpat->headlen) {
   1761          tagcmp = 1;
   1762        }
   1763      }
   1764 
   1765      if (tagcmp == 0) {
   1766        // We've located the tag, now skip back and search
   1767        // forward until the first matching tag is found.
   1768        st->state = TS_SKIP_BACK;
   1769        sinfo_p->match_offset = sinfo_p->curr_offset;
   1770        return TAG_MATCH_NEXT;
   1771      }
   1772      if (tagcmp < 0) {
   1773        sinfo_p->curr_offset = vim_ftell(st->fp);
   1774        if (sinfo_p->curr_offset < sinfo_p->high_offset) {
   1775          sinfo_p->low_offset = sinfo_p->curr_offset;
   1776          if (margs->sortic) {
   1777            sinfo_p->low_char = TOUPPER_ASC(tagpp->tagname[0]);
   1778          } else {
   1779            sinfo_p->low_char = (uint8_t)tagpp->tagname[0];
   1780          }
   1781          return TAG_MATCH_NEXT;
   1782        }
   1783      }
   1784      if (tagcmp > 0 && sinfo_p->curr_offset != sinfo_p->high_offset) {
   1785        sinfo_p->high_offset = sinfo_p->curr_offset;
   1786        if (margs->sortic) {
   1787          sinfo_p->high_char = TOUPPER_ASC(tagpp->tagname[0]);
   1788        } else {
   1789          sinfo_p->high_char = (uint8_t)tagpp->tagname[0];
   1790        }
   1791        return TAG_MATCH_NEXT;
   1792      }
   1793 
   1794      // No match yet and are at the end of the binary search.
   1795      return TAG_MATCH_STOP;
   1796    } else if (st->state == TS_SKIP_BACK) {
   1797      assert(cmplen >= 0);
   1798      if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
   1799        st->state = TS_STEP_FORWARD;
   1800      } else {
   1801        // Have to skip back more.  Restore the curr_offset
   1802        // used, otherwise we get stuck at a long line.
   1803        sinfo_p->curr_offset = sinfo_p->curr_offset_used;
   1804      }
   1805      return TAG_MATCH_NEXT;
   1806    } else if (st->state == TS_STEP_FORWARD) {
   1807      assert(cmplen >= 0);
   1808      if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
   1809        return ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset)
   1810               ? TAG_MATCH_STOP   // past last match
   1811               : TAG_MATCH_NEXT;  // before first match
   1812      }
   1813    } else {
   1814      // skip this match if it can't match
   1815      assert(cmplen >= 0);
   1816      if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
   1817        return TAG_MATCH_NEXT;
   1818      }
   1819    }
   1820 
   1821    // Can be a matching tag, isolate the file name and command.
   1822    tagpp->fname = tagpp->tagname_end + 1;
   1823    tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
   1824    if (tagpp->fname_end == NULL) {
   1825      status = FAIL;
   1826    } else {
   1827      tagpp->command = tagpp->fname_end + 1;
   1828      status = OK;
   1829    }
   1830  } else {
   1831    status = parse_tag_line(st->lbuf, tagpp);
   1832  }
   1833 
   1834  return status == FAIL
   1835         ? TAG_MATCH_FAIL
   1836         : TAG_MATCH_SUCCESS;
   1837 }
   1838 
   1839 /// Initialize the structure used for tag matching.
   1840 static void findtags_matchargs_init(findtags_match_args_T *margs, int flags)
   1841 {
   1842  margs->matchoff = 0;                        // match offset
   1843  margs->match_re = false;                    // match with regexp
   1844  margs->match_no_ic = false;                 // matches with case
   1845  margs->has_re = (flags & TAG_REGEXP);       // regexp used
   1846  margs->sortic = false;                      // tag file sorted in nocase
   1847  margs->sort_error = false;                  // tags file not sorted
   1848 }
   1849 
   1850 /// Compares the tag name in "tagpp->tagname" with a search pattern in
   1851 /// "st->orgpat->pat".
   1852 /// Returns true if the tag matches, false if the tag doesn't match.
   1853 /// Uses the values in "margs" for doing the comparison.
   1854 static bool findtags_match_tag(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs)
   1855 {
   1856  bool match = false;
   1857 
   1858  // First try matching with the pattern literally (also when it is
   1859  // a regexp).
   1860  int cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
   1861  if (p_tl != 0 && cmplen > p_tl) {           // adjust for 'taglength'
   1862    cmplen = (int)p_tl;
   1863  }
   1864  // if tag length does not match, don't try comparing
   1865  if (st->orgpat->len != cmplen) {
   1866    match = false;
   1867  } else {
   1868    if (st->orgpat->regmatch.rm_ic) {
   1869      assert(cmplen >= 0);
   1870      match = mb_strnicmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
   1871      if (match) {
   1872        margs->match_no_ic = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
   1873      }
   1874    } else {
   1875      match = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
   1876    }
   1877  }
   1878 
   1879  // Has a regexp: Also find tags matching regexp.
   1880  margs->match_re = false;
   1881  if (!match && st->orgpat->regmatch.regprog != NULL) {
   1882    char cc = *tagpp->tagname_end;
   1883    *tagpp->tagname_end = NUL;
   1884    match = vim_regexec(&st->orgpat->regmatch, tagpp->tagname, 0);
   1885    if (match) {
   1886      margs->matchoff = (int)(st->orgpat->regmatch.startp[0] - tagpp->tagname);
   1887      if (st->orgpat->regmatch.rm_ic) {
   1888        st->orgpat->regmatch.rm_ic = false;
   1889        margs->match_no_ic = vim_regexec(&st->orgpat->regmatch,
   1890                                         tagpp->tagname, 0);
   1891        st->orgpat->regmatch.rm_ic = true;
   1892      }
   1893    }
   1894    *tagpp->tagname_end = cc;
   1895    margs->match_re = true;
   1896  }
   1897 
   1898  return match;
   1899 }
   1900 
   1901 /// Convert the encoding of a line read from a tags file in "st->lbuf".
   1902 /// Converting the pattern from 'enc' to the tags file encoding doesn't work,
   1903 /// because characters are not recognized. The converted line is saved in
   1904 /// st->lbuf.
   1905 static void findtags_string_convert(findtags_state_T *st)
   1906 {
   1907  char *conv_line = string_convert(&st->vimconv, st->lbuf, NULL);
   1908  if (conv_line == NULL) {
   1909    return;
   1910  }
   1911 
   1912  // Copy or swap lbuf and conv_line.
   1913  int len = (int)strlen(conv_line) + 1;
   1914  if (len > st->lbuf_size) {
   1915    xfree(st->lbuf);
   1916    st->lbuf = conv_line;
   1917    st->lbuf_size = len;
   1918  } else {
   1919    STRCPY(st->lbuf, conv_line);
   1920    xfree(conv_line);
   1921  }
   1922 }
   1923 
   1924 /// Add a matching tag found in a tags file to st->ht_match and st->ga_match.
   1925 static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs,
   1926                               char *buf_ffname, hash_T *hash)
   1927 {
   1928  const bool name_only = (st->flags & TAG_NAMES);
   1929  size_t len = 0;
   1930  size_t mfp_size = 0;
   1931  char *mfp;
   1932 
   1933  // Decide in which array to store this match.
   1934  bool is_current = test_for_current(tagpp->fname, tagpp->fname_end,
   1935                                     st->tag_fname, buf_ffname);
   1936 
   1937  // current tag line is static
   1938  bool is_static = test_for_static(tagpp);
   1939 
   1940  // Decide in which of the sixteen tables to store this match.
   1941  int mtt = is_static
   1942            ? is_current ? MT_ST_CUR : MT_ST_OTH
   1943            : is_current ? MT_GL_CUR : MT_GL_OTH;
   1944 
   1945  if (st->orgpat->regmatch.rm_ic && !margs->match_no_ic) {
   1946    mtt += MT_IC_OFF;
   1947  }
   1948  if (margs->match_re) {
   1949    mtt += MT_RE_OFF;
   1950  }
   1951 
   1952  // Add the found match in ht_match[mtt] and ga_match[mtt].
   1953  // Store the info we need later, which depends on the kind of
   1954  // tags we are dealing with.
   1955  if (st->help_only) {
   1956 #define ML_EXTRA 3
   1957    // Append the help-heuristic number after the tagname, for
   1958    // sorting it later.  The heuristic is ignored for
   1959    // detecting duplicates.
   1960    // The format is {tagname}@{lang}NUL{heuristic}NUL
   1961    *tagpp->tagname_end = NUL;
   1962    len = (size_t)(tagpp->tagname_end - tagpp->tagname);
   1963    mfp_size = sizeof(char) + len + 10 + ML_EXTRA + 1;
   1964    mfp = xmalloc(mfp_size);
   1965 
   1966    char *p = mfp;
   1967    STRCPY(p, tagpp->tagname);
   1968    p[len] = '@';
   1969    STRCPY(p + len + 1, st->help_lang);
   1970    snprintf(p + len + 1 + ML_EXTRA, mfp_size - (len + 1 + ML_EXTRA), "%06d",
   1971             help_heuristic(tagpp->tagname,
   1972                            margs->match_re ? margs->matchoff : 0,
   1973                            !margs->match_no_ic) + st->help_pri);
   1974 
   1975    *tagpp->tagname_end = TAB;
   1976  } else if (name_only) {
   1977    if (st->get_searchpat) {
   1978      char *temp_end = tagpp->command;
   1979 
   1980      if (*temp_end == '/') {
   1981        while (*temp_end && *temp_end != '\r'
   1982               && *temp_end != '\n'
   1983               && *temp_end != '$') {
   1984          temp_end++;
   1985        }
   1986      }
   1987 
   1988      if (tagpp->command + 2 < temp_end) {
   1989        len = (size_t)(temp_end - tagpp->command - 2);
   1990        mfp = xmalloc(len + 2);
   1991        xmemcpyz(mfp, tagpp->command + 2, len);
   1992      } else {
   1993        mfp = NULL;
   1994      }
   1995      st->get_searchpat = false;
   1996    } else {
   1997      len = (size_t)(tagpp->tagname_end - tagpp->tagname);
   1998      mfp = xmalloc(sizeof(char) + len + 1);
   1999      xmemcpyz(mfp, tagpp->tagname, len);
   2000 
   2001      // if wanted, re-read line to get long form too
   2002      if (State & MODE_INSERT) {
   2003        st->get_searchpat = p_sft;
   2004      }
   2005    }
   2006  } else {
   2007    size_t tag_fname_len = strlen(st->tag_fname);
   2008    // Save the tag in a buffer.
   2009    // Use 0x02 to separate fields (Can't use NUL, because the
   2010    // hash key is terminated by NUL).
   2011    // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
   2012    // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
   2013    // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
   2014    // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
   2015    len = tag_fname_len + strlen(st->lbuf) + 3;
   2016    mfp = xmalloc(sizeof(char) + len + 1);
   2017    char *p = mfp;
   2018    p[0] = (char)(mtt + 1);
   2019    STRCPY(p + 1, st->tag_fname);
   2020 #ifdef BACKSLASH_IN_FILENAME
   2021    // Ignore differences in slashes, avoid adding
   2022    // both path/file and path\file.
   2023    slash_adjust(p + 1);
   2024 #endif
   2025    p[tag_fname_len + 1] = TAG_SEP;
   2026    char *s = p + 1 + tag_fname_len + 1;
   2027    STRCPY(s, st->lbuf);
   2028  }
   2029 
   2030  if (mfp != NULL) {
   2031    hashitem_T *hi;
   2032 
   2033    // Don't add identical matches.
   2034    // "mfp" is used as a hash key, there is a NUL byte to end
   2035    // the part that matters for comparing, more bytes may
   2036    // follow after it.  E.g. help tags store the priority
   2037    // after the NUL.
   2038    *hash = hash_hash(mfp);
   2039    hi = hash_lookup(&st->ht_match[mtt], mfp, strlen(mfp), *hash);
   2040    if (HASHITEM_EMPTY(hi)) {
   2041      hash_add_item(&st->ht_match[mtt], hi, mfp, *hash);
   2042      GA_APPEND(char *, &st->ga_match[mtt], mfp);
   2043      st->match_count++;
   2044    } else {
   2045      // duplicate tag, drop it
   2046      xfree(mfp);
   2047    }
   2048  }
   2049 }
   2050 
   2051 /// Read and get all the tags from file st->tag_fname.
   2052 /// Sets "st->stop_searching" to true to stop searching for additional tags.
   2053 static void findtags_get_all_tags(findtags_state_T *st, findtags_match_args_T *margs,
   2054                                  char *buf_ffname)
   2055 {
   2056  tagptrs_T tagp;
   2057  tagsearch_info_T search_info;
   2058  hash_T hash = 0;
   2059 
   2060  // This is only to avoid a compiler warning for using search_info
   2061  // uninitialised.
   2062  CLEAR_FIELD(search_info);
   2063 
   2064  // Read and parse the lines in the file one by one
   2065  while (true) {
   2066    // check for CTRL-C typed, more often when jumping around
   2067    if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
   2068      line_breakcheck();
   2069    } else {
   2070      fast_breakcheck();
   2071    }
   2072    if ((st->flags & TAG_INS_COMP)) {   // Double brackets for gcc
   2073      ins_compl_check_keys(30, false);
   2074    }
   2075    if (got_int || ins_compl_interrupted()) {
   2076      st->stop_searching = true;
   2077      break;
   2078    }
   2079    // When mincount is TAG_MANY, stop when enough matches have been
   2080    // found (for completion).
   2081    if (st->mincount == TAG_MANY && st->match_count >= TAG_MANY) {
   2082      st->stop_searching = true;
   2083      break;
   2084    }
   2085    if (st->get_searchpat) {
   2086      goto line_read_in;
   2087    }
   2088 
   2089    int retval = (int)findtags_get_next_line(st, &search_info);
   2090    if (retval == TAGS_READ_IGNORE) {
   2091      continue;
   2092    }
   2093    if (retval == TAGS_READ_EOF) {
   2094      break;
   2095    }
   2096 
   2097 line_read_in:
   2098 
   2099    if (st->vimconv.vc_type != CONV_NONE) {
   2100      findtags_string_convert(st);
   2101    }
   2102 
   2103    // When still at the start of the file, check for Emacs tags file
   2104    // format, and for "not sorted" flag.
   2105    if (st->state == TS_START) {
   2106      if (!findtags_start_state_handler(st, &margs->sortic, &search_info)) {
   2107        continue;
   2108      }
   2109    }
   2110 
   2111    // When the line is too long the NUL will not be in the
   2112    // last-but-one byte (see vim_fgets()).
   2113    // Has been reported for Mozilla JS with extremely long names.
   2114    // In that case we need to increase lbuf_size.
   2115    if (st->lbuf[st->lbuf_size - 2] != NUL) {
   2116      st->lbuf_size *= 2;
   2117      xfree(st->lbuf);
   2118      st->lbuf = xmalloc((size_t)st->lbuf_size);
   2119 
   2120      if (st->state == TS_STEP_FORWARD || st->state == TS_LINEAR) {
   2121        // Seek to the same position to read the same line again
   2122        vim_ignored = vim_fseek(st->fp, search_info.curr_offset, SEEK_SET);
   2123      }
   2124      // this will try the same thing again, make sure the offset is
   2125      // different
   2126      search_info.curr_offset = 0;
   2127      continue;
   2128    }
   2129 
   2130    retval = (int)findtags_parse_line(st, &tagp, margs, &search_info);
   2131    if (retval == TAG_MATCH_NEXT) {
   2132      continue;
   2133    }
   2134    if (retval == TAG_MATCH_STOP) {
   2135      break;
   2136    }
   2137    if (retval == TAG_MATCH_FAIL) {
   2138      semsg(_("E431: Format error in tags file \"%s\""), st->tag_fname);
   2139      semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(st->fp));
   2140      st->stop_searching = true;
   2141      return;
   2142    }
   2143 
   2144    // If a match is found, add it to ht_match[] and ga_match[].
   2145    if (findtags_match_tag(st, &tagp, margs)) {
   2146      findtags_add_match(st, &tagp, margs, buf_ffname, &hash);
   2147    }
   2148  }  // forever
   2149 }
   2150 
   2151 /// Search for tags matching "st->orgpat.pat" in the "st->tag_fname" tags file.
   2152 /// Information needed to search for the tags is in the "st" state structure.
   2153 /// The matching tags are returned in "st". If an error is encountered, then
   2154 /// "st->stop_searching" is set to true.
   2155 static void findtags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
   2156 {
   2157  findtags_match_args_T margs;
   2158 
   2159  st->vimconv.vc_type = CONV_NONE;
   2160  st->tag_file_sorted = NUL;
   2161  st->fp = NULL;
   2162  findtags_matchargs_init(&margs, st->flags);
   2163 
   2164  // A file that doesn't exist is silently ignored.  Only when not a
   2165  // single file is found, an error message is given (further on).
   2166  if (curbuf->b_help) {
   2167    if (!findtags_in_help_init(st)) {
   2168      return;
   2169    }
   2170  }
   2171 
   2172  st->fp = os_fopen(st->tag_fname, "r");
   2173  if (st->fp == NULL) {
   2174    return;
   2175  }
   2176 
   2177  if (p_verbose >= 5) {
   2178    verbose_enter();
   2179    smsg(0, _("Searching tags file %s"), st->tag_fname);
   2180    verbose_leave();
   2181  }
   2182  st->did_open = true;   // remember that we found at least one file
   2183 
   2184  st->state = TS_START;  // we're at the start of the file
   2185 
   2186  // Read and parse the lines in the file one by one
   2187  findtags_get_all_tags(st, &margs, buf_ffname);
   2188 
   2189  if (st->fp != NULL) {
   2190    fclose(st->fp);
   2191    st->fp = NULL;
   2192  }
   2193  if (st->vimconv.vc_type != CONV_NONE) {
   2194    convert_setup(&st->vimconv, NULL, NULL);
   2195  }
   2196 
   2197  if (margs.sort_error) {
   2198    semsg(_("E432: Tags file not sorted: %s"), st->tag_fname);
   2199  }
   2200 
   2201  // Stop searching if sufficient tags have been found.
   2202  if (st->match_count >= st->mincount) {
   2203    st->stop_searching = true;
   2204  }
   2205 }
   2206 
   2207 /// Copy the tags found by find_tags() to "matchesp".
   2208 /// Returns the number of matches copied.
   2209 static int findtags_copy_matches(findtags_state_T *st, char ***matchesp)
   2210 {
   2211  const bool name_only = (st->flags & TAG_NAMES);
   2212 
   2213  char **matches = st->match_count > 0
   2214                   ? xmalloc((size_t)st->match_count * sizeof(char *))
   2215                   : NULL;
   2216 
   2217  st->match_count = 0;
   2218  for (int mtt = 0; mtt < MT_COUNT; mtt++) {
   2219    for (int i = 0; i < st->ga_match[mtt].ga_len; i++) {
   2220      char *mfp = ((char **)(st->ga_match[mtt].ga_data))[i];
   2221      if (matches == NULL) {
   2222        xfree(mfp);
   2223      } else {
   2224        if (!name_only) {
   2225          // Change mtt back to zero-based.
   2226          *mfp = (char)(*mfp - 1);
   2227 
   2228          // change the TAG_SEP back to NUL
   2229          for (char *p = mfp + 1; *p != NUL; p++) {
   2230            if (*p == TAG_SEP) {
   2231              *p = NUL;
   2232            }
   2233          }
   2234        }
   2235        matches[st->match_count++] = mfp;
   2236      }
   2237    }
   2238 
   2239    ga_clear(&st->ga_match[mtt]);
   2240    hash_clear(&st->ht_match[mtt]);
   2241  }
   2242 
   2243  *matchesp = matches;
   2244  return st->match_count;
   2245 }
   2246 
   2247 /// find_tags() - search for tags in tags files
   2248 ///
   2249 /// Return FAIL if search completely failed (*num_matches will be 0, *matchesp
   2250 /// will be NULL), OK otherwise.
   2251 ///
   2252 /// There is a priority in which type of tag is recognized.
   2253 ///
   2254 ///  6.  A static or global tag with a full matching tag for the current file.
   2255 ///  5.  A global tag with a full matching tag for another file.
   2256 ///  4.  A static tag with a full matching tag for another file.
   2257 ///  3.  A static or global tag with an ignore-case matching tag for the
   2258 ///      current file.
   2259 ///  2.  A global tag with an ignore-case matching tag for another file.
   2260 ///  1.  A static tag with an ignore-case matching tag for another file.
   2261 ///
   2262 /// Tags in an emacs-style tags file are always global.
   2263 ///
   2264 /// flags:
   2265 /// TAG_HELP       only search for help tags
   2266 /// TAG_NAMES      only return name of tag
   2267 /// TAG_REGEXP     use "pat" as a regexp
   2268 /// TAG_NOIC       don't always ignore case
   2269 /// TAG_KEEP_LANG  keep language
   2270 /// TAG_NO_TAGFUNC do not call the 'tagfunc' function
   2271 ///
   2272 /// @param pat  pattern to search for
   2273 /// @param num_matches  return: number of matches found
   2274 /// @param matchesp  return: array of matches found
   2275 /// @param mincount  MAXCOL: find all matches
   2276 ///                  other: minimal number of matches
   2277 /// @param buf_ffname  name of buffer for priority
   2278 int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int mincount,
   2279              char *buf_ffname)
   2280 {
   2281  findtags_state_T st;
   2282  tagname_T tn;                         // info for get_tagfname()
   2283  int first_file;                       // trying first tag file
   2284  int retval = FAIL;                    // return value
   2285 
   2286  int i;
   2287  char *saved_pat = NULL;                // copy of pat[]
   2288 
   2289  int findall = (mincount == MAXCOL || mincount == TAG_MANY);  // find all matching tags
   2290  bool has_re = (flags & TAG_REGEXP);            // regexp used
   2291  int noic = (flags & TAG_NOIC);
   2292  int verbose = (flags & TAG_VERBOSE);
   2293  int save_p_ic = p_ic;
   2294 
   2295  // uncrustify:off
   2296 
   2297  // Change the value of 'ignorecase' according to 'tagcase' for the
   2298  // duration of this function.
   2299  switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) {
   2300  case kOptTcFlagFollowic: break;
   2301  case kOptTcFlagIgnore:
   2302    p_ic = true;
   2303    break;
   2304  case kOptTcFlagMatch:
   2305    p_ic = false;
   2306    break;
   2307  case kOptTcFlagFollowscs:
   2308    p_ic = ignorecase(pat);
   2309    break;
   2310  case kOptTcFlagSmart:
   2311    p_ic = ignorecase_opt(pat, true, true);
   2312    break;
   2313  default:
   2314    abort();
   2315  }
   2316 
   2317  // uncrustify:on
   2318 
   2319  int help_save = curbuf->b_help;
   2320 
   2321  findtags_state_init(&st, pat, flags, mincount);
   2322 
   2323  // Initialize a few variables
   2324  if (st.help_only) {                           // want tags from help file
   2325    curbuf->b_help = true;                      // will be restored later
   2326  }
   2327 
   2328  if (curbuf->b_help) {
   2329    // When "@ab" is specified use only the "ab" language, otherwise
   2330    // search all languages.
   2331    if (st.orgpat->len > 3 && pat[st.orgpat->len - 3] == '@'
   2332        && ASCII_ISALPHA(pat[st.orgpat->len - 2])
   2333        && ASCII_ISALPHA(pat[st.orgpat->len - 1])) {
   2334      saved_pat = xstrnsave(pat, (size_t)st.orgpat->len - 3);
   2335      st.help_lang_find = &pat[st.orgpat->len - 2];
   2336      st.orgpat->pat = saved_pat;
   2337      st.orgpat->len -= 3;
   2338    }
   2339  }
   2340  if (p_tl != 0 && st.orgpat->len > p_tl) {  // adjust for 'taglength'
   2341    st.orgpat->len = (int)p_tl;
   2342  }
   2343 
   2344  int save_emsg_off = emsg_off;
   2345  emsg_off = true;    // don't want error for invalid RE here
   2346  prepare_pats(st.orgpat, has_re);
   2347  emsg_off = save_emsg_off;
   2348  if (has_re && st.orgpat->regmatch.regprog == NULL) {
   2349    goto findtag_end;
   2350  }
   2351 
   2352  retval = findtags_apply_tfu(&st, pat, buf_ffname);
   2353  if (retval != NOTDONE) {
   2354    goto findtag_end;
   2355  }
   2356 
   2357  // re-initialize the default return value
   2358  retval = FAIL;
   2359 
   2360  // Set a flag if the file extension is .txt
   2361  if ((flags & TAG_KEEP_LANG)
   2362      && st.help_lang_find == NULL
   2363      && curbuf->b_fname != NULL
   2364      && (i = (int)strlen(curbuf->b_fname)) > 4
   2365      && STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) {
   2366    st.is_txt = true;
   2367  }
   2368 
   2369  // When finding a specified number of matches, first try with matching
   2370  // case, so binary search can be used, and try ignore-case matches in a
   2371  // second loop.
   2372  // When finding all matches, 'tagbsearch' is off, or there is no fixed
   2373  // string to look for, ignore case right away to avoid going though the
   2374  // tags files twice.
   2375  // When the tag file is case-fold sorted, it is either one or the other.
   2376  // Only ignore case when TAG_NOIC not used or 'ignorecase' set.
   2377  st.orgpat->regmatch.rm_ic = ((p_ic || !noic)
   2378                               && (findall || st.orgpat->headlen == 0 || !p_tbs));
   2379  for (int round = 1; round <= 2; round++) {
   2380    st.linear = (st.orgpat->headlen == 0 || !p_tbs || round == 2);
   2381 
   2382    // Try tag file names from tags option one by one.
   2383    for (first_file = true;
   2384         get_tagfname(&tn, first_file, st.tag_fname) == OK;
   2385         first_file = false) {
   2386      findtags_in_file(&st, flags, buf_ffname);
   2387      if (st.stop_searching) {
   2388        retval = OK;
   2389        break;
   2390      }
   2391    }   // end of for-each-file loop
   2392 
   2393    tagname_free(&tn);
   2394 
   2395    // stop searching when already did a linear search, or when TAG_NOIC
   2396    // used, and 'ignorecase' not set or already did case-ignore search
   2397    if (st.stop_searching || st.linear || (!p_ic && noic)
   2398        || st.orgpat->regmatch.rm_ic) {
   2399      break;
   2400    }
   2401 
   2402    // try another time while ignoring case
   2403    st.orgpat->regmatch.rm_ic = true;
   2404  }
   2405 
   2406  if (!st.stop_searching) {
   2407    if (!st.did_open && verbose) {  // never opened any tags file
   2408      emsg(_("E433: No tags file"));
   2409    }
   2410    retval = OK;                // It's OK even when no tag found
   2411  }
   2412 
   2413 findtag_end:
   2414  findtags_state_free(&st);
   2415 
   2416  // Move the matches from the ga_match[] arrays into one list of
   2417  // matches.  When retval == FAIL, free the matches.
   2418  if (retval == FAIL) {
   2419    st.match_count = 0;
   2420  }
   2421 
   2422  *num_matches = findtags_copy_matches(&st, matchesp);
   2423 
   2424  curbuf->b_help = help_save;
   2425  xfree(saved_pat);
   2426 
   2427  p_ic = save_p_ic;
   2428 
   2429  return retval;
   2430 }
   2431 
   2432 static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
   2433 
   2434 // Callback function for finding all "tags" and "tags-??" files in
   2435 // 'runtimepath' doc directories.
   2436 static bool found_tagfile_cb(int num_fnames, char **fnames, bool all, void *cookie)
   2437 {
   2438  for (int i = 0; i < num_fnames; i++) {
   2439    char *const tag_fname = xstrdup(fnames[i]);
   2440 
   2441 #ifdef BACKSLASH_IN_FILENAME
   2442    slash_adjust(tag_fname);
   2443 #endif
   2444    simplify_filename(tag_fname);
   2445    GA_APPEND(char *, &tag_fnames, tag_fname);
   2446 
   2447    if (!all) {
   2448      break;
   2449    }
   2450  }
   2451 
   2452  return num_fnames > 0;
   2453 }
   2454 
   2455 #if defined(EXITFREE)
   2456 void free_tag_stuff(void)
   2457 {
   2458  ga_clear_strings(&tag_fnames);
   2459  if (curwin != NULL) {
   2460    do_tag(NULL, DT_FREE, 0, 0, 0);
   2461  }
   2462  tag_freematch();
   2463 
   2464  tagstack_clear_entry(&ptag_entry);
   2465 }
   2466 
   2467 #endif
   2468 
   2469 /// Get the next name of a tag file from the tag file list.
   2470 /// For help files, use "tags" file only.
   2471 ///
   2472 /// @param tnp  holds status info
   2473 /// @param first  true when first file name is wanted
   2474 /// @param buf  pointer to buffer of MAXPATHL chars
   2475 ///
   2476 /// @return  FAIL if no more tag file names, OK otherwise.
   2477 int get_tagfname(tagname_T *tnp, int first, char *buf)
   2478 {
   2479  char *fname = NULL;
   2480 
   2481  if (first) {
   2482    CLEAR_POINTER(tnp);
   2483  }
   2484 
   2485  if (curbuf->b_help) {
   2486    // For help files it's done in a completely different way:
   2487    // Find "doc/tags" and "doc/tags-??" in all directories in
   2488    // 'runtimepath'.
   2489    if (first) {
   2490      ga_clear_strings(&tag_fnames);
   2491      ga_init(&tag_fnames, (int)sizeof(char *), 10);
   2492      do_in_runtimepath("doc/tags doc/tags-??", DIP_ALL,
   2493                        found_tagfile_cb, NULL);
   2494    }
   2495 
   2496    if (tnp->tn_hf_idx >= tag_fnames.ga_len) {
   2497      // Not found in 'runtimepath', use 'helpfile', if it exists and
   2498      // wasn't used yet, replacing "help.txt" with "tags".
   2499      if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) {
   2500        return FAIL;
   2501      }
   2502      tnp->tn_hf_idx++;
   2503      xstrlcpy(buf, p_hf, MAXPATHL - STRLEN_LITERAL("tags"));
   2504      STRCPY(path_tail(buf), "tags");
   2505 #ifdef BACKSLASH_IN_FILENAME
   2506      slash_adjust(buf);
   2507 #endif
   2508      simplify_filename(buf);
   2509 
   2510      for (int i = 0; i < tag_fnames.ga_len; i++) {
   2511        if (strcmp(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
   2512          return FAIL;  // avoid duplicate file names
   2513        }
   2514      }
   2515    } else {
   2516      xstrlcpy(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
   2517    }
   2518    return OK;
   2519  }
   2520 
   2521  if (first) {
   2522    // Init.  We make a copy of 'tags', because autocommands may change
   2523    // the value without notifying us.
   2524    tnp->tn_tags = xstrdup((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags);
   2525    tnp->tn_np = tnp->tn_tags;
   2526  }
   2527 
   2528  // Loop until we have found a file name that can be used.
   2529  // There are two states:
   2530  // tnp->tn_did_filefind_init == false: setup for next part in 'tags'.
   2531  // tnp->tn_did_filefind_init == true: find next file in this part.
   2532  while (true) {
   2533    if (tnp->tn_did_filefind_init) {
   2534      fname = vim_findfile(tnp->tn_search_ctx);
   2535      if (fname != NULL) {
   2536        break;
   2537      }
   2538 
   2539      tnp->tn_did_filefind_init = false;
   2540    } else {
   2541      char *filename = NULL;
   2542 
   2543      // Stop when used all parts of 'tags'.
   2544      if (*tnp->tn_np == NUL) {
   2545        vim_findfile_cleanup(tnp->tn_search_ctx);
   2546        tnp->tn_search_ctx = NULL;
   2547        return FAIL;
   2548      }
   2549 
   2550      // Copy next file name into buf.
   2551      buf[0] = NUL;
   2552      copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
   2553 
   2554      char *r_ptr = vim_findfile_stopdir(buf);
   2555      // move the filename one char forward and truncate the
   2556      // filepath with a NUL
   2557      filename = path_tail(buf);
   2558      if (r_ptr != NULL) {
   2559        STRMOVE(r_ptr + 1, r_ptr);
   2560        r_ptr++;
   2561      }
   2562      STRMOVE(filename + 1, filename);
   2563      *filename++ = NUL;
   2564 
   2565      tnp->tn_search_ctx = vim_findfile_init(buf, filename, strlen(filename),
   2566                                             r_ptr, 100,
   2567                                             false,                   // don't free visited list
   2568                                             FINDFILE_FILE,           // we search for a file
   2569                                             tnp->tn_search_ctx, true, curbuf->b_ffname);
   2570      if (tnp->tn_search_ctx != NULL) {
   2571        tnp->tn_did_filefind_init = true;
   2572      }
   2573    }
   2574  }
   2575 
   2576  STRCPY(buf, fname);
   2577  xfree(fname);
   2578  return OK;
   2579 }
   2580 
   2581 // Free the contents of a tagname_T that was filled by get_tagfname().
   2582 void tagname_free(tagname_T *tnp)
   2583 {
   2584  xfree(tnp->tn_tags);
   2585  vim_findfile_cleanup(tnp->tn_search_ctx);
   2586  tnp->tn_search_ctx = NULL;
   2587  ga_clear_strings(&tag_fnames);
   2588 }
   2589 
   2590 /// Parse one line from the tags file. Find start/end of tag name, start/end of
   2591 /// file name and start of search pattern.
   2592 ///
   2593 /// If is_etag is true, tagp->fname and tagp->fname_end are not set.
   2594 ///
   2595 /// @param lbuf  line to be parsed
   2596 ///
   2597 /// @return  FAIL if there is a format error in this line, OK otherwise.
   2598 static int parse_tag_line(char *lbuf, tagptrs_T *tagp)
   2599 {
   2600  // Isolate the tagname, from lbuf up to the first white
   2601  tagp->tagname = lbuf;
   2602  char *p = vim_strchr(lbuf, TAB);
   2603  if (p == NULL) {
   2604    return FAIL;
   2605  }
   2606  tagp->tagname_end = p;
   2607 
   2608  // Isolate file name, from first to second white space
   2609  if (*p != NUL) {
   2610    p++;
   2611  }
   2612  tagp->fname = p;
   2613  p = vim_strchr(p, TAB);
   2614  if (p == NULL) {
   2615    return FAIL;
   2616  }
   2617  tagp->fname_end = p;
   2618 
   2619  // find start of search command, after second white space
   2620  if (*p != NUL) {
   2621    p++;
   2622  }
   2623  if (*p == NUL) {
   2624    return FAIL;
   2625  }
   2626  tagp->command = p;
   2627 
   2628  return OK;
   2629 }
   2630 
   2631 // Check if tagname is a static tag
   2632 //
   2633 // Static tags produced by the older ctags program have the format:
   2634 //      'file:tag  file  /pattern'.
   2635 // This is only recognized when both occurrence of 'file' are the same, to
   2636 // avoid recognizing "string::string" or ":exit".
   2637 //
   2638 // Static tags produced by the new ctags program have the format:
   2639 //      'tag  file  /pattern/;"<Tab>file:'          "
   2640 //
   2641 // Return true if it is a static tag and adjust *tagname to the real tag.
   2642 // Return false if it is not a static tag.
   2643 static bool test_for_static(tagptrs_T *tagp)
   2644 {
   2645  // Check for new style static tag ":...<Tab>file:[<Tab>...]"
   2646  char *p = tagp->command;
   2647  while ((p = vim_strchr(p, '\t')) != NULL) {
   2648    p++;
   2649    if (strncmp(p, "file:", 5) == 0) {
   2650      return true;
   2651    }
   2652  }
   2653 
   2654  return false;
   2655 }
   2656 
   2657 /// @return  the length of a matching tag line.
   2658 static size_t matching_line_len(const char *const lbuf)
   2659 {
   2660  const char *p = lbuf + 1;
   2661 
   2662  // does the same thing as parse_match()
   2663  p += strlen(p) + 1;
   2664  return (size_t)(p - lbuf) + strlen(p);
   2665 }
   2666 
   2667 /// Parse a line from a matching tag.  Does not change the line itself.
   2668 ///
   2669 /// The line that we get looks like this:
   2670 /// Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf>
   2671 /// other tag: <mtt><tag_fname><NUL><NUL><lbuf>
   2672 /// without Emacs tags: <mtt><tag_fname><NUL><lbuf>
   2673 ///
   2674 /// @param lbuf  input: matching line
   2675 /// @param tagp  output: pointers into the line
   2676 ///
   2677 /// @return  OK or FAIL.
   2678 static int parse_match(char *lbuf, tagptrs_T *tagp)
   2679 {
   2680  tagp->tag_fname = lbuf + 1;
   2681  lbuf += strlen(tagp->tag_fname) + 2;
   2682 
   2683  // Find search pattern and the file name for non-etags.
   2684  int retval = parse_tag_line(lbuf, tagp);
   2685 
   2686  tagp->tagkind = NULL;
   2687  tagp->user_data = NULL;
   2688  tagp->tagline = 0;
   2689  tagp->command_end = NULL;
   2690 
   2691  if (retval != OK) {
   2692    return retval;
   2693  }
   2694 
   2695  // Try to find a kind field: "kind:<kind>" or just "<kind>"
   2696  char *p = tagp->command;
   2697  if (find_extra(&p) == OK) {
   2698    tagp->command_end = p;
   2699    if (p > tagp->command && p[-1] == '|') {
   2700      tagp->command_end = p - 1;  // drop trailing bar
   2701    }
   2702    p += 2;  // skip ";\""
   2703    if (*p++ == TAB) {
   2704      // Accept ASCII alphabetic kind characters and any multi-byte
   2705      // character.
   2706      while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) {
   2707        if (strncmp(p, "kind:", 5) == 0) {
   2708          tagp->tagkind = p + 5;
   2709        } else if (strncmp(p, "user_data:", 10) == 0) {
   2710          tagp->user_data = p + 10;
   2711        } else if (strncmp(p, "line:", 5) == 0) {
   2712          tagp->tagline = atoi(p + 5);
   2713        }
   2714        if (tagp->tagkind != NULL && tagp->user_data != NULL) {
   2715          break;
   2716        }
   2717 
   2718        char *pc = vim_strchr(p, ':');
   2719        char *pt = vim_strchr(p, '\t');
   2720        if (pc == NULL || (pt != NULL && pc > pt)) {
   2721          tagp->tagkind = p;
   2722        }
   2723        if (pt == NULL) {
   2724          break;
   2725        }
   2726        p = pt;
   2727        MB_PTR_ADV(p);
   2728      }
   2729    }
   2730  }
   2731  if (tagp->tagkind != NULL) {
   2732    for (p = tagp->tagkind;
   2733         *p && *p != '\t' && *p != '\r' && *p != '\n';
   2734         MB_PTR_ADV(p)) {}
   2735    tagp->tagkind_end = p;
   2736  }
   2737  if (tagp->user_data != NULL) {
   2738    for (p = tagp->user_data;
   2739         *p && *p != '\t' && *p != '\r' && *p != '\n';
   2740         MB_PTR_ADV(p)) {}
   2741    tagp->user_data_end = p;
   2742  }
   2743  return retval;
   2744 }
   2745 
   2746 // Find out the actual file name of a tag.  Concatenate the tags file name
   2747 // with the matching tag file name.
   2748 // Returns an allocated string.
   2749 static char *tag_full_fname(tagptrs_T *tagp)
   2750 {
   2751  char c = *tagp->fname_end;
   2752  *tagp->fname_end = NUL;
   2753  char *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
   2754  *tagp->fname_end = c;
   2755 
   2756  return fullname;
   2757 }
   2758 
   2759 /// Jump to a tag that has been found in one of the tag files
   2760 ///
   2761 /// @param lbuf_arg  line from the tags file for this tag
   2762 /// @param forceit  :ta with !
   2763 /// @param keep_help  keep help flag
   2764 ///
   2765 /// @return  OK for success, NOTAGFILE when file not found, FAIL otherwise.
   2766 static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
   2767 {
   2768  if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) {
   2769    return FAIL;
   2770  }
   2771 
   2772  char *pbuf_end;
   2773  char *tofree_fname = NULL;
   2774  tagptrs_T tagp;
   2775  int retval = FAIL;
   2776  int getfile_result = GETFILE_UNUSED;
   2777  int search_options;
   2778  win_T *curwin_save = NULL;
   2779  char *full_fname = NULL;
   2780  const bool old_KeyTyped = KeyTyped;       // getting the file may reset it
   2781  const int l_g_do_tagpreview = g_do_tagpreview;
   2782  const size_t len = matching_line_len(lbuf_arg) + 1;
   2783  char *lbuf = xmalloc(len);
   2784  memmove(lbuf, lbuf_arg, len);
   2785 
   2786  char *pbuf = xmalloc(LSIZE);  // search pattern buffer
   2787 
   2788  // parse the match line into the tagp structure
   2789  if (parse_match(lbuf, &tagp) == FAIL) {
   2790    tagp.fname_end = NULL;
   2791    goto erret;
   2792  }
   2793 
   2794  // truncate the file name, so it can be used as a string
   2795  *tagp.fname_end = NUL;
   2796  char *fname = tagp.fname;
   2797 
   2798  // copy the command to pbuf[], remove trailing CR/NL
   2799  char *str = tagp.command;
   2800  for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r';) {
   2801    *pbuf_end++ = *str++;
   2802    if (pbuf_end - pbuf + 1 >= LSIZE) {
   2803      break;
   2804    }
   2805  }
   2806  *pbuf_end = NUL;
   2807 
   2808  {
   2809    // Remove the "<Tab>fieldname:value" stuff; we don't need it here.
   2810    str = pbuf;
   2811    if (find_extra(&str) == OK) {
   2812      pbuf_end = str;
   2813      *pbuf_end = NUL;
   2814    }
   2815  }
   2816 
   2817  // Expand file name, when needed (for environment variables).
   2818  // If 'tagrelative' option set, may change file name.
   2819  fname = expand_tag_fname(fname, tagp.tag_fname, true);
   2820  tofree_fname = fname;         // free() it later
   2821 
   2822  // Check if the file with the tag exists before abandoning the current
   2823  // file.  Also accept a file name for which there is a matching BufReadCmd
   2824  // autocommand event (e.g., http://sys/file).
   2825  if (!os_path_exists(fname)
   2826      && !has_autocmd(EVENT_BUFREADCMD, fname, NULL)) {
   2827    retval = NOTAGFILE;
   2828    xfree(nofile_fname);
   2829    nofile_fname = xstrdup(fname);
   2830    goto erret;
   2831  }
   2832 
   2833  RedrawingDisabled++;
   2834 
   2835  if (l_g_do_tagpreview != 0) {
   2836    postponed_split = 0;        // don't split again below
   2837    curwin_save = curwin;       // Save current window
   2838 
   2839    // If we are reusing a window, we may change dir when
   2840    // entering it (autocommands) so turn the tag filename
   2841    // into a fullpath
   2842    if (!curwin->w_p_pvw) {
   2843      full_fname = FullName_save(fname, false);
   2844      fname = full_fname;
   2845 
   2846      // Make the preview window the current window.
   2847      // Open a preview window when needed.
   2848      prepare_tagpreview(true);
   2849    }
   2850  }
   2851 
   2852  // If it was a CTRL-W CTRL-] command split window now.  For ":tab tag"
   2853  // open a new tab page.
   2854  if (postponed_split && (swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))) {
   2855    buf_T *const existing_buf = buflist_findname_exp(fname);
   2856 
   2857    if (existing_buf != NULL) {
   2858      // If 'switchbuf' is set jump to the window containing "buf".
   2859      if (swbuf_goto_win_with_buf(existing_buf) != NULL) {
   2860        // We've switched to the buffer, the usual loading of the file
   2861        // must be skipped.
   2862        getfile_result = GETFILE_SAME_FILE;
   2863      }
   2864    }
   2865  }
   2866  if (getfile_result == GETFILE_UNUSED
   2867      && (postponed_split || cmdmod.cmod_tab != 0)) {
   2868    if (swb_flags & kOptSwbFlagVsplit) {
   2869      // If 'switchbuf' contains 'vsplit', then use a new vertically
   2870      // split window.
   2871      cmdmod.cmod_split |= WSP_VERT;
   2872    }
   2873 
   2874    if ((swb_flags & kOptSwbFlagNewtab) && cmdmod.cmod_tab == 0) {
   2875      // If 'switchbuf' contains 'newtab', then use a new tabpage
   2876      cmdmod.cmod_tab = tabpage_index(curtab) + 1;
   2877    }
   2878 
   2879    if (win_split(postponed_split > 0 ? postponed_split : 0,
   2880                  postponed_split_flags) == FAIL) {
   2881      RedrawingDisabled--;
   2882      goto erret;
   2883    }
   2884    RESET_BINDING(curwin);
   2885  }
   2886 
   2887  if (keep_help) {
   2888    // A :ta from a help file will keep the b_help flag set.  For ":ptag"
   2889    // we need to use the flag from the window where we came from.
   2890    if (l_g_do_tagpreview != 0) {
   2891      keep_help_flag = bt_help(curwin_save->w_buffer);
   2892    } else {
   2893      keep_help_flag = curbuf->b_help;
   2894    }
   2895  }
   2896 
   2897  if (getfile_result == GETFILE_UNUSED) {
   2898    // Careful: getfile() may trigger autocommands and call jumpto_tag()
   2899    // recursively.
   2900    getfile_result = getfile(0, fname, NULL, true, 0, forceit);
   2901  }
   2902  keep_help_flag = false;
   2903 
   2904  if (GETFILE_SUCCESS(getfile_result)) {    // got to the right file
   2905    curwin->w_set_curswant = true;
   2906    postponed_split = 0;
   2907 
   2908    const optmagic_T save_magic_overruled = magic_overruled;
   2909    magic_overruled = OPTION_MAGIC_OFF;  // always execute with 'nomagic'
   2910    // Save value of no_hlsearch, jumping to a tag is not a real search
   2911    const bool save_no_hlsearch = no_hlsearch;
   2912 
   2913    // If 'cpoptions' contains 't', store the search pattern for the "n"
   2914    // command.  If 'cpoptions' does not contain 't', the search pattern
   2915    // is not stored.
   2916    if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) {
   2917      search_options = 0;
   2918    } else {
   2919      search_options = SEARCH_KEEP;
   2920    }
   2921 
   2922    // If the command is a search, try here.
   2923    //
   2924    // Reset 'smartcase' for the search, since the search pattern was not
   2925    // typed by the user.
   2926    // Only use do_search() when there is a full search command, without
   2927    // anything following.
   2928    str = pbuf;
   2929    if (pbuf[0] == '/' || pbuf[0] == '?') {
   2930      str = skip_regexp(pbuf + 1, pbuf[0], false) + 1;
   2931    }
   2932    if (str > pbuf_end - 1) {   // search command with nothing following
   2933      size_t pbuflen = (size_t)(pbuf_end - pbuf);
   2934 
   2935      bool save_p_ws = p_ws;
   2936      int save_p_ic = p_ic;
   2937      int save_p_scs = p_scs;
   2938      p_ws = true;              // need 'wrapscan' for backward searches
   2939      p_ic = false;             // don't ignore case now
   2940      p_scs = false;
   2941      linenr_T save_lnum = curwin->w_cursor.lnum;
   2942 
   2943      curwin->w_cursor.lnum = tagp.tagline > 0
   2944                              // start search before line from "line:" field
   2945                              ? tagp.tagline - 1
   2946                              // start search before first line
   2947                              : 0;
   2948 
   2949      if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1,
   2950                    search_options, NULL)) {
   2951        retval = OK;
   2952      } else {
   2953        int found = 1;
   2954 
   2955        // try again, ignore case now
   2956        p_ic = true;
   2957        if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, 1,
   2958                       search_options, NULL)) {
   2959          // Failed to find pattern, take a guess: "^func  ("
   2960          found = 2;
   2961          test_for_static(&tagp);
   2962          char cc = *tagp.tagname_end;
   2963          *tagp.tagname_end = NUL;
   2964          pbuflen = (size_t)snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
   2965          if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) {
   2966            // Guess again: "^char * \<func  ("
   2967            pbuflen = (size_t)snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
   2968                                       tagp.tagname);
   2969            if (!do_search(NULL, '/', '/', pbuf, pbuflen, 1, search_options, NULL)) {
   2970              found = 0;
   2971            }
   2972          }
   2973          *tagp.tagname_end = cc;
   2974        }
   2975        if (found == 0) {
   2976          emsg(_("E434: Can't find tag pattern"));
   2977          curwin->w_cursor.lnum = save_lnum;
   2978        } else {
   2979          // Only give a message when really guessed, not when 'ic'
   2980          // is set and match found while ignoring case.
   2981          if (found == 2 || !save_p_ic) {
   2982            msg(_("E435: Couldn't find tag, just guessing!"), 0);
   2983            if (!msg_scrolled && msg_silent == 0) {
   2984              msg_delay(1010, true);
   2985            }
   2986          }
   2987          retval = OK;
   2988        }
   2989      }
   2990      p_ws = save_p_ws;
   2991      p_ic = save_p_ic;
   2992      p_scs = save_p_scs;
   2993 
   2994      // A search command may have positioned the cursor beyond the end
   2995      // of the line.  May need to correct that here.
   2996      check_cursor(curwin);
   2997    } else {
   2998      const int save_secure = secure;
   2999 
   3000      // Setup the sandbox for executing the command from the tags file.
   3001      secure = 1;
   3002      sandbox++;
   3003      curwin->w_cursor.lnum = 1;  // start command in line 1
   3004      curwin->w_cursor.col = 0;
   3005      curwin->w_cursor.coladd = 0;
   3006      do_cmdline_cmd(pbuf);
   3007      retval = OK;
   3008 
   3009      // When the command has done something that is not allowed make sure
   3010      // the error message can be seen.
   3011      if (secure == 2) {
   3012        wait_return(true);
   3013      }
   3014      secure = save_secure;
   3015      sandbox--;
   3016    }
   3017 
   3018    magic_overruled = save_magic_overruled;
   3019    // restore no_hlsearch when keeping the old search pattern
   3020    if (search_options) {
   3021      set_no_hlsearch(save_no_hlsearch);
   3022    }
   3023 
   3024    // Return OK if jumped to another file (at least we found the file!).
   3025    if (getfile_result == GETFILE_OPEN_OTHER) {
   3026      retval = OK;
   3027    }
   3028 
   3029    if (retval == OK) {
   3030      // For a help buffer: Put the cursor line at the top of the window,
   3031      // the help subject will be below it.
   3032      if (curbuf->b_help) {
   3033        set_topline(curwin, curwin->w_cursor.lnum);
   3034      }
   3035      if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
   3036        foldOpenCursor();
   3037      }
   3038    }
   3039 
   3040    if (l_g_do_tagpreview != 0
   3041        && curwin != curwin_save && win_valid(curwin_save)) {
   3042      // Return cursor to where we were
   3043      validate_cursor(curwin);
   3044      redraw_later(curwin, UPD_VALID);
   3045      win_enter(curwin_save, true);
   3046    }
   3047 
   3048    RedrawingDisabled--;
   3049  } else {
   3050    RedrawingDisabled--;
   3051    if (postponed_split) {              // close the window
   3052      win_close(curwin, false, false);
   3053      postponed_split = 0;
   3054    }
   3055  }
   3056 
   3057 erret:
   3058  g_do_tagpreview = 0;  // For next time
   3059  xfree(lbuf);
   3060  xfree(pbuf);
   3061  xfree(tofree_fname);
   3062  xfree(full_fname);
   3063 
   3064  return retval;
   3065 }
   3066 
   3067 /// If "expand" is true, expand wildcards in fname.
   3068 /// If 'tagrelative' option set, change fname (name of file containing tag)
   3069 /// according to tag_fname (name of tag file containing fname).
   3070 ///
   3071 /// @return  a pointer to allocated memory.
   3072 static char *expand_tag_fname(char *fname, char *const tag_fname, const bool expand)
   3073 {
   3074  char *p;
   3075  char *expanded_fname = NULL;
   3076  expand_T xpc;
   3077 
   3078  // Expand file name (for environment variables) when needed.
   3079  if (expand && path_has_wildcard(fname)) {
   3080    ExpandInit(&xpc);
   3081    xpc.xp_context = EXPAND_FILES;
   3082    expanded_fname = ExpandOne(&xpc, fname, NULL,
   3083                               WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
   3084    if (expanded_fname != NULL) {
   3085      fname = expanded_fname;
   3086    }
   3087  }
   3088 
   3089  char *retval;
   3090  if ((p_tr || curbuf->b_help)
   3091      && !vim_isAbsName(fname)
   3092      && (p = path_tail(tag_fname)) != tag_fname) {
   3093    retval = xmalloc(MAXPATHL);
   3094    STRCPY(retval, tag_fname);
   3095    xstrlcpy(retval + (p - tag_fname), fname, (size_t)(MAXPATHL - (p - tag_fname)));
   3096    // Translate names like "src/a/../b/file.c" into "src/b/file.c".
   3097    simplify_filename(retval);
   3098  } else {
   3099    retval = xstrdup(fname);
   3100  }
   3101 
   3102  xfree(expanded_fname);
   3103 
   3104  return retval;
   3105 }
   3106 
   3107 /// Check if we have a tag for the buffer with name "buf_ffname".
   3108 /// This is a bit slow, because of the full path compare in path_full_compare().
   3109 ///
   3110 /// @return  true if tag for file "fname" if tag file "tag_fname" is for current
   3111 ///          file.
   3112 static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname)
   3113 {
   3114  int retval = false;
   3115 
   3116  if (buf_ffname != NULL) {     // if the buffer has a name
   3117    char c;
   3118    {
   3119      c = *fname_end;
   3120      *fname_end = NUL;
   3121    }
   3122    char *fullname = expand_tag_fname(fname, tag_fname, true);
   3123    retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles);
   3124    xfree(fullname);
   3125    *fname_end = c;
   3126  }
   3127 
   3128  return retval;
   3129 }
   3130 
   3131 // Find the end of the tagaddress.
   3132 // Return OK if ";\"" is following, FAIL otherwise.
   3133 static int find_extra(char **pp)
   3134 {
   3135  char *str = *pp;
   3136  char first_char = **pp;
   3137 
   3138  // Repeat for addresses separated with ';'
   3139  while (true) {
   3140    if (ascii_isdigit(*str)) {
   3141      str = skipdigits(str + 1);
   3142    } else if (*str == '/' || *str == '?') {
   3143      str = skip_regexp(str + 1, *str, false);
   3144      if (*str != first_char) {
   3145        str = NULL;
   3146      } else {
   3147        str++;
   3148      }
   3149    } else {
   3150      // not a line number or search string, look for terminator.
   3151      str = strstr(str, "|;\"");
   3152      if (str != NULL) {
   3153        str++;
   3154        break;
   3155      }
   3156    }
   3157    if (str == NULL || *str != ';'
   3158        || !(ascii_isdigit(str[1]) || str[1] == '/' || str[1] == '?')) {
   3159      break;
   3160    }
   3161    str++;  // skip ';'
   3162    first_char = *str;
   3163  }
   3164 
   3165  if (str != NULL && strncmp(str, ";\"", 2) == 0) {
   3166    *pp = str;
   3167    return OK;
   3168  }
   3169  return FAIL;
   3170 }
   3171 
   3172 /// Free a single entry in a tag stack
   3173 void tagstack_clear_entry(taggy_T *item)
   3174 {
   3175  XFREE_CLEAR(item->tagname);
   3176  XFREE_CLEAR(item->user_data);
   3177 }
   3178 
   3179 /// @param tagnames  expand tag names
   3180 int expand_tags(bool tagnames, char *pat, int *num_file, char ***file)
   3181 {
   3182  size_t name_buf_size = 100;
   3183  int ret;
   3184 
   3185  char *name_buf = xmalloc(name_buf_size);
   3186 
   3187  int extra_flag = tagnames ? TAG_NAMES : 0;
   3188  if (pat[0] == '/') {
   3189    ret = find_tags(pat + 1, num_file, file,
   3190                    TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
   3191                    TAG_MANY, curbuf->b_ffname);
   3192  } else {
   3193    ret = find_tags(pat, num_file, file,
   3194                    TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
   3195                    TAG_MANY, curbuf->b_ffname);
   3196  }
   3197  if (ret == OK && !tagnames) {
   3198    // Reorganize the tags for display and matching as strings of:
   3199    // "<tagname>\0<kind>\0<filename>\0"
   3200    for (int i = 0; i < *num_file; i++) {
   3201      tagptrs_T t_p;
   3202      parse_match((*file)[i], &t_p);
   3203      size_t len = (size_t)(t_p.tagname_end - t_p.tagname);
   3204      if (len > name_buf_size - 3) {
   3205        name_buf_size = len + 3;
   3206        char *buf = xrealloc(name_buf, name_buf_size);
   3207        name_buf = buf;
   3208      }
   3209 
   3210      memmove(name_buf, t_p.tagname, len);
   3211      name_buf[len++] = 0;
   3212      name_buf[len++] = (t_p.tagkind != NULL && *t_p.tagkind)
   3213                        ? *t_p.tagkind : 'f';
   3214      name_buf[len++] = 0;
   3215      memmove((*file)[i] + len, t_p.fname, (size_t)(t_p.fname_end - t_p.fname));
   3216      (*file)[i][len + (size_t)(t_p.fname_end - t_p.fname)] = 0;
   3217      memmove((*file)[i], name_buf, len);
   3218    }
   3219  }
   3220  xfree(name_buf);
   3221  return ret;
   3222 }
   3223 
   3224 /// Add a tag field to the dictionary "dict".
   3225 /// Return OK or FAIL.
   3226 ///
   3227 /// @param start  start of the value
   3228 /// @param end  after the value; can be NULL
   3229 static int add_tag_field(dict_T *dict, const char *field_name, const char *start, const char *end)
   3230  FUNC_ATTR_NONNULL_ARG(1, 2)
   3231 {
   3232  // Check that the field name doesn't exist yet.
   3233  if (tv_dict_find(dict, field_name, -1) != NULL) {
   3234    if (p_verbose > 0) {
   3235      verbose_enter();
   3236      smsg(0, _("Duplicate field name: %s"), field_name);
   3237      verbose_leave();
   3238    }
   3239    return FAIL;
   3240  }
   3241  int len = 0;
   3242  char *buf = xmalloc(MAXPATHL);
   3243  if (start != NULL) {
   3244    if (end == NULL) {
   3245      end = start + strlen(start);
   3246      while (end > start && (end[-1] == '\r' || end[-1] == '\n')) {
   3247        end--;
   3248      }
   3249    }
   3250    len = MIN((int)(end - start), MAXPATHL - 1);
   3251    xmemcpyz(buf, start, (size_t)len);
   3252  }
   3253  buf[len] = NUL;
   3254  int retval = tv_dict_add_str(dict, field_name, strlen(field_name), buf);
   3255  xfree(buf);
   3256  return retval;
   3257 }
   3258 
   3259 /// Add the tags matching the specified pattern "pat" to the list "list"
   3260 /// as a dictionary. Use "buf_fname" for priority, unless NULL.
   3261 int get_tags(list_T *list, char *pat, char *buf_fname)
   3262 {
   3263  int num_matches;
   3264  char **matches;
   3265  tagptrs_T tp;
   3266 
   3267  int ret = find_tags(pat, &num_matches, &matches, TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname);
   3268  if (ret != OK || num_matches <= 0) {
   3269    return ret;
   3270  }
   3271 
   3272  for (int i = 0; i < num_matches; i++) {
   3273    if (parse_match(matches[i], &tp) == FAIL) {
   3274      xfree(matches[i]);
   3275      continue;
   3276    }
   3277 
   3278    bool is_static = test_for_static(&tp);
   3279 
   3280    // Skip pseudo-tag lines.
   3281    if (strncmp(tp.tagname, "!_TAG_", 6) == 0) {
   3282      xfree(matches[i]);
   3283      continue;
   3284    }
   3285 
   3286    dict_T *dict = tv_dict_alloc();
   3287    tv_list_append_dict(list, dict);
   3288 
   3289    char *full_fname = tag_full_fname(&tp);
   3290    if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL
   3291        || add_tag_field(dict, "filename", full_fname, NULL) == FAIL
   3292        || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL
   3293        || add_tag_field(dict, "kind", tp.tagkind,
   3294                         tp.tagkind ? tp.tagkind_end : NULL) == FAIL
   3295        || tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) {
   3296      ret = FAIL;
   3297    }
   3298 
   3299    xfree(full_fname);
   3300 
   3301    if (tp.command_end != NULL) {
   3302      for (char *p = tp.command_end + 3;
   3303           *p != NUL && *p != '\n' && *p != '\r';
   3304           MB_PTR_ADV(p)) {
   3305        if (p == tp.tagkind
   3306            || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) {
   3307          // skip "kind:<kind>" and "<kind>"
   3308          p = tp.tagkind_end - 1;
   3309        } else if (strncmp(p, "file:", 5) == 0) {
   3310          // skip "file:" (static tag)
   3311          p += 4;
   3312        } else if (!ascii_iswhite(*p)) {
   3313          int len;
   3314 
   3315          // Add extra field as a dict entry.  Fields are
   3316          // separated by Tabs.
   3317          char *n = p;
   3318          while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') {
   3319            p++;
   3320          }
   3321          len = (int)(p - n);
   3322          if (*p == ':' && len > 0) {
   3323            char *s = ++p;
   3324            while (*p != NUL && (uint8_t)(*p) >= ' ') {
   3325              p++;
   3326            }
   3327            n[len] = NUL;
   3328            if (add_tag_field(dict, n, s, p) == FAIL) {
   3329              ret = FAIL;
   3330            }
   3331            n[len] = ':';
   3332          } else {
   3333            // Skip field without colon.
   3334            while (*p != NUL && (uint8_t)(*p) >= ' ') {
   3335              p++;
   3336            }
   3337          }
   3338          if (*p == NUL) {
   3339            break;
   3340          }
   3341        }
   3342      }
   3343    }
   3344 
   3345    xfree(matches[i]);
   3346  }
   3347  xfree(matches);
   3348  return ret;
   3349 }
   3350 
   3351 // Return information about 'tag' in dict 'retdict'.
   3352 static void get_tag_details(taggy_T *tag, dict_T *retdict)
   3353 {
   3354  tv_dict_add_str(retdict, S_LEN("tagname"), tag->tagname);
   3355  tv_dict_add_nr(retdict, S_LEN("matchnr"), tag->cur_match + 1);
   3356  tv_dict_add_nr(retdict, S_LEN("bufnr"), tag->cur_fnum);
   3357  if (tag->user_data) {
   3358    tv_dict_add_str(retdict, S_LEN("user_data"), tag->user_data);
   3359  }
   3360 
   3361  list_T *pos = tv_list_alloc(4);
   3362  tv_dict_add_list(retdict, S_LEN("from"), pos);
   3363 
   3364  fmark_T *fmark = &tag->fmark;
   3365  tv_list_append_number(pos,
   3366                        (varnumber_T)(fmark->fnum != -1 ? fmark->fnum : 0));
   3367  tv_list_append_number(pos, (varnumber_T)fmark->mark.lnum);
   3368  tv_list_append_number(pos, (varnumber_T)(fmark->mark.col == MAXCOL
   3369                                           ? MAXCOL : fmark->mark.col + 1));
   3370  tv_list_append_number(pos, (varnumber_T)fmark->mark.coladd);
   3371 }
   3372 
   3373 // Return the tag stack entries of the specified window 'wp' in dictionary
   3374 // 'retdict'.
   3375 void get_tagstack(win_T *wp, dict_T *retdict)
   3376 {
   3377  tv_dict_add_nr(retdict, S_LEN("length"), wp->w_tagstacklen);
   3378  tv_dict_add_nr(retdict, S_LEN("curidx"), wp->w_tagstackidx + 1);
   3379  list_T *l = tv_list_alloc(2);
   3380  tv_dict_add_list(retdict, S_LEN("items"), l);
   3381 
   3382  for (int i = 0; i < wp->w_tagstacklen; i++) {
   3383    dict_T *d = tv_dict_alloc();
   3384    tv_list_append_dict(l, d);
   3385    get_tag_details(&wp->w_tagstack[i], d);
   3386  }
   3387 }
   3388 
   3389 // Free all the entries in the tag stack of the specified window
   3390 static void tagstack_clear(win_T *wp)
   3391 {
   3392  // Free the current tag stack
   3393  for (int i = 0; i < wp->w_tagstacklen; i++) {
   3394    tagstack_clear_entry(&wp->w_tagstack[i]);
   3395  }
   3396  wp->w_tagstacklen = 0;
   3397  wp->w_tagstackidx = 0;
   3398 }
   3399 
   3400 // Remove the oldest entry from the tag stack and shift the rest of
   3401 // the entries to free up the top of the stack.
   3402 static void tagstack_shift(win_T *wp)
   3403 {
   3404  taggy_T *tagstack = wp->w_tagstack;
   3405  tagstack_clear_entry(&tagstack[0]);
   3406  for (int i = 1; i < wp->w_tagstacklen; i++) {
   3407    tagstack[i - 1] = tagstack[i];
   3408  }
   3409  wp->w_tagstacklen--;
   3410 }
   3411 
   3412 /// Push a new item to the tag stack
   3413 static void tagstack_push_item(win_T *wp, char *tagname, int cur_fnum, int cur_match, pos_T mark,
   3414                               int fnum, char *user_data)
   3415 {
   3416  taggy_T *tagstack = wp->w_tagstack;
   3417  int idx = wp->w_tagstacklen;  // top of the stack
   3418 
   3419  // if the tagstack is full: remove the oldest entry
   3420  if (idx >= TAGSTACKSIZE) {
   3421    tagstack_shift(wp);
   3422    idx = TAGSTACKSIZE - 1;
   3423  }
   3424 
   3425  wp->w_tagstacklen++;
   3426  tagstack[idx].tagname = tagname;
   3427  tagstack[idx].cur_fnum = cur_fnum;
   3428  tagstack[idx].cur_match = cur_match;
   3429  tagstack[idx].cur_match = MAX(tagstack[idx].cur_match, 0);
   3430  tagstack[idx].fmark.mark = mark;
   3431  tagstack[idx].fmark.fnum = fnum;
   3432  tagstack[idx].fmark.view = (fmarkv_T)INIT_FMARKV;
   3433  tagstack[idx].user_data = user_data;
   3434 }
   3435 
   3436 /// Add a list of items to the tag stack in the specified window
   3437 static void tagstack_push_items(win_T *wp, list_T *l)
   3438 {
   3439  dictitem_T *di;
   3440  char *tagname;
   3441  pos_T mark;
   3442  int fnum;
   3443 
   3444  // Add one entry at a time to the tag stack
   3445  for (listitem_T *li = tv_list_first(l); li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
   3446    if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT
   3447        || TV_LIST_ITEM_TV(li)->vval.v_dict == NULL) {
   3448      continue;  // Skip non-dict items
   3449    }
   3450    dict_T *itemdict = TV_LIST_ITEM_TV(li)->vval.v_dict;
   3451 
   3452    // parse 'from' for the cursor position before the tag jump
   3453    if ((di = tv_dict_find(itemdict, "from", -1)) == NULL) {
   3454      continue;
   3455    }
   3456    if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) {
   3457      continue;
   3458    }
   3459    if ((tagname = tv_dict_get_string(itemdict, "tagname", true)) == NULL) {
   3460      continue;
   3461    }
   3462 
   3463    if (mark.col > 0) {
   3464      mark.col--;
   3465    }
   3466    tagstack_push_item(wp,
   3467                       tagname,
   3468                       (int)tv_dict_get_number(itemdict, "bufnr"),
   3469                       (int)tv_dict_get_number(itemdict, "matchnr") - 1,
   3470                       mark, fnum,
   3471                       tv_dict_get_string(itemdict, "user_data", true));
   3472  }
   3473 }
   3474 
   3475 // Set the current index in the tag stack. Valid values are between 0
   3476 // and the stack length (inclusive).
   3477 static void tagstack_set_curidx(win_T *wp, int curidx)
   3478 {
   3479  wp->w_tagstackidx = curidx;
   3480  // sanity check
   3481  wp->w_tagstackidx = MIN(MAX(wp->w_tagstackidx, 0), wp->w_tagstacklen);
   3482 }
   3483 
   3484 // Set the tag stack entries of the specified window.
   3485 // 'action' is set to one of:
   3486 //    'a' for append
   3487 //    'r' for replace
   3488 //    't' for truncate
   3489 int set_tagstack(win_T *wp, const dict_T *d, int action)
   3490  FUNC_ATTR_NONNULL_ARG(1)
   3491 {
   3492  // not allowed to alter the tag stack entries from inside tagfunc
   3493  if (tfu_in_use) {
   3494    emsg(_(e_cannot_modify_tag_stack_within_tagfunc));
   3495    return FAIL;
   3496  }
   3497 
   3498  dictitem_T *di;
   3499  list_T *l = NULL;
   3500 
   3501  if ((di = tv_dict_find(d, "items", -1)) != NULL) {
   3502    if (di->di_tv.v_type != VAR_LIST) {
   3503      emsg(_(e_listreq));
   3504      return FAIL;
   3505    }
   3506    l = di->di_tv.vval.v_list;
   3507  }
   3508 
   3509  if ((di = tv_dict_find(d, "curidx", -1)) != NULL) {
   3510    tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
   3511  }
   3512 
   3513  if (action == 't') {  // truncate the stack
   3514    taggy_T *const tagstack = wp->w_tagstack;
   3515    const int tagstackidx = wp->w_tagstackidx;
   3516    int tagstacklen = wp->w_tagstacklen;
   3517 
   3518    // delete all the tag stack entries above the current entry
   3519    while (tagstackidx < tagstacklen) {
   3520      tagstack_clear_entry(&tagstack[--tagstacklen]);
   3521    }
   3522    wp->w_tagstacklen = tagstacklen;
   3523  }
   3524 
   3525  if (l != NULL) {
   3526    if (action == 'r') {  // replace the stack
   3527      tagstack_clear(wp);
   3528    }
   3529 
   3530    tagstack_push_items(wp, l);
   3531    // set the current index after the last entry
   3532    wp->w_tagstackidx = wp->w_tagstacklen;
   3533  }
   3534 
   3535  return OK;
   3536 }