aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/tag.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/nvim/tag.c
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-usermarks.tar.gz
rneovim-usermarks.tar.bz2
rneovim-usermarks.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/nvim/tag.c')
-rw-r--r--src/nvim/tag.c2728
1 files changed, 1429 insertions, 1299 deletions
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 65c56bf01b..197184c181 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -1,34 +1,41 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * Code to handle tags and the tag stack
- */
+// Code to handle tags and the tag stack
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/gettext.h"
+#include "nvim/globals.h"
+#include "nvim/hashtab.h"
#include "nvim/help.h"
-#include "nvim/if_cscope.h"
+#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
+#include "nvim/macros.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
@@ -38,49 +45,47 @@
#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/pos.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
-/*
- * Structure to hold pointers to various items in a tag line.
- */
+// Structure to hold pointers to various items in a tag line.
typedef struct tag_pointers {
// filled in by parse_tag_line():
- char_u *tagname; // start of tag name (skip "file:")
- char_u *tagname_end; // char after tag name
- char_u *fname; // first char of file name
- char_u *fname_end; // char after file name
- char_u *command; // first char of command
+ char *tagname; // start of tag name (skip "file:")
+ char *tagname_end; // char after tag name
+ char *fname; // first char of file name
+ char *fname_end; // char after file name
+ char *command; // first char of command
// filled in by parse_match():
- char_u *command_end; // first char after command
- char_u *tag_fname; // file name of the tags file. This is used
+ char *command_end; // first char after command
+ char *tag_fname; // file name of the tags file. This is used
// when 'tr' is set.
- char_u *tagkind; // "kind:" value
- char_u *tagkind_end; // end of tagkind
- char_u *user_data; // user_data string
- char_u *user_data_end; // end of user_data
- linenr_T tagline; // "line:" value
+ char *tagkind; // "kind:" value
+ char *tagkind_end; // end of tagkind
+ char *user_data; // user_data string
+ char *user_data_end; // end of user_data
+ linenr_T tagline; // "line:" value
} tagptrs_T;
-/*
- * Structure to hold info about the tag pattern being used.
- */
+// Structure to hold info about the tag pattern being used.
typedef struct {
- char_u *pat; // the pattern
- int len; // length of pat[]
- char_u *head; // start of pattern head
- int headlen; // length of head[]
- regmatch_T regmatch; // regexp program, may be NULL
+ char *pat; // the pattern
+ int len; // length of pat[]
+ char *head; // start of pattern head
+ int headlen; // length of head[]
+ regmatch_T regmatch; // regexp program, may be NULL
} pat_T;
// The matching tags are first stored in one of the hash tables. In
@@ -88,45 +93,163 @@ typedef struct {
// ht_match[] is used to find duplicates, ga_match[] to keep them in sequence.
// At the end, the matches from ga_match[] are concatenated, to make a list
// sorted on priority.
-#define MT_ST_CUR 0 // static match in current file
-#define MT_GL_CUR 1 // global match in current file
-#define MT_GL_OTH 2 // global match in other file
-#define MT_ST_OTH 3 // static match in other file
-#define MT_IC_OFF 4 // add for icase match
-#define MT_RE_OFF 8 // add for regexp match
-#define MT_MASK 7 // mask for printing priority
-#define MT_COUNT 16
+enum {
+ MT_ST_CUR = 0, // static match in current file
+ MT_GL_CUR = 1, // global match in current file
+ MT_GL_OTH = 2, // global match in other file
+ MT_ST_OTH = 3, // static match in other file
+ MT_IC_OFF = 4, // add for icase match
+ MT_RE_OFF = 8, // add for regexp match
+ MT_MASK = 7, // mask for printing priority
+ MT_COUNT = 16,
+};
static char *mt_names[MT_COUNT/2] =
{ "FSC", "F C", "F ", "FS ", " SC", " C", " ", " S " };
#define NOTAGFILE 99 // return value for jumpto_tag
-static char_u *nofile_fname = NULL; // fname for NOTAGFILE error
+static char *nofile_fname = NULL; // fname for NOTAGFILE error
+
+/// Return values used when reading lines from a tags file.
+typedef enum {
+ TAGS_READ_SUCCESS = 1,
+ TAGS_READ_EOF,
+ TAGS_READ_IGNORE,
+} tags_read_status_T;
+
+/// States used during a tags search
+typedef enum {
+ TS_START, ///< at start of file
+ TS_LINEAR, ///< linear searching forward, till EOF
+ TS_BINARY, ///< binary searching
+ TS_SKIP_BACK, ///< skipping backwards
+ TS_STEP_FORWARD, ///< stepping forwards
+} tagsearch_state_T;
+
+/// Binary search file offsets in a tags file
+typedef struct {
+ off_T low_offset; ///< offset for first char of first line that
+ ///< could match
+ off_T high_offset; ///< offset of char after last line that could
+ ///< match
+ off_T curr_offset; ///< Current file offset in search range
+ off_T curr_offset_used; ///< curr_offset used when skipping back
+ off_T match_offset; ///< Where the binary search found a tag
+ int low_char; ///< first char at low_offset
+ int high_char; ///< first char at high_offset
+} tagsearch_info_T;
+
+/// Return values used when matching tags against a pattern.
+typedef enum {
+ TAG_MATCH_SUCCESS = 1,
+ TAG_MATCH_FAIL,
+ TAG_MATCH_STOP,
+ TAG_MATCH_NEXT,
+} tagmatch_status_T;
+
+/// Arguments used for matching tags read from a tags file against a pattern.
+typedef struct {
+ int matchoff; ///< tag match offset
+ bool match_re; ///< true if the tag matches a regexp
+ bool match_no_ic; ///< true if the tag matches with case
+ bool has_re; ///< regular expression used
+ bool sortic; ///< tags file sorted ignoring case (foldcase)
+ bool sort_error; ///< tags file not sorted
+} findtags_match_args_T;
+
+/// State information used during a tag search
+typedef struct {
+ tagsearch_state_T state; ///< tag search state
+ bool stop_searching; ///< stop when match found or error
+ pat_T *orgpat; ///< holds unconverted pattern info
+ char *lbuf; ///< line buffer
+ int lbuf_size; ///< length of lbuf
+ char *tag_fname; ///< name of the tag file
+ FILE *fp; ///< current tags file pointer
+ int flags; ///< flags used for tag search
+ int tag_file_sorted; ///< !_TAG_FILE_SORTED value
+ bool get_searchpat; ///< used for 'showfulltag'
+ bool help_only; ///< only search for help tags
+ bool did_open; ///< did open a tag file
+ int mincount; ///< MAXCOL: find all matches
+ ///< other: minimal number of matches
+ bool linear; ///< do a linear search
+ vimconv_T vimconv;
+ char help_lang[3]; ///< lang of current tags file
+ int help_pri; ///< help language priority
+ char *help_lang_find; ///< lang to be found
+ bool is_txt; ///< flag of file extension
+ int match_count; ///< number of matches found
+ garray_T ga_match[MT_COUNT]; ///< stores matches in sequence
+ hashtab_T ht_match[MT_COUNT]; ///< stores matches by key
+} findtags_state_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tag.c.generated.h"
#endif
-static char_u *bottommsg = (char_u *)N_("E555: at bottom of tag stack");
-static char_u *topmsg = (char_u *)N_("E556: at top of tag stack");
-static char_u *recurmsg
- = (char_u *)N_("E986: cannot modify the tag stack within tagfunc");
-static char_u *tfu_inv_ret_msg
- = (char_u *)N_("E987: invalid return value from tagfunc");
+static char *bottommsg = N_("E555: at bottom of tag stack");
+static char *topmsg = N_("E556: at top of tag stack");
+static char *recurmsg = N_("E986: cannot modify the tag stack within tagfunc");
+static char *tfu_inv_ret_msg = N_("E987: invalid return value from tagfunc");
+static char e_window_unexpectedly_close_while_searching_for_tags[]
+ = N_("E1299: Window unexpectedly closed while searching for tags");
-static char_u *tagmatchname = NULL; // name of last used tag
+static char *tagmatchname = NULL; // name of last used tag
-/*
- * Tag for preview window is remembered separately, to avoid messing up the
- * normal tagstack.
- */
+// Tag for preview window is remembered separately, to avoid messing up the
+// normal tagstack.
static taggy_T ptag_entry = { NULL, INIT_FMARK, 0, 0, NULL };
static int tfu_in_use = false; // disallow recursive call of tagfunc
+static Callback tfu_cb; // 'tagfunc' callback function
// Used instead of NUL to separate tag fields in the growarrays.
#define TAG_SEP 0x02
+/// Reads the 'tagfunc' option value and convert that to a callback value.
+/// Invoked when the 'tagfunc' option is set. The option value can be a name of
+/// a function (string), or function(<name>) or funcref(<name>) or a lambda.
+void set_tagfunc_option(char **errmsg)
+{
+ callback_free(&tfu_cb);
+ callback_free(&curbuf->b_tfu_cb);
+
+ if (*curbuf->b_p_tfu == NUL) {
+ return;
+ }
+
+ if (option_set_callback_func(curbuf->b_p_tfu, &tfu_cb) == FAIL) {
+ *errmsg = e_invarg;
+ }
+
+ callback_copy(&curbuf->b_tfu_cb, &tfu_cb);
+}
+
+#if defined(EXITFREE)
+void free_tagfunc_option(void)
+{
+ callback_free(&tfu_cb);
+}
+#endif
+
+/// Mark the global 'tagfunc' callback with "copyID" so that it is not garbage
+/// collected.
+bool set_ref_in_tagfunc(int copyID)
+{
+ return set_ref_in_callback(&tfu_cb, copyID, NULL, NULL);
+}
+
+/// Copy the global 'tagfunc' callback function to the buffer-local 'tagfunc'
+/// callback for 'buf'.
+void set_buflocal_tfu_callback(buf_T *buf)
+{
+ callback_free(&buf->b_tfu_cb);
+ if (tfu_cb.type != kCallbackNone) {
+ callback_copy(&buf->b_tfu_cb, &tfu_cb);
+ }
+}
+
/// Jump to tag; handling of tag commands and tag stack
///
/// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack
@@ -140,16 +263,13 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc
/// type == DT_LAST: jump to last match of same tag
/// type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches
/// type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list
-/// type == DT_CSCOPE: use cscope to find the tag
/// type == DT_LTAG: use location list for displaying tag matches
/// type == DT_FREE: free cached matches
///
-/// for cscope, returns true if we jumped to tag or aborted, false otherwise
-///
/// @param tag tag (pattern) to jump to
/// @param forceit :ta with !
/// @param verbose print "tag not found" message
-bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
+void do_tag(char *tag, int type, int count, int forceit, int verbose)
{
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
@@ -166,13 +286,13 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
int error_cur_match = 0;
int save_pos = false;
fmark_T saved_fmark;
- bool jumped_to_tag = false;
int new_num_matches;
char **new_matches;
int use_tagstack;
int skip_msg = false;
- char_u *buf_ffname = (char_u *)curbuf->b_ffname; // name for priority computation
+ char *buf_ffname = curbuf->b_ffname; // name for priority computation
int use_tfu = 1;
+ char *tofree = NULL;
// remember the matches for the last used tag
static int num_matches = 0;
@@ -182,16 +302,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (tfu_in_use) {
emsg(_(recurmsg));
- return false;
+ return;
}
#ifdef EXITFREE
if (type == DT_FREE) {
// remove the list of matches
FreeWild(num_matches, matches);
- cs_free_tags();
num_matches = 0;
- return false;
+ return;
}
#endif
@@ -215,7 +334,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
new_tag = true;
if (g_do_tagpreview != 0) {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = xstrdup(tag);
}
} else {
if (g_do_tagpreview != 0) {
@@ -227,25 +346,21 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// new pattern, add to the tag stack
if (*tag != NUL
&& (type == DT_TAG || type == DT_SELECT || type == DT_JUMP
- || type == DT_LTAG
- || type == DT_CSCOPE
- )) {
+ || type == DT_LTAG)) {
if (g_do_tagpreview != 0) {
if (ptag_entry.tagname != NULL
- && STRCMP(ptag_entry.tagname, tag) == 0) {
+ && strcmp(ptag_entry.tagname, tag) == 0) {
// Jumping to same tag: keep the current match, so that
// the CursorHold autocommand example works.
cur_match = ptag_entry.cur_match;
cur_fnum = ptag_entry.cur_fnum;
} else {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = xstrdup(tag);
}
} else {
- /*
- * If the last used entry is not at the top, delete all tag
- * stack entries above it.
- */
+ // If the last used entry is not at the top, delete all tag
+ // stack entries above it.
while (tagstackidx < tagstacklen) {
tagstack_clear_entry(&tagstack[--tagstacklen]);
}
@@ -261,7 +376,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
// put the tag name in the tag stack
- tagstack[tagstackidx].tagname = vim_strsave(tag);
+ tagstack[tagstackidx].tagname = xstrdup(tag);
curwin->w_tagstacklen = tagstacklen;
@@ -270,8 +385,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
new_tag = true;
} else {
- if (
- g_do_tagpreview != 0 ? ptag_entry.tagname == NULL :
+ if (g_do_tagpreview != 0 ? ptag_entry.tagname == NULL :
tagstacklen == 0) {
// empty stack
emsg(_(e_tagstack));
@@ -299,10 +413,8 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// tagstack before it's used.
saved_fmark = tagstack[tagstackidx].fmark;
if (saved_fmark.fnum != curbuf->b_fnum) {
- /*
- * Jump to other file. If this fails (e.g. because the
- * file was changed) keep original position in tag stack.
- */
+ // Jump to other file. If this fails (e.g. because the
+ // file was changed) keep original position in tag stack.
if (buflist_getfile(saved_fmark.fnum, saved_fmark.mark.lnum,
GETF_SETMARK, forceit) == FAIL) {
tagstackidx = oldtagstackidx; // back to old posn
@@ -324,7 +436,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// remove the old list of matches
FreeWild(num_matches, matches);
- cs_free_tags();
num_matches = 0;
tag_freematch();
goto end_do_tag;
@@ -339,11 +450,9 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// ":tag" (no argument): go to newer pattern
save_pos = true; // save the cursor position below
if ((tagstackidx += count - 1) >= tagstacklen) {
- /*
- * Beyond the last one, just give an error message and
- * go to the last one. Don't store the cursor
- * position.
- */
+ // Beyond the last one, just give an error message and
+ // go to the last one. Don't store the cursor
+ // position.
tagstackidx = tagstacklen - 1;
emsg(_(topmsg));
save_pos = false;
@@ -375,7 +484,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
cur_match = count - 1; break;
case DT_SELECT:
case DT_JUMP:
- case DT_CSCOPE:
case DT_LAST:
cur_match = MAXCOL - 1; break;
case DT_NEXT:
@@ -400,9 +508,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
ptag_entry.cur_fnum = cur_fnum;
}
} else {
- /*
- * For ":tag [arg]" or ":tselect" remember position before the jump.
- */
+ // For ":tag [arg]" or ":tselect" remember position before the jump.
saved_fmark = tagstack[tagstackidx].fmark;
if (save_pos) {
tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
@@ -427,32 +533,33 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
buf_T *buf = buflist_findnr(cur_fnum);
if (buf != NULL) {
- buf_ffname = (char_u *)buf->b_ffname;
+ buf_ffname = buf->b_ffname;
}
}
- /*
- * Repeat searching for tags, when a file has not been found.
- */
+ // Repeat searching for tags, when a file has not been found.
for (;;) {
int other_name;
- char_u *name;
+ char *name;
// When desired match not found yet, try to find it (and others).
if (use_tagstack) {
- name = tagstack[tagstackidx].tagname;
+ // make a copy, the tagstack may change in 'tagfunc'
+ name = xstrdup(tagstack[tagstackidx].tagname);
+ xfree(tofree);
+ tofree = name;
} else if (g_do_tagpreview != 0) {
name = ptag_entry.tagname;
} else {
name = tag;
}
- other_name = (tagmatchname == NULL || STRCMP(tagmatchname, name) != 0);
+ other_name = (tagmatchname == NULL || strcmp(tagmatchname, name) != 0);
if (new_tag
|| (cur_match >= num_matches && max_num_matches != MAXCOL)
|| other_name) {
if (other_name) {
xfree(tagmatchname);
- tagmatchname = vim_strsave(name);
+ tagmatchname = xstrdup(name);
}
if (type == DT_SELECT || type == DT_JUMP
@@ -473,9 +580,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
flags = TAG_NOIC;
}
- if (type == DT_CSCOPE) {
- flags = TAG_CSCOPE;
- }
if (verbose) {
flags |= TAG_VERBOSE;
}
@@ -490,6 +594,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// found: all matches found.
}
+ // A tag function may do anything, which may cause various
+ // information to become invalid. At least check for the tagstack
+ // to still be the same.
+ if (tagstack != curwin->w_tagstack) {
+ emsg(_(e_window_unexpectedly_close_while_searching_for_tags));
+ FreeWild(new_num_matches, new_matches);
+ break;
+ }
+
// If there already were some matches for the same name, move them
// to the start. Avoids that the order changes when using
// ":tnext" and jumping to another file.
@@ -501,15 +614,15 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// Find the position of each old match in the new list. Need
// to use parse_match() to find the tag line.
for (j = 0; j < num_matches; j++) {
- parse_match((char_u *)matches[j], &tagp);
+ parse_match(matches[j], &tagp);
for (i = idx; i < new_num_matches; i++) {
- parse_match((char_u *)new_matches[i], &tagp2);
- if (STRCMP(tagp.tagname, tagp2.tagname) == 0) {
- char_u *p = (char_u *)new_matches[i];
+ parse_match(new_matches[i], &tagp2);
+ if (strcmp(tagp.tagname, tagp2.tagname) == 0) {
+ char *p = new_matches[i];
for (k = i; k > idx; k--) {
new_matches[k] = new_matches[k - 1];
}
- new_matches[idx++] = (char *)p;
+ new_matches[idx++] = p;
break;
}
}
@@ -528,10 +641,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
} else {
bool ask_for_selection = false;
- if (type == DT_CSCOPE && num_matches > 1) {
- cs_print_tags();
- ask_for_selection = true;
- } else if (type == DT_TAG && *tag != NUL) {
+ if (type == DT_TAG && *tag != NUL) {
// If a count is supplied to the ":tag <name>" command, then
// jump to count'th matching tag.
cur_match = count > 0 ? count - 1 : 0;
@@ -555,8 +665,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
tagstack[tagstackidx].fmark = saved_fmark;
tagstackidx = prevtagstackidx;
}
- cs_free_tags();
- jumped_to_tag = true;
break;
}
cur_match = i - 1;
@@ -584,12 +692,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
tagstack[tagstackidx].cur_fnum = cur_fnum;
// store user-provided data originating from tagfunc
- if (use_tfu && parse_match((char_u *)matches[cur_match], &tagp2) == OK
+ if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK
&& tagp2.user_data) {
XFREE_CLEAR(tagstack[tagstackidx].user_data);
- tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data,
- (size_t)(tagp2.user_data_end -
- tagp2.user_data));
+ tagstack[tagstackidx].user_data =
+ xstrnsave(tagp2.user_data, (size_t)(tagp2.user_data_end - tagp2.user_data));
}
tagstackidx++;
@@ -598,21 +705,18 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
ptag_entry.cur_fnum = cur_fnum;
}
- /*
- * Only when going to try the next match, report that the previous
- * file didn't exist. Otherwise an emsg() is given below.
- */
+ // Only when going to try the next match, report that the previous
+ // file didn't exist. Otherwise an emsg() is given below.
if (nofile_fname != NULL && error_cur_match != cur_match) {
smsg(_("File \"%s\" does not exist"), nofile_fname);
}
ic = (matches[cur_match][0] & MT_IC_OFF);
if (type != DT_TAG && type != DT_SELECT && type != DT_JUMP
- && type != DT_CSCOPE
&& (num_matches > 1 || ic)
&& !skip_msg) {
// Give an indication of the number of matching tags
- snprintf((char *)IObuff, sizeof(IObuff), _("tag %d of %d%s"),
+ snprintf(IObuff, sizeof(IObuff), _("tag %d of %d%s"),
cur_match + 1,
num_matches,
max_num_matches != MAXCOL ? _(" or more") : "");
@@ -624,11 +728,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (ic) {
msg_attr((const char *)IObuff, HL_ATTR(HLF_W));
} else {
- msg((char *)IObuff);
+ msg(IObuff);
}
msg_scroll = true; // Don't overwrite this message.
} else {
- give_warning((char *)IObuff, ic);
+ give_warning(IObuff, ic);
}
if (ic && !msg_scrolled && msg_silent == 0) {
ui_flush();
@@ -637,13 +741,11 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
// Let the SwapExists event know what tag we are jumping to.
- vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name);
- set_vim_var_string(VV_SWAPCOMMAND, (char *)IObuff, -1);
+ vim_snprintf(IObuff, IOSIZE, ":ta %s\r", name);
+ set_vim_var_string(VV_SWAPCOMMAND, IObuff, -1);
- /*
- * Jump to the desired match.
- */
- i = jumpto_tag((char_u *)matches[cur_match], forceit, type != DT_CSCOPE);
+ // Jump to the desired match.
+ i = jumpto_tag(matches[cur_match], forceit, true);
set_vim_var_string(VV_SWAPCOMMAND, NULL, -1);
@@ -673,7 +775,6 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (use_tagstack && tagstackidx > curwin->w_tagstacklen) {
tagstackidx = curwin->w_tagstackidx;
}
- jumped_to_tag = true;
}
}
break;
@@ -686,8 +787,7 @@ end_do_tag:
}
postponed_split = 0; // don't split next time
g_do_tagpreview = 0; // don't do tag preview next time
-
- return jumped_to_tag;
+ xfree(tofree);
}
// List all the matching tags.
@@ -696,15 +796,15 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
int i;
- char_u *p;
- char_u *command_end;
+ char *p;
+ char *command_end;
tagptrs_T tagp;
int taglen;
int attr;
// Assume that the first match indicates how long the tags can
// be, and align the file names to that.
- parse_match((char_u *)matches[0], &tagp);
+ parse_match(matches[0], &tagp);
taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
if (taglen < 18) {
taglen = 18;
@@ -722,7 +822,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
for (i = 0; i < num_matches && !got_int; i++) {
- parse_match((char_u *)matches[i], &tagp);
+ parse_match(matches[i], &tagp);
if (!new_tag && (
(g_do_tagpreview != 0
&& i == ptag_entry.cur_match)
@@ -732,10 +832,10 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
} else {
*IObuff = ' ';
}
- vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
+ vim_snprintf(IObuff + 1, IOSIZE - 1,
"%2d %s ", i + 1,
mt_names[matches[i][0] & MT_MASK]);
- msg_puts((char *)IObuff);
+ msg_puts(IObuff);
if (tagp.tagkind != NULL) {
msg_outtrans_len(tagp.tagkind,
(int)(tagp.tagkind_end - tagp.tagkind));
@@ -772,21 +872,21 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
}
// skip "file:" without a value (static tag)
- if (STRNCMP(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
+ if (strncmp(p, "file:", 5) == 0 && ascii_isspace(p[5])) {
p += 5;
continue;
}
// skip "kind:<kind>" and "<kind>"
if (p == tagp.tagkind
|| (p + 5 == tagp.tagkind
- && STRNCMP(p, "kind:", 5) == 0)) {
+ && strncmp(p, "kind:", 5) == 0)) {
p = tagp.tagkind_end;
continue;
}
// print all other extra fields
attr = HL_ATTR(HLF_CM);
while (*p && *p != '\r' && *p != '\n') {
- if (msg_col + ptr2cells((char *)p) >= Columns) {
+ if (msg_col + ptr2cells(p) >= Columns) {
msg_putchar('\n');
if (got_int) {
break;
@@ -832,7 +932,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
}
while (p != command_end) {
- if (msg_col + (*p == TAB ? 1 : ptr2cells((char *)p)) > Columns) {
+ if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) {
msg_putchar('\n');
}
if (got_int) {
@@ -877,14 +977,14 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
/// Add the matching tags to the location list for the current
/// window.
-static int add_llist_tags(char_u *tag, int num_matches, char **matches)
+static int add_llist_tags(char *tag, int num_matches, char **matches)
{
list_T *list;
- char_u tag_name[128 + 1];
- char_u *fname;
- char_u *cmd;
+ char tag_name[128 + 1];
+ char *fname;
+ char *cmd;
int i;
- char_u *p;
+ char *p;
tagptrs_T tagp;
fname = xmalloc(MAXPATHL + 1);
@@ -896,14 +996,14 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
long lnum;
dict_T *dict;
- parse_match((char_u *)matches[i], &tagp);
+ parse_match(matches[i], &tagp);
// Save the tag name
len = (int)(tagp.tagname_end - tagp.tagname);
if (len > 128) {
len = 128;
}
- STRLCPY(tag_name, tagp.tagname, len + 1);
+ xstrlcpy(tag_name, tagp.tagname, (size_t)len + 1);
tag_name[len] = NUL;
// Save the tag file name
@@ -911,17 +1011,17 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
if (p == NULL) {
continue;
}
- STRLCPY(fname, p, MAXPATHL);
+ xstrlcpy(fname, p, MAXPATHL);
XFREE_CLEAR(p);
// Get the line number or the search pattern used to locate
// the tag.
lnum = 0;
- if (isdigit(*tagp.command)) {
+ if (isdigit((uint8_t)(*tagp.command))) {
// Line number is used to locate the tag
- lnum = atol((char *)tagp.command);
+ lnum = atol(tagp.command);
} else {
- char_u *cmd_start, *cmd_end;
+ char *cmd_start, *cmd_end;
// Search pattern is used to locate the tag
@@ -969,7 +1069,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
if (cmd_len > (CMDBUFFSIZE - 5)) {
cmd_len = CMDBUFFSIZE - 5;
}
- snprintf((char *)cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
+ snprintf(cmd + len, (size_t)(CMDBUFFSIZE + 1 - len),
"%.*s", cmd_len, cmd_start);
len += cmd_len;
@@ -987,16 +1087,16 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
dict = tv_dict_alloc();
tv_list_append_dict(list, dict);
- tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name);
+ tv_dict_add_str(dict, S_LEN("text"), tag_name);
tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname);
tv_dict_add_nr(dict, S_LEN("lnum"), lnum);
if (lnum == 0) {
- tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd);
+ tv_dict_add_str(dict, S_LEN("pattern"), cmd);
}
}
- vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
- set_errorlist(curwin, list, ' ', (char *)IObuff, NULL);
+ vim_snprintf(IObuff, IOSIZE, "ltag %s", tag);
+ set_errorlist(curwin, list, ' ', IObuff, NULL);
tv_list_free(list);
XFREE_CLEAR(fname);
@@ -1005,9 +1105,7 @@ static int add_llist_tags(char_u *tag, int num_matches, char **matches)
return OK;
}
-/*
- * Free cached tags.
- */
+// Free cached tags.
void tag_freematch(void)
{
XFREE_CLEAR(tagmatchname);
@@ -1023,20 +1121,18 @@ static void taglen_advance(int l)
}
}
-/*
- * Print the tag stack
- */
+// Print the tag stack
void do_tags(exarg_T *eap)
{
int i;
- char_u *name;
+ char *name;
taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx;
int tagstacklen = curwin->w_tagstacklen;
// Highlight title
msg_puts_title(_("\n # TO tag FROM line in file/text"));
- for (i = 0; i < tagstacklen; ++i) {
+ for (i = 0; i < tagstacklen; i++) {
if (tagstack[i].tagname != NULL) {
name = fm_getname(&(tagstack[i].fmark), 30);
if (name == NULL) { // file name not available
@@ -1044,35 +1140,32 @@ void do_tags(exarg_T *eap)
}
msg_putchar('\n');
- vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR " ",
+ vim_snprintf(IObuff, IOSIZE, "%c%2d %2d %-15s %5" PRIdLINENR " ",
i == tagstackidx ? '>' : ' ',
i + 1,
tagstack[i].cur_match + 1,
tagstack[i].tagname,
tagstack[i].fmark.mark.lnum);
- msg_outtrans((char *)IObuff);
+ msg_outtrans(IObuff);
msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
}
- ui_flush(); // show one line at a time
}
if (tagstackidx == tagstacklen) { // idx at top of stack
msg_puts("\n>");
}
}
-/*
- * Compare two strings, for length "len", ignoring case the ASCII way.
- * return 0 for match, < 0 for smaller, > 0 for bigger
- * Make sure case is folded to uppercase in comparison (like for 'sort -f')
- */
-static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
+// Compare two strings, for length "len", ignoring case the ASCII way.
+// return 0 for match, < 0 for smaller, > 0 for bigger
+// Make sure case is folded to uppercase in comparison (like for 'sort -f')
+static int tag_strnicmp(char *s1, char *s2, size_t len)
{
int i;
while (len > 0) {
- i = TOUPPER_ASC(*s1) - TOUPPER_ASC(*s2);
+ i = TOUPPER_ASC((uint8_t)(*s1)) - TOUPPER_ASC((uint8_t)(*s2));
if (i != 0) {
return i; // this character different
}
@@ -1086,9 +1179,7 @@ static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
return 0; // strings match
}
-/*
- * Extract info from the tag search pattern "pats->pat".
- */
+// Extract info from the tag search pattern "pats->pat".
static void prepare_pats(pat_T *pats, int has_re)
{
pats->head = pats->pat;
@@ -1105,7 +1196,8 @@ static void prepare_pats(pat_T *pats, int has_re)
pats->headlen = 0;
} else {
for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) {
- if (vim_strchr((p_magic ? ".[~*\\$" : "\\$"), pats->head[pats->headlen]) != NULL) {
+ if (vim_strchr(magic_isset() ? ".[~*\\$" : "\\$",
+ (uint8_t)pats->head[pats->headlen]) != NULL) {
break;
}
}
@@ -1116,7 +1208,7 @@ static void prepare_pats(pat_T *pats, int has_re)
}
if (has_re) {
- pats->regmatch.regprog = vim_regcomp((char *)pats->pat, p_magic ? RE_MAGIC : 0);
+ pats->regmatch.regprog = vim_regcomp(pats->pat, magic_isset() ? RE_MAGIC : 0);
} else {
pats->regmatch.regprog = NULL;
}
@@ -1133,8 +1225,7 @@ static void prepare_pats(pat_T *pats, int has_re)
/// @param match_count here the number of tags found will be placed
/// @param flags flags from find_tags (TAG_*)
/// @param buf_ffname name of buffer for priority
-static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int flags,
- char_u *buf_ffname)
+static int find_tagfunc_tags(char *pat, garray_T *ga, int *match_count, int flags, char *buf_ffname)
{
pos_T save_pos;
list_T *taglist;
@@ -1142,7 +1233,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
int result = FAIL;
typval_T args[4];
typval_T rettv;
- char_u flagString[4];
+ char flagString[4];
taggy_T *tag = NULL;
if (curwin->w_tagstacklen > 0) {
@@ -1153,14 +1244,14 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
}
}
- if (*curbuf->b_p_tfu == NUL) {
+ if (*curbuf->b_p_tfu == NUL || curbuf->b_tfu_cb.type == kCallbackNone) {
return FAIL;
}
args[0].v_type = VAR_STRING;
- args[0].vval.v_string = (char *)pat;
+ args[0].vval.v_string = pat;
args[1].v_type = VAR_STRING;
- args[1].vval.v_string = (char *)flagString;
+ args[1].vval.v_string = flagString;
// create 'info' dict argument
dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
@@ -1177,14 +1268,14 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
args[3].v_type = VAR_UNKNOWN;
- vim_snprintf((char *)flagString, sizeof(flagString),
+ vim_snprintf(flagString, sizeof(flagString),
"%s%s%s",
g_tag_at_cursor ? "c": "",
flags & TAG_INS_COMP ? "i": "",
flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
- result = call_vim_function((char *)curbuf->b_p_tfu, 3, args, &rettv);
+ result = callback_call(&curbuf->b_tfu_cb, 3, args, &rettv);
curwin->w_cursor = save_pos; // restore the cursor position
d->dv_refcount--;
@@ -1203,10 +1294,10 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
taglist = rettv.vval.v_list;
TV_LIST_ITER_CONST(taglist, li, {
- char_u *res_name;
- char_u *res_fname;
- char_u *res_cmd;
- char_u *res_kind;
+ char *res_name;
+ char *res_fname;
+ char *res_cmd;
+ char *res_kind;
int has_extra = 0;
int name_only = flags & TAG_NAMES;
@@ -1222,34 +1313,34 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
res_kind = NULL;
TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
- const char_u *dict_key = di->di_key;
+ const char *dict_key = (char *)di->di_key;
typval_T *tv = &di->di_tv;
if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
continue;
}
- len += STRLEN(tv->vval.v_string) + 1; // Space for "\tVALUE"
- if (!STRCMP(dict_key, "name")) {
- res_name = (char_u *)tv->vval.v_string;
+ len += strlen(tv->vval.v_string) + 1; // Space for "\tVALUE"
+ if (!strcmp(dict_key, "name")) {
+ res_name = tv->vval.v_string;
continue;
}
- if (!STRCMP(dict_key, "filename")) {
- res_fname = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "filename")) {
+ res_fname = tv->vval.v_string;
continue;
}
- if (!STRCMP(dict_key, "cmd")) {
- res_cmd = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "cmd")) {
+ res_cmd = tv->vval.v_string;
continue;
}
has_extra = 1;
- if (!STRCMP(dict_key, "kind")) {
- res_kind = (char_u *)tv->vval.v_string;
+ if (!strcmp(dict_key, "kind")) {
+ res_kind = tv->vval.v_string;
continue;
}
// Other elements will be stored as "\tKEY:VALUE"
// Allocate space for the key and the colon
- len += STRLEN(dict_key) + 1;
+ len += strlen(dict_key) + 1;
});
if (has_extra) {
@@ -1261,62 +1352,62 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
break;
}
- char_u *const mfp = name_only ? vim_strsave(res_name) : xmalloc(len + 2);
+ char *const mfp = name_only ? xstrdup(res_name) : xmalloc(len + 2);
if (!name_only) {
- char_u *p = mfp;
+ char *p = mfp;
*p++ = MT_GL_OTH + 1; // mtt
*p++ = TAG_SEP; // no tag file name
STRCPY(p, res_name);
- p += STRLEN(p);
+ p += strlen(p);
*p++ = TAB;
STRCPY(p, res_fname);
- p += STRLEN(p);
+ p += strlen(p);
*p++ = TAB;
STRCPY(p, res_cmd);
- p += STRLEN(p);
+ p += strlen(p);
if (has_extra) {
STRCPY(p, ";\"");
- p += STRLEN(p);
+ p += strlen(p);
if (res_kind) {
*p++ = TAB;
STRCPY(p, res_kind);
- p += STRLEN(p);
+ p += strlen(p);
}
TV_DICT_ITER(TV_LIST_ITEM_TV(li)->vval.v_dict, di, {
- const char_u *dict_key = di->di_key;
+ const char *dict_key = (char *)di->di_key;
typval_T *tv = &di->di_tv;
if (tv->v_type != VAR_STRING || tv->vval.v_string == NULL) {
continue;
}
- if (!STRCMP(dict_key, "name")) {
+ if (!strcmp(dict_key, "name")) {
continue;
}
- if (!STRCMP(dict_key, "filename")) {
+ if (!strcmp(dict_key, "filename")) {
continue;
}
- if (!STRCMP(dict_key, "cmd")) {
+ if (!strcmp(dict_key, "cmd")) {
continue;
}
- if (!STRCMP(dict_key, "kind")) {
+ if (!strcmp(dict_key, "kind")) {
continue;
}
*p++ = TAB;
STRCPY(p, dict_key);
- p += STRLEN(p);
+ p += strlen(p);
STRCPY(p, ":");
- p += STRLEN(p);
+ p += strlen(p);
STRCPY(p, tv->vval.v_string);
- p += STRLEN(p);
+ p += strlen(p);
});
}
}
@@ -1334,6 +1425,873 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
return result;
}
+/// Initialize the state used by find_tags()
+static void findtags_state_init(findtags_state_T *st, char *pat, int flags, int mincount)
+{
+ st->tag_fname = xmalloc(MAXPATHL + 1);
+ st->fp = NULL;
+ st->orgpat = xmalloc(sizeof(pat_T));
+ st->orgpat->pat = pat;
+ st->orgpat->len = (int)strlen(pat);
+ st->orgpat->regmatch.regprog = NULL;
+ st->flags = flags;
+ st->tag_file_sorted = NUL;
+ st->help_lang_find = NULL;
+ st->is_txt = false;
+ st->did_open = false;
+ st->help_only = (flags & TAG_HELP);
+ st->get_searchpat = false;
+ st->help_lang[0] = NUL;
+ st->help_pri = 0;
+ st->mincount = mincount;
+ st->lbuf_size = LSIZE;
+ st->lbuf = xmalloc((size_t)st->lbuf_size);
+ st->match_count = 0;
+ st->stop_searching = false;
+
+ for (int mtt = 0; mtt < MT_COUNT; mtt++) {
+ ga_init(&st->ga_match[mtt], sizeof(char *), 100);
+ hash_init(&st->ht_match[mtt]);
+ }
+}
+
+/// Free the state used by find_tags()
+static void findtags_state_free(findtags_state_T *st)
+{
+ xfree(st->tag_fname);
+ xfree(st->lbuf);
+ vim_regfree(st->orgpat->regmatch.regprog);
+ xfree(st->orgpat);
+}
+
+/// Initialize the language and priority used for searching tags in a Vim help
+/// file.
+/// Returns true to process the help file for tags and false to skip the file.
+static bool findtags_in_help_init(findtags_state_T *st)
+{
+ int i;
+
+ // Keep "en" as the language if the file extension is ".txt"
+ if (st->is_txt) {
+ STRCPY(st->help_lang, "en");
+ } else {
+ // Prefer help tags according to 'helplang'. Put the two-letter
+ // language name in help_lang[].
+ i = (int)strlen(st->tag_fname);
+ if (i > 3 && st->tag_fname[i - 3] == '-') {
+ xstrlcpy(st->help_lang, st->tag_fname + i - 2, 3);
+ } else {
+ STRCPY(st->help_lang, "en");
+ }
+ }
+ // When searching for a specific language skip tags files for other
+ // languages.
+ if (st->help_lang_find != NULL
+ && STRICMP(st->help_lang, st->help_lang_find) != 0) {
+ return false;
+ }
+
+ // For CTRL-] in a help file prefer a match with the same language.
+ if ((st->flags & TAG_KEEP_LANG)
+ && st->help_lang_find == NULL
+ && curbuf->b_fname != NULL
+ && (i = (int)strlen(curbuf->b_fname)) > 4
+ && curbuf->b_fname[i - 1] == 'x'
+ && curbuf->b_fname[i - 4] == '.'
+ && STRNICMP(curbuf->b_fname + i - 3, st->help_lang, 2) == 0) {
+ st->help_pri = 0;
+ } else {
+ st->help_pri = 1;
+ char *s;
+ for (s = p_hlg; *s != NUL; s++) {
+ if (STRNICMP(s, st->help_lang, 2) == 0) {
+ break;
+ }
+ st->help_pri++;
+ if ((s = vim_strchr(s, ',')) == NULL) {
+ break;
+ }
+ }
+ if (s == NULL || *s == NUL) {
+ // Language not in 'helplang': use last, prefer English, unless
+ // found already.
+ st->help_pri++;
+ if (STRICMP(st->help_lang, "en") != 0) {
+ st->help_pri++;
+ }
+ }
+ }
+
+ return true;
+}
+
+/// Use the function set in 'tagfunc' (if configured and enabled) to get the
+/// tags.
+/// Return OK if at least 1 tag has been successfully found, NOTDONE if the
+/// 'tagfunc' is not used or the 'tagfunc' returns v:null and FAIL otherwise.
+static int findtags_apply_tfu(findtags_state_T *st, char *pat, char *buf_ffname)
+{
+ const bool use_tfu = ((st->flags & TAG_NO_TAGFUNC) == 0);
+
+ if (!use_tfu || tfu_in_use || *curbuf->b_p_tfu == NUL) {
+ return NOTDONE;
+ }
+
+ tfu_in_use = true;
+ int retval = find_tagfunc_tags(pat, st->ga_match, &st->match_count,
+ st->flags, buf_ffname);
+ tfu_in_use = false;
+
+ return retval;
+}
+
+/// Read the next line from a tags file.
+/// Returns TAGS_READ_SUCCESS if a tags line is successfully read and should be
+/// processed.
+/// Returns TAGS_READ_EOF if the end of file is reached.
+/// Returns TAGS_READ_IGNORE if the current line should be ignored (used when
+/// reached end of a emacs included tags file)
+static tags_read_status_T findtags_get_next_line(findtags_state_T *st, tagsearch_info_T *sinfo_p)
+{
+ int eof;
+ off_T offset;
+
+ // For binary search: compute the next offset to use.
+ if (st->state == TS_BINARY) {
+ offset = sinfo_p->low_offset + ((sinfo_p->high_offset - sinfo_p->low_offset) / 2);
+ if (offset == sinfo_p->curr_offset) {
+ return TAGS_READ_EOF; // End the binary search without a match.
+ } else {
+ sinfo_p->curr_offset = offset;
+ }
+ } else if (st->state == TS_SKIP_BACK) {
+ // Skipping back (after a match during binary search).
+ sinfo_p->curr_offset -= st->lbuf_size * 2;
+ if (sinfo_p->curr_offset < 0) {
+ sinfo_p->curr_offset = 0;
+ rewind(st->fp);
+ st->state = TS_STEP_FORWARD;
+ }
+ }
+
+ // When jumping around in the file, first read a line to find the
+ // start of the next line.
+ if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
+ // Adjust the search file offset to the correct position
+ sinfo_p->curr_offset_used = sinfo_p->curr_offset;
+ vim_ignored = vim_fseek(st->fp, sinfo_p->curr_offset, SEEK_SET);
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ if (!eof && sinfo_p->curr_offset != 0) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ if (sinfo_p->curr_offset == sinfo_p->high_offset) {
+ // oops, gone a bit too far; try from low offset
+ vim_ignored = vim_fseek(st->fp, sinfo_p->low_offset, SEEK_SET);
+ sinfo_p->curr_offset = sinfo_p->low_offset;
+ }
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ }
+ // skip empty and blank lines
+ while (!eof && vim_isblankline(st->lbuf)) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ }
+ if (eof) {
+ // Hit end of file. Skip backwards.
+ st->state = TS_SKIP_BACK;
+ sinfo_p->match_offset = vim_ftell(st->fp);
+ sinfo_p->curr_offset = sinfo_p->curr_offset_used;
+ return TAGS_READ_IGNORE;
+ }
+ } else {
+ // Not jumping around in the file: Read the next line.
+
+ // skip empty and blank lines
+ do {
+ eof = vim_fgets(st->lbuf, st->lbuf_size, st->fp);
+ } while (!eof && vim_isblankline(st->lbuf));
+
+ if (eof) {
+ return TAGS_READ_EOF;
+ }
+ }
+
+ return TAGS_READ_SUCCESS;
+}
+
+/// Parse a tags file header line in "st->lbuf".
+/// Returns true if the current line in st->lbuf is not a tags header line and
+/// should be parsed as a regular tag line. Returns false if the line is a
+/// header line and the next header line should be read.
+static bool findtags_hdr_parse(findtags_state_T *st)
+{
+ // Header lines in a tags file start with "!_TAG_"
+ if (strncmp(st->lbuf, "!_TAG_", 6) != 0) {
+ // Non-header item before the header, e.g. "!" itself.
+ return true;
+ }
+
+ // Process the header line.
+ if (strncmp(st->lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
+ st->tag_file_sorted = (uint8_t)st->lbuf[18];
+ }
+ if (strncmp(st->lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
+ // Prepare to convert every line from the specified encoding to
+ // 'encoding'.
+ char *p;
+ for (p = st->lbuf + 20; *p > ' ' && *p < 127; p++) {}
+ *p = NUL;
+ convert_setup(&st->vimconv, st->lbuf + 20, p_enc);
+ }
+
+ // Read the next line. Unrecognized flags are ignored.
+ return false;
+}
+
+/// Handler to initialize the state when starting to process a new tags file.
+/// Called in the TS_START state when finding tags from a tags file.
+/// Returns true if the line read from the tags file should be parsed and
+/// false if the line should be ignored.
+static bool findtags_start_state_handler(findtags_state_T *st, bool *sortic,
+ tagsearch_info_T *sinfo_p)
+{
+ const bool noic = (st->flags & TAG_NOIC);
+
+ // The header ends when the line sorts below "!_TAG_". When case is
+ // folded lower case letters sort before "_".
+ if (strncmp(st->lbuf, "!_TAG_", 6) <= 0
+ || (st->lbuf[0] == '!' && ASCII_ISLOWER(st->lbuf[1]))) {
+ return findtags_hdr_parse(st);
+ }
+
+ // Headers ends.
+
+ // When there is no tag head, or ignoring case, need to do a
+ // linear search.
+ // When no "!_TAG_" is found, default to binary search. If
+ // the tag file isn't sorted, the second loop will find it.
+ // When "!_TAG_FILE_SORTED" found: start binary search if
+ // flag set.
+ if (st->linear) {
+ st->state = TS_LINEAR;
+ } else if (st->tag_file_sorted == NUL) {
+ st->state = TS_BINARY;
+ } else if (st->tag_file_sorted == '1') {
+ st->state = TS_BINARY;
+ } else if (st->tag_file_sorted == '2') {
+ st->state = TS_BINARY;
+ *sortic = true;
+ st->orgpat->regmatch.rm_ic = (p_ic || !noic);
+ } else {
+ st->state = TS_LINEAR;
+ }
+
+ if (st->state == TS_BINARY && st->orgpat->regmatch.rm_ic && !*sortic) {
+ // Binary search won't work for ignoring case, use linear
+ // search.
+ st->linear = true;
+ st->state = TS_LINEAR;
+ }
+
+ // When starting a binary search, get the size of the file and
+ // compute the first offset.
+ if (st->state == TS_BINARY) {
+ if (vim_fseek(st->fp, 0, SEEK_END) != 0) {
+ // can't seek, don't use binary search
+ st->state = TS_LINEAR;
+ } else {
+ // Get the tag file size.
+ // Don't use lseek(), it doesn't work
+ // properly on MacOS Catalina.
+ const off_T filesize = vim_ftell(st->fp);
+ vim_ignored = vim_fseek(st->fp, 0, SEEK_SET);
+
+ // Calculate the first read offset in the file. Start
+ // the search in the middle of the file.
+ sinfo_p->low_offset = 0;
+ sinfo_p->low_char = 0;
+ sinfo_p->high_offset = filesize;
+ sinfo_p->curr_offset = 0;
+ sinfo_p->high_char = 0xff;
+ }
+ return false;
+ }
+
+ return true;
+}
+
+/// Parse a tag line read from a tags file.
+/// Also compares the tag name in "tagpp->tagname" with a search pattern in
+/// "st->orgpat->head" as a quick check if the tag may match.
+/// Returns:
+/// - TAG_MATCH_SUCCESS if the tag may match
+/// - TAG_MATCH_FAIL if the tag doesn't match
+/// - TAG_MATCH_NEXT to look for the next matching tag (used in a binary search)
+/// - TAG_MATCH_STOP if all the tags are processed without a match.
+/// Uses the values in "margs" for doing the comparison.
+static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *tagpp,
+ findtags_match_args_T *margs,
+ tagsearch_info_T *sinfo_p)
+{
+ int status;
+ int i;
+ int cmplen;
+ int tagcmp;
+
+ // Figure out where the different strings are in this line.
+ // For "normal" tags: Do a quick check if the tag matches.
+ // This speeds up tag searching a lot!
+ if (st->orgpat->headlen) {
+ CLEAR_FIELD(*tagpp);
+ tagpp->tagname = st->lbuf;
+ tagpp->tagname_end = vim_strchr(st->lbuf, TAB);
+ if (tagpp->tagname_end == NULL) {
+ // Corrupted tag line.
+ return TAG_MATCH_FAIL;
+ }
+
+ // Skip this line if the length of the tag is different and
+ // there is no regexp, or the tag is too short.
+ cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
+ if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
+ cmplen = (int)p_tl;
+ }
+ if ((st->flags & TAG_REGEXP) && st->orgpat->headlen < cmplen) {
+ cmplen = st->orgpat->headlen;
+ } else if (st->state == TS_LINEAR && st->orgpat->headlen != cmplen) {
+ return TAG_MATCH_NEXT;
+ }
+
+ if (st->state == TS_BINARY) {
+ // Simplistic check for unsorted tags file.
+ i = (int)tagpp->tagname[0];
+ if (margs->sortic) {
+ i = TOUPPER_ASC(tagpp->tagname[0]);
+ }
+ if (i < sinfo_p->low_char || i > sinfo_p->high_char) {
+ margs->sort_error = true;
+ }
+
+ // Compare the current tag with the searched tag.
+ if (margs->sortic) {
+ tagcmp = tag_strnicmp(tagpp->tagname, st->orgpat->head,
+ (size_t)cmplen);
+ } else {
+ tagcmp = strncmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen);
+ }
+
+ // A match with a shorter tag means to search forward.
+ // A match with a longer tag means to search backward.
+ if (tagcmp == 0) {
+ if (cmplen < st->orgpat->headlen) {
+ tagcmp = -1;
+ } else if (cmplen > st->orgpat->headlen) {
+ tagcmp = 1;
+ }
+ }
+
+ if (tagcmp == 0) {
+ // We've located the tag, now skip back and search
+ // forward until the first matching tag is found.
+ st->state = TS_SKIP_BACK;
+ sinfo_p->match_offset = sinfo_p->curr_offset;
+ return TAG_MATCH_NEXT;
+ }
+ if (tagcmp < 0) {
+ sinfo_p->curr_offset = vim_ftell(st->fp);
+ if (sinfo_p->curr_offset < sinfo_p->high_offset) {
+ sinfo_p->low_offset = sinfo_p->curr_offset;
+ if (margs->sortic) {
+ sinfo_p->low_char = TOUPPER_ASC(tagpp->tagname[0]);
+ } else {
+ sinfo_p->low_char = (uint8_t)tagpp->tagname[0];
+ }
+ return TAG_MATCH_NEXT;
+ }
+ }
+ if (tagcmp > 0 && sinfo_p->curr_offset != sinfo_p->high_offset) {
+ sinfo_p->high_offset = sinfo_p->curr_offset;
+ if (margs->sortic) {
+ sinfo_p->high_char = TOUPPER_ASC(tagpp->tagname[0]);
+ } else {
+ sinfo_p->high_char = (uint8_t)tagpp->tagname[0];
+ }
+ return TAG_MATCH_NEXT;
+ }
+
+ // No match yet and are at the end of the binary search.
+ return TAG_MATCH_STOP;
+ } else if (st->state == TS_SKIP_BACK) {
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ st->state = TS_STEP_FORWARD;
+ } else {
+ // Have to skip back more. Restore the curr_offset
+ // used, otherwise we get stuck at a long line.
+ sinfo_p->curr_offset = sinfo_p->curr_offset_used;
+ }
+ return TAG_MATCH_NEXT;
+ } else if (st->state == TS_STEP_FORWARD) {
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ if ((off_T)vim_ftell(st->fp) > sinfo_p->match_offset) {
+ return TAG_MATCH_STOP; // past last match
+ } else {
+ return TAG_MATCH_NEXT; // before first match
+ }
+ }
+ } else {
+ // skip this match if it can't match
+ assert(cmplen >= 0);
+ if (mb_strnicmp(tagpp->tagname, st->orgpat->head, (size_t)cmplen) != 0) {
+ return TAG_MATCH_NEXT;
+ }
+ }
+
+ // Can be a matching tag, isolate the file name and command.
+ tagpp->fname = tagpp->tagname_end + 1;
+ tagpp->fname_end = vim_strchr(tagpp->fname, TAB);
+ if (tagpp->fname_end == NULL) {
+ status = FAIL;
+ } else {
+ tagpp->command = tagpp->fname_end + 1;
+ status = OK;
+ }
+ } else {
+ status = parse_tag_line(st->lbuf, tagpp);
+ }
+
+ if (status == FAIL) {
+ return TAG_MATCH_FAIL;
+ }
+
+ return TAG_MATCH_SUCCESS;
+}
+
+/// Initialize the structure used for tag matching.
+static void findtags_matchargs_init(findtags_match_args_T *margs, int flags)
+{
+ margs->matchoff = 0; // match offset
+ margs->match_re = false; // match with regexp
+ margs->match_no_ic = false; // matches with case
+ margs->has_re = (flags & TAG_REGEXP); // regexp used
+ margs->sortic = false; // tag file sorted in nocase
+ margs->sort_error = false; // tags file not sorted
+}
+
+/// Compares the tag name in "tagpp->tagname" with a search pattern in
+/// "st->orgpat->pat".
+/// Returns true if the tag matches, false if the tag doesn't match.
+/// Uses the values in "margs" for doing the comparison.
+static bool findtags_match_tag(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs)
+{
+ bool match = false;
+
+ // First try matching with the pattern literally (also when it is
+ // a regexp).
+ int cmplen = (int)(tagpp->tagname_end - tagpp->tagname);
+ if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
+ cmplen = (int)p_tl;
+ }
+ // if tag length does not match, don't try comparing
+ if (st->orgpat->len != cmplen) {
+ match = false;
+ } else {
+ if (st->orgpat->regmatch.rm_ic) {
+ assert(cmplen >= 0);
+ match = mb_strnicmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ if (match) {
+ margs->match_no_ic = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ }
+ } else {
+ match = strncmp(tagpp->tagname, st->orgpat->pat, (size_t)cmplen) == 0;
+ }
+ }
+
+ // Has a regexp: Also find tags matching regexp.
+ margs->match_re = false;
+ if (!match && st->orgpat->regmatch.regprog != NULL) {
+ char cc = *tagpp->tagname_end;
+ *tagpp->tagname_end = NUL;
+ match = vim_regexec(&st->orgpat->regmatch, tagpp->tagname, (colnr_T)0);
+ if (match) {
+ margs->matchoff = (int)(st->orgpat->regmatch.startp[0] - tagpp->tagname);
+ if (st->orgpat->regmatch.rm_ic) {
+ st->orgpat->regmatch.rm_ic = false;
+ margs->match_no_ic = vim_regexec(&st->orgpat->regmatch,
+ tagpp->tagname, (colnr_T)0);
+ st->orgpat->regmatch.rm_ic = true;
+ }
+ }
+ *tagpp->tagname_end = cc;
+ margs->match_re = true;
+ }
+
+ return match;
+}
+
+/// Convert the encoding of a line read from a tags file in "st->lbuf".
+/// Converting the pattern from 'enc' to the tags file encoding doesn't work,
+/// because characters are not recognized. The converted line is saved in
+/// st->lbuf.
+static void findtags_string_convert(findtags_state_T *st)
+{
+ char *conv_line = string_convert(&st->vimconv, st->lbuf, NULL);
+ if (conv_line == NULL) {
+ return;
+ }
+
+ // Copy or swap lbuf and conv_line.
+ int len = (int)strlen(conv_line) + 1;
+ if (len > st->lbuf_size) {
+ xfree(st->lbuf);
+ st->lbuf = conv_line;
+ st->lbuf_size = len;
+ } else {
+ STRCPY(st->lbuf, conv_line);
+ xfree(conv_line);
+ }
+}
+
+/// Add a matching tag found in a tags file to st->ht_match and st->ga_match.
+static void findtags_add_match(findtags_state_T *st, tagptrs_T *tagpp, findtags_match_args_T *margs,
+ char *buf_ffname, hash_T *hash)
+{
+ const bool name_only = (st->flags & TAG_NAMES);
+ int mtt;
+ size_t len = 0;
+ bool is_current; // file name matches
+ bool is_static; // current tag line is static
+ char *mfp;
+ char *p;
+ char *s;
+
+ // Decide in which array to store this match.
+ is_current = test_for_current(tagpp->fname, tagpp->fname_end,
+ st->tag_fname, buf_ffname);
+ is_static = test_for_static(tagpp);
+
+ // Decide in which of the sixteen tables to store this match.
+ if (is_static) {
+ if (is_current) {
+ mtt = MT_ST_CUR;
+ } else {
+ mtt = MT_ST_OTH;
+ }
+ } else {
+ if (is_current) {
+ mtt = MT_GL_CUR;
+ } else {
+ mtt = MT_GL_OTH;
+ }
+ }
+ if (st->orgpat->regmatch.rm_ic && !margs->match_no_ic) {
+ mtt += MT_IC_OFF;
+ }
+ if (margs->match_re) {
+ mtt += MT_RE_OFF;
+ }
+
+ // Add the found match in ht_match[mtt] and ga_match[mtt].
+ // Store the info we need later, which depends on the kind of
+ // tags we are dealing with.
+ if (st->help_only) {
+#define ML_EXTRA 3
+ // Append the help-heuristic number after the tagname, for
+ // sorting it later. The heuristic is ignored for
+ // detecting duplicates.
+ // The format is {tagname}@{lang}NUL{heuristic}NUL
+ *tagpp->tagname_end = NUL;
+ len = (size_t)(tagpp->tagname_end - tagpp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
+
+ p = mfp;
+ STRCPY(p, tagpp->tagname);
+ p[len] = '@';
+ STRCPY(p + len + 1, st->help_lang);
+ snprintf(p + len + 1 + ML_EXTRA, strlen(p) + len + 1 + ML_EXTRA, "%06d",
+ help_heuristic(tagpp->tagname,
+ margs->match_re ? margs->matchoff : 0,
+ !margs->match_no_ic) + st->help_pri);
+
+ *tagpp->tagname_end = TAB;
+ } else if (name_only) {
+ if (st->get_searchpat) {
+ char *temp_end = tagpp->command;
+
+ if (*temp_end == '/') {
+ while (*temp_end && *temp_end != '\r'
+ && *temp_end != '\n'
+ && *temp_end != '$') {
+ temp_end++;
+ }
+ }
+
+ if (tagpp->command + 2 < temp_end) {
+ len = (size_t)(temp_end - tagpp->command - 2);
+ mfp = xmalloc(len + 2);
+ xstrlcpy(mfp, tagpp->command + 2, len + 1);
+ } else {
+ mfp = NULL;
+ }
+ st->get_searchpat = false;
+ } else {
+ len = (size_t)(tagpp->tagname_end - tagpp->tagname);
+ mfp = xmalloc(sizeof(char) + len + 1);
+ xstrlcpy(mfp, tagpp->tagname, len + 1);
+
+ // if wanted, re-read line to get long form too
+ if (State & MODE_INSERT) {
+ st->get_searchpat = p_sft;
+ }
+ }
+ } else {
+ size_t tag_fname_len = strlen(st->tag_fname);
+ // Save the tag in a buffer.
+ // Use 0x02 to separate fields (Can't use NUL, because the
+ // hash key is terminated by NUL).
+ // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
+ // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
+ // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
+ // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
+ len = tag_fname_len + strlen(st->lbuf) + 3;
+ mfp = xmalloc(sizeof(char) + len + 1);
+ p = mfp;
+ p[0] = (char)(mtt + 1);
+ STRCPY(p + 1, st->tag_fname);
+#ifdef BACKSLASH_IN_FILENAME
+ // Ignore differences in slashes, avoid adding
+ // both path/file and path\file.
+ slash_adjust(p + 1);
+#endif
+ p[tag_fname_len + 1] = TAG_SEP;
+ s = p + 1 + tag_fname_len + 1;
+ STRCPY(s, st->lbuf);
+ }
+
+ if (mfp != NULL) {
+ hashitem_T *hi;
+
+ // Don't add identical matches.
+ // "mfp" is used as a hash key, there is a NUL byte to end
+ // the part that matters for comparing, more bytes may
+ // follow after it. E.g. help tags store the priority
+ // after the NUL.
+ *hash = hash_hash(mfp);
+ hi = hash_lookup(&st->ht_match[mtt], (const char *)mfp, strlen(mfp), *hash);
+ if (HASHITEM_EMPTY(hi)) {
+ hash_add_item(&st->ht_match[mtt], hi, mfp, *hash);
+ GA_APPEND(char *, &st->ga_match[mtt], mfp);
+ st->match_count++;
+ } else {
+ // duplicate tag, drop it
+ xfree(mfp);
+ }
+ }
+}
+
+/// Read and get all the tags from file st->tag_fname.
+/// Sets "st->stop_searching" to true to stop searching for additional tags.
+static void findtags_get_all_tags(findtags_state_T *st, findtags_match_args_T *margs,
+ char *buf_ffname)
+{
+ tagptrs_T tagp;
+ tagsearch_info_T search_info;
+ int retval;
+ hash_T hash = 0;
+
+ // This is only to avoid a compiler warning for using search_info
+ // uninitialised.
+ CLEAR_FIELD(search_info);
+
+ // Read and parse the lines in the file one by one
+ for (;;) {
+ // check for CTRL-C typed, more often when jumping around
+ if (st->state == TS_BINARY || st->state == TS_SKIP_BACK) {
+ line_breakcheck();
+ } else {
+ fast_breakcheck();
+ }
+ if ((st->flags & TAG_INS_COMP)) { // Double brackets for gcc
+ ins_compl_check_keys(30, false);
+ }
+ if (got_int || ins_compl_interrupted()) {
+ st->stop_searching = true;
+ break;
+ }
+ // When mincount is TAG_MANY, stop when enough matches have been
+ // found (for completion).
+ if (st->mincount == TAG_MANY && st->match_count >= TAG_MANY) {
+ st->stop_searching = true;
+ break;
+ }
+ if (st->get_searchpat) {
+ goto line_read_in;
+ }
+
+ retval = (int)findtags_get_next_line(st, &search_info);
+ if (retval == TAGS_READ_IGNORE) {
+ continue;
+ }
+ if (retval == TAGS_READ_EOF) {
+ break;
+ }
+
+line_read_in:
+
+ if (st->vimconv.vc_type != CONV_NONE) {
+ findtags_string_convert(st);
+ }
+
+ // When still at the start of the file, check for Emacs tags file
+ // format, and for "not sorted" flag.
+ if (st->state == TS_START) {
+ if (!findtags_start_state_handler(st, &margs->sortic, &search_info)) {
+ continue;
+ }
+ }
+
+ // When the line is too long the NUL will not be in the
+ // last-but-one byte (see vim_fgets()).
+ // Has been reported for Mozilla JS with extremely long names.
+ // In that case we need to increase lbuf_size.
+ if (st->lbuf[st->lbuf_size - 2] != NUL) {
+ st->lbuf_size *= 2;
+ xfree(st->lbuf);
+ st->lbuf = xmalloc((size_t)st->lbuf_size);
+
+ if (st->state == TS_STEP_FORWARD || st->state == TS_LINEAR) {
+ // Seek to the same position to read the same line again
+ vim_ignored = vim_fseek(st->fp, search_info.curr_offset, SEEK_SET);
+ }
+ // this will try the same thing again, make sure the offset is
+ // different
+ search_info.curr_offset = 0;
+ continue;
+ }
+
+ retval = (int)findtags_parse_line(st, &tagp, margs, &search_info);
+ if (retval == TAG_MATCH_NEXT) {
+ continue;
+ }
+ if (retval == TAG_MATCH_STOP) {
+ break;
+ }
+ if (retval == TAG_MATCH_FAIL) {
+ semsg(_("E431: Format error in tags file \"%s\""), st->tag_fname);
+ semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(st->fp));
+ st->stop_searching = true;
+ return;
+ }
+
+ // If a match is found, add it to ht_match[] and ga_match[].
+ if (findtags_match_tag(st, &tagp, margs)) {
+ findtags_add_match(st, &tagp, margs, buf_ffname, &hash);
+ }
+ } // forever
+}
+
+/// Search for tags matching "st->orgpat.pat" in the "st->tag_fname" tags file.
+/// Information needed to search for the tags is in the "st" state structure.
+/// The matching tags are returned in "st". If an error is encountered, then
+/// "st->stop_searching" is set to true.
+static void findtags_in_file(findtags_state_T *st, int flags, char *buf_ffname)
+{
+ findtags_match_args_T margs;
+
+ st->vimconv.vc_type = CONV_NONE;
+ st->tag_file_sorted = NUL;
+ st->fp = NULL;
+ findtags_matchargs_init(&margs, st->flags);
+
+ // A file that doesn't exist is silently ignored. Only when not a
+ // single file is found, an error message is given (further on).
+ if (curbuf->b_help) {
+ if (!findtags_in_help_init(st)) {
+ return;
+ }
+ }
+
+ st->fp = os_fopen(st->tag_fname, "r");
+ if (st->fp == NULL) {
+ return;
+ }
+
+ if (p_verbose >= 5) {
+ verbose_enter();
+ smsg(_("Searching tags file %s"), st->tag_fname);
+ verbose_leave();
+ }
+ st->did_open = true; // remember that we found at least one file
+
+ st->state = TS_START; // we're at the start of the file
+
+ // Read and parse the lines in the file one by one
+ findtags_get_all_tags(st, &margs, buf_ffname);
+
+ if (st->fp != NULL) {
+ fclose(st->fp);
+ st->fp = NULL;
+ }
+ if (st->vimconv.vc_type != CONV_NONE) {
+ convert_setup(&st->vimconv, NULL, NULL);
+ }
+
+ if (margs.sort_error) {
+ semsg(_("E432: Tags file not sorted: %s"), st->tag_fname);
+ }
+
+ // Stop searching if sufficient tags have been found.
+ if (st->match_count >= st->mincount) {
+ st->stop_searching = true;
+ }
+}
+
+/// Copy the tags found by find_tags() to "matchesp".
+/// Returns the number of matches copied.
+static int findtags_copy_matches(findtags_state_T *st, char ***matchesp)
+{
+ const bool name_only = (st->flags & TAG_NAMES);
+ char **matches;
+ int mtt;
+ int i;
+ char *mfp;
+ char *p;
+
+ if (st->match_count > 0) {
+ matches = xmalloc((size_t)st->match_count * sizeof(char *));
+ } else {
+ matches = NULL;
+ }
+ st->match_count = 0;
+ for (mtt = 0; mtt < MT_COUNT; mtt++) {
+ for (i = 0; i < st->ga_match[mtt].ga_len; i++) {
+ mfp = ((char **)(st->ga_match[mtt].ga_data))[i];
+ if (matches == NULL) {
+ xfree(mfp);
+ } else {
+ if (!name_only) {
+ // Change mtt back to zero-based.
+ *mfp = (char)(*mfp - 1);
+
+ // change the TAG_SEP back to NUL
+ for (p = mfp + 1; *p != NUL; p++) {
+ if (*p == TAG_SEP) {
+ *p = NUL;
+ }
+ }
+ }
+ matches[st->match_count++] = mfp;
+ }
+ }
+
+ ga_clear(&st->ga_match[mtt]);
+ hash_clear(&st->ht_match[mtt]);
+ }
+
+ *matchesp = matches;
+ return st->match_count;
+}
+
/// find_tags() - search for tags in tags files
///
/// Return FAIL if search completely failed (*num_matches will be 0, *matchesp
@@ -1357,94 +2315,33 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
/// TAG_REGEXP use "pat" as a regexp
/// TAG_NOIC don't always ignore case
/// TAG_KEEP_LANG keep language
-/// TAG_CSCOPE use cscope results for tags
/// TAG_NO_TAGFUNC do not call the 'tagfunc' function
///
/// @param pat pattern to search for
/// @param num_matches return: number of matches found
/// @param matchesp return: array of matches found
-/// @param mincount MAXCOL: find all matches other: minimal number of matches */
+/// @param mincount MAXCOL: find all matches
+/// other: minimal number of matches
/// @param buf_ffname name of buffer for priority
-int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mincount,
- char_u *buf_ffname)
+int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int mincount,
+ char *buf_ffname)
{
- FILE *fp;
- char_u *lbuf; // line buffer
- int lbuf_size = LSIZE; // length of lbuf
- char_u *tag_fname; // name of tag file
+ findtags_state_T st;
tagname_T tn; // info for get_tagfname()
int first_file; // trying first tag file
- tagptrs_T tagp;
- bool did_open = false; // did open a tag file
- bool stop_searching = false; // stop when match found or error
int retval = FAIL; // return value
- int is_static; // current tag line is static
- int is_current; // file name matches
- bool eof = false; // found end-of-file
- char_u *p;
- char_u *s;
- int i;
- int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value
- struct tag_search_info { // Binary search file offsets
- off_T low_offset; // offset for first char of first line that
- // could match
- off_T high_offset; // offset of char after last line that could
- // match
- off_T curr_offset; // Current file offset in search range
- off_T curr_offset_used; // curr_offset used when skipping back
- off_T match_offset; // Where the binary search found a tag
- int low_char; // first char at low_offset
- int high_char; // first char at high_offset
- } search_info;
- int tagcmp;
- off_T offset;
int round;
- enum {
- TS_START, // at start of file
- TS_LINEAR, // linear searching forward, till EOF
- TS_BINARY, // binary searching
- TS_SKIP_BACK, // skipping backwards
- TS_STEP_FORWARD, // stepping forwards
- } state; // Current search state
- int cmplen;
- int match; // matches
- int match_no_ic = 0; // matches with rm_ic == FALSE
- int match_re; // match with regexp
- int matchoff = 0;
int save_emsg_off;
- char *mfp;
- garray_T ga_match[MT_COUNT]; // stores matches in sequence
- hashtab_T ht_match[MT_COUNT]; // stores matches by key
- hash_T hash = 0;
- int match_count = 0; // number of matches found
- char **matches;
- int mtt;
int help_save;
- int help_pri = 0;
- char_u *help_lang_find = NULL; // lang to be found
- char_u help_lang[3]; // lang of current tags file
- char_u *saved_pat = NULL; // copy of pat[]
- bool is_txt = false;
-
- pat_T orgpat; // holds unconverted pattern info
- vimconv_T vimconv;
+ int i;
+ char *saved_pat = NULL; // copy of pat[]
- int findall = (mincount == MAXCOL || mincount == TAG_MANY);
- // find all matching tags
- bool sort_error = false; // tags file not sorted
- int linear; // do a linear search
- bool sortic = false; // tag file sorted in nocase
- bool line_error = false; // syntax error
+ int findall = (mincount == MAXCOL || mincount == TAG_MANY); // find all matching tags
int has_re = (flags & TAG_REGEXP); // regexp used
- int help_only = (flags & TAG_HELP);
- int name_only = (flags & TAG_NAMES);
int noic = (flags & TAG_NOIC);
- int get_it_again = FALSE;
- int use_cscope = (flags & TAG_CSCOPE);
int verbose = (flags & TAG_VERBOSE);
- int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
int save_p_ic = p_ic;
// Change the value of 'ignorecase' according to 'tagcase' for the
@@ -1469,829 +2366,109 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
}
help_save = curbuf->b_help;
- orgpat.pat = pat;
- orgpat.regmatch.regprog = NULL;
- vimconv.vc_type = CONV_NONE;
-
- /*
- * Allocate memory for the buffers that are used
- */
- lbuf = xmalloc((size_t)lbuf_size);
- tag_fname = xmalloc(MAXPATHL + 1);
- for (mtt = 0; mtt < MT_COUNT; mtt++) {
- ga_init(&ga_match[mtt], sizeof(char *), 100);
- hash_init(&ht_match[mtt]);
- }
- STRCPY(tag_fname, "from cscope"); // for error messages
+ findtags_state_init(&st, pat, flags, mincount);
- /*
- * Initialize a few variables
- */
- if (help_only) { // want tags from help file
+ // Initialize a few variables
+ if (st.help_only) { // want tags from help file
curbuf->b_help = true; // will be restored later
- } else if (use_cscope) {
- // Make sure we don't mix help and cscope, confuses Coverity.
- help_only = false;
- curbuf->b_help = false;
}
- orgpat.len = (int)STRLEN(pat);
if (curbuf->b_help) {
// When "@ab" is specified use only the "ab" language, otherwise
// search all languages.
- if (orgpat.len > 3 && pat[orgpat.len - 3] == '@'
- && ASCII_ISALPHA(pat[orgpat.len - 2])
- && ASCII_ISALPHA(pat[orgpat.len - 1])) {
- saved_pat = vim_strnsave(pat, (size_t)orgpat.len - 3);
- help_lang_find = &pat[orgpat.len - 2];
- orgpat.pat = saved_pat;
- orgpat.len -= 3;
+ if (st.orgpat->len > 3 && pat[st.orgpat->len - 3] == '@'
+ && ASCII_ISALPHA(pat[st.orgpat->len - 2])
+ && ASCII_ISALPHA(pat[st.orgpat->len - 1])) {
+ saved_pat = xstrnsave(pat, (size_t)st.orgpat->len - 3);
+ st.help_lang_find = &pat[st.orgpat->len - 2];
+ st.orgpat->pat = saved_pat;
+ st.orgpat->len -= 3;
}
}
- if (p_tl != 0 && orgpat.len > p_tl) { // adjust for 'taglength'
- orgpat.len = (int)p_tl;
+ if (p_tl != 0 && st.orgpat->len > p_tl) { // adjust for 'taglength'
+ st.orgpat->len = (int)p_tl;
}
save_emsg_off = emsg_off;
- emsg_off = TRUE; // don't want error for invalid RE here
- prepare_pats(&orgpat, has_re);
+ emsg_off = true; // don't want error for invalid RE here
+ prepare_pats(st.orgpat, has_re);
emsg_off = save_emsg_off;
- if (has_re && orgpat.regmatch.regprog == NULL) {
+ if (has_re && st.orgpat->regmatch.regprog == NULL) {
goto findtag_end;
}
- // This is only to avoid a compiler warning for using search_info
- // uninitialised.
- CLEAR_FIELD(search_info);
+ retval = findtags_apply_tfu(&st, pat, buf_ffname);
+ if (retval != NOTDONE) {
+ goto findtag_end;
+ }
+
+ // re-initialize the default return value
+ retval = FAIL;
- if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use) {
- tfu_in_use = true;
- retval = find_tagfunc_tags(pat, &ga_match[0], &match_count,
- flags, buf_ffname);
- tfu_in_use = false;
- if (retval != NOTDONE) {
- goto findtag_end;
- }
- }
-
- /*
- * When finding a specified number of matches, first try with matching
- * case, so binary search can be used, and try ignore-case matches in a
- * second loop.
- * When finding all matches, 'tagbsearch' is off, or there is no fixed
- * string to look for, ignore case right away to avoid going though the
- * tags files twice.
- * When the tag file is case-fold sorted, it is either one or the other.
- * Only ignore case when TAG_NOIC not used or 'ignorecase' set.
- */
// Set a flag if the file extension is .txt
if ((flags & TAG_KEEP_LANG)
- && help_lang_find == NULL
+ && st.help_lang_find == NULL
&& curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
+ && (i = (int)strlen(curbuf->b_fname)) > 4
&& STRICMP(curbuf->b_fname + i - 4, ".txt") == 0) {
- is_txt = true;
- }
- orgpat.regmatch.rm_ic = ((p_ic || !noic)
- && (findall || orgpat.headlen == 0 || !p_tbs));
- for (round = 1; round <= 2; ++round) {
- linear = (orgpat.headlen == 0 || !p_tbs || round == 2);
+ st.is_txt = true;
+ }
+
+ // When finding a specified number of matches, first try with matching
+ // case, so binary search can be used, and try ignore-case matches in a
+ // second loop.
+ // When finding all matches, 'tagbsearch' is off, or there is no fixed
+ // string to look for, ignore case right away to avoid going though the
+ // tags files twice.
+ // When the tag file is case-fold sorted, it is either one or the other.
+ // Only ignore case when TAG_NOIC not used or 'ignorecase' set.
+ st.orgpat->regmatch.rm_ic = ((p_ic || !noic)
+ && (findall || st.orgpat->headlen == 0 || !p_tbs));
+ for (round = 1; round <= 2; round++) {
+ st.linear = (st.orgpat->headlen == 0 || !p_tbs || round == 2);
// Try tag file names from tags option one by one.
for (first_file = true;
- use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK;
+ get_tagfname(&tn, first_file, st.tag_fname) == OK;
first_file = false) {
- // A file that doesn't exist is silently ignored. Only when not a
- // single file is found, an error message is given (further on).
- if (use_cscope) {
- fp = NULL; // avoid GCC warning
- } else {
- if (curbuf->b_help) {
- // Keep en if the file extension is .txt
- if (is_txt) {
- STRCPY(help_lang, "en");
- } else {
- // Prefer help tags according to 'helplang'. Put the
- // two-letter language name in help_lang[].
- i = (int)STRLEN(tag_fname);
- if (i > 3 && tag_fname[i - 3] == '-') {
- STRCPY(help_lang, tag_fname + i - 2);
- } else {
- STRCPY(help_lang, "en");
- }
- }
-
- // When searching for a specific language skip tags files
- // for other languages.
- if (help_lang_find != NULL
- && STRICMP(help_lang, help_lang_find) != 0) {
- continue;
- }
-
- // For CTRL-] in a help file prefer a match with the same
- // language.
- if ((flags & TAG_KEEP_LANG)
- && help_lang_find == NULL
- && curbuf->b_fname != NULL
- && (i = (int)STRLEN(curbuf->b_fname)) > 4
- && curbuf->b_fname[i - 1] == 'x'
- && curbuf->b_fname[i - 4] == '.'
- && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) {
- help_pri = 0;
- } else {
- help_pri = 1;
- for (s = p_hlg; *s != NUL; ++s) {
- if (STRNICMP(s, help_lang, 2) == 0) {
- break;
- }
- help_pri++;
- if ((s = (char_u *)vim_strchr((char *)s, ',')) == NULL) {
- break;
- }
- }
- if (s == NULL || *s == NUL) {
- // Language not in 'helplang': use last, prefer English,
- // unless found already.
- help_pri++;
- if (STRICMP(help_lang, "en") != 0) {
- help_pri++;
- }
- }
- }
- }
-
- if ((fp = os_fopen((char *)tag_fname, "r")) == NULL) {
- continue;
- }
-
- if (p_verbose >= 5) {
- verbose_enter();
- smsg(_("Searching tags file %s"), tag_fname);
- verbose_leave();
- }
- }
- did_open = true; // remember that we found at least one file
-
- state = TS_START; // we're at the start of the file
-
- /*
- * Read and parse the lines in the file one by one
- */
- for (;;) {
- // check for CTRL-C typed, more often when jumping around
- if (state == TS_BINARY || state == TS_SKIP_BACK) {
- line_breakcheck();
- } else {
- fast_breakcheck();
- }
- if ((flags & TAG_INS_COMP)) { // Double brackets for gcc
- ins_compl_check_keys(30, false);
- }
- if (got_int || ins_compl_interrupted()) {
- stop_searching = true;
- break;
- }
- // When mincount is TAG_MANY, stop when enough matches have been
- // found (for completion).
- if (mincount == TAG_MANY && match_count >= TAG_MANY) {
- stop_searching = true;
- retval = OK;
- break;
- }
- if (get_it_again) {
- goto line_read_in;
- }
- /*
- * For binary search: compute the next offset to use.
- */
- if (state == TS_BINARY) {
- offset = search_info.low_offset + ((search_info.high_offset
- - search_info.low_offset) / 2);
- if (offset == search_info.curr_offset) {
- break; // End the binary search without a match.
- } else {
- search_info.curr_offset = offset;
- }
- } else if (state == TS_SKIP_BACK) {
- // Skipping back (after a match during binary search).
- search_info.curr_offset -= lbuf_size * 2;
- if (search_info.curr_offset < 0) {
- search_info.curr_offset = 0;
- rewind(fp);
- state = TS_STEP_FORWARD;
- }
- }
-
- /*
- * When jumping around in the file, first read a line to find the
- * start of the next line.
- */
- if (state == TS_BINARY || state == TS_SKIP_BACK) {
- // Adjust the search file offset to the correct position
- search_info.curr_offset_used = search_info.curr_offset;
- vim_fseek(fp, search_info.curr_offset, SEEK_SET);
- eof = vim_fgets(lbuf, lbuf_size, fp);
- if (!eof && search_info.curr_offset != 0) {
- // The explicit cast is to work around a bug in gcc 3.4.2
- // (repeated below).
- search_info.curr_offset = vim_ftell(fp);
- if (search_info.curr_offset == search_info.high_offset) {
- // oops, gone a bit too far; try from low offset
- vim_fseek(fp, search_info.low_offset, SEEK_SET);
- search_info.curr_offset = search_info.low_offset;
- }
- eof = vim_fgets(lbuf, lbuf_size, fp);
- }
- // skip empty and blank lines
- while (!eof && vim_isblankline(lbuf)) {
- search_info.curr_offset = vim_ftell(fp);
- eof = vim_fgets(lbuf, lbuf_size, fp);
- }
- if (eof) {
- // Hit end of file. Skip backwards.
- state = TS_SKIP_BACK;
- search_info.match_offset = vim_ftell(fp);
- search_info.curr_offset = search_info.curr_offset_used;
- continue;
- }
- }
- /*
- * Not jumping around in the file: Read the next line.
- */
- else {
- // skip empty and blank lines
- do {
- eof = use_cscope
- ? cs_fgets(lbuf, lbuf_size)
- : vim_fgets(lbuf, lbuf_size, fp);
- } while (!eof && vim_isblankline(lbuf));
-
- if (eof) {
- break; // end of file
- }
- }
-line_read_in:
-
- if (vimconv.vc_type != CONV_NONE) {
- char_u *conv_line;
- int len;
-
- // Convert every line. Converting the pattern from 'enc' to
- // the tags file encoding doesn't work, because characters are
- // not recognized.
- conv_line = string_convert(&vimconv, lbuf, NULL);
- if (conv_line != NULL) {
- // Copy or swap lbuf and conv_line.
- len = (int)STRLEN(conv_line) + 1;
- if (len > lbuf_size) {
- xfree(lbuf);
- lbuf = conv_line;
- lbuf_size = len;
- } else {
- STRCPY(lbuf, conv_line);
- xfree(conv_line);
- }
- }
- }
-
- /*
- * When still at the start of the file, check for Emacs tags file
- * format, and for "not sorted" flag.
- */
- if (state == TS_START) {
- // The header ends when the line sorts below "!_TAG_". When
- // case is folded lower case letters sort before "_".
- if (STRNCMP(lbuf, "!_TAG_", 6) <= 0
- || (lbuf[0] == '!' && ASCII_ISLOWER(lbuf[1]))) {
- if (STRNCMP(lbuf, "!_TAG_", 6) != 0) {
- // Non-header item before the header, e.g. "!" itself.
- goto parse_line;
- }
-
- /*
- * Read header line.
- */
- if (STRNCMP(lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) {
- tag_file_sorted = lbuf[18];
- }
- if (STRNCMP(lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) {
- // Prepare to convert every line from the specified
- // encoding to 'encoding'.
- for (p = lbuf + 20; *p > ' ' && *p < 127; p++) {}
- *p = NUL;
- convert_setup(&vimconv, lbuf + 20, p_enc);
- }
-
- // Read the next line. Unrecognized flags are ignored.
- continue;
- }
-
- // Headers ends.
-
- /*
- * When there is no tag head, or ignoring case, need to do a
- * linear search.
- * When no "!_TAG_" is found, default to binary search. If
- * the tag file isn't sorted, the second loop will find it.
- * When "!_TAG_FILE_SORTED" found: start binary search if
- * flag set.
- * For cscope, it's always linear.
- */
- if (linear || use_cscope) {
- state = TS_LINEAR;
- } else if (tag_file_sorted == NUL) {
- state = TS_BINARY;
- } else if (tag_file_sorted == '1') {
- state = TS_BINARY;
- } else if (tag_file_sorted == '2') {
- state = TS_BINARY;
- sortic = true;
- orgpat.regmatch.rm_ic = (p_ic || !noic);
- } else {
- state = TS_LINEAR;
- }
-
- if (state == TS_BINARY && orgpat.regmatch.rm_ic && !sortic) {
- // Binary search won't work for ignoring case, use linear
- // search.
- linear = true;
- state = TS_LINEAR;
- }
-
- // When starting a binary search, get the size of the file and
- // compute the first offset.
- if (state == TS_BINARY) {
- if (vim_fseek(fp, 0, SEEK_END) != 0) {
- // can't seek, don't use binary search
- state = TS_LINEAR;
- } else {
- // Get the tag file size.
- // Don't use lseek(), it doesn't work
- // properly on MacOS Catalina.
- const off_T filesize = vim_ftell(fp);
- vim_fseek(fp, 0, SEEK_SET);
-
- // Calculate the first read offset in the file. Start
- // the search in the middle of the file.
- search_info.low_offset = 0;
- search_info.low_char = 0;
- search_info.high_offset = filesize;
- search_info.curr_offset = 0;
- search_info.high_char = 0xff;
- }
- continue;
- }
- }
-
-parse_line:
- // When the line is too long the NUL will not be in the
- // last-but-one byte (see vim_fgets()).
- // Has been reported for Mozilla JS with extremely long names.
- // In that case we need to increase lbuf_size.
- if (lbuf[lbuf_size - 2] != NUL && !use_cscope) {
- lbuf_size *= 2;
- xfree(lbuf);
- lbuf = xmalloc((size_t)lbuf_size);
- // this will try the same thing again, make sure the offset is
- // different
- search_info.curr_offset = 0;
- continue;
- }
-
- // Figure out where the different strings are in this line.
- // For "normal" tags: Do a quick check if the tag matches.
- // This speeds up tag searching a lot!
- if (orgpat.headlen) {
- CLEAR_FIELD(tagp);
- tagp.tagname = lbuf;
- tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);
- if (tagp.tagname_end == NULL) {
- // Corrupted tag line.
- line_error = true;
- break;
- }
-
- /*
- * Skip this line if the length of the tag is different and
- * there is no regexp, or the tag is too short.
- */
- cmplen = (int)(tagp.tagname_end - tagp.tagname);
- if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = (int)p_tl;
- }
- if (has_re && orgpat.headlen < cmplen) {
- cmplen = orgpat.headlen;
- } else if (state == TS_LINEAR && orgpat.headlen != cmplen) {
- continue;
- }
-
- if (state == TS_BINARY) {
- /*
- * Simplistic check for unsorted tags file.
- */
- i = (int)tagp.tagname[0];
- if (sortic) {
- i = TOUPPER_ASC(tagp.tagname[0]);
- }
- if (i < search_info.low_char || i > search_info.high_char) {
- sort_error = true;
- }
-
- /*
- * Compare the current tag with the searched tag.
- */
- if (sortic) {
- tagcmp = tag_strnicmp(tagp.tagname, orgpat.head,
- (size_t)cmplen);
- } else {
- tagcmp = STRNCMP(tagp.tagname, orgpat.head, cmplen);
- }
-
- /*
- * A match with a shorter tag means to search forward.
- * A match with a longer tag means to search backward.
- */
- if (tagcmp == 0) {
- if (cmplen < orgpat.headlen) {
- tagcmp = -1;
- } else if (cmplen > orgpat.headlen) {
- tagcmp = 1;
- }
- }
-
- if (tagcmp == 0) {
- // We've located the tag, now skip back and search
- // forward until the first matching tag is found.
- state = TS_SKIP_BACK;
- search_info.match_offset = search_info.curr_offset;
- continue;
- }
- if (tagcmp < 0) {
- search_info.curr_offset = vim_ftell(fp);
- if (search_info.curr_offset < search_info.high_offset) {
- search_info.low_offset = search_info.curr_offset;
- if (sortic) {
- search_info.low_char =
- TOUPPER_ASC(tagp.tagname[0]);
- } else {
- search_info.low_char = tagp.tagname[0];
- }
- continue;
- }
- }
- if (tagcmp > 0
- && search_info.curr_offset != search_info.high_offset) {
- search_info.high_offset = search_info.curr_offset;
- if (sortic) {
- search_info.high_char =
- TOUPPER_ASC(tagp.tagname[0]);
- } else {
- search_info.high_char = tagp.tagname[0];
- }
- continue;
- }
-
- // No match yet and are at the end of the binary search.
- break;
- } else if (state == TS_SKIP_BACK) {
- assert(cmplen >= 0);
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- state = TS_STEP_FORWARD;
- } else {
- // Have to skip back more. Restore the curr_offset
- // used, otherwise we get stuck at a long line.
- search_info.curr_offset = search_info.curr_offset_used;
- }
- continue;
- } else if (state == TS_STEP_FORWARD) {
- assert(cmplen >= 0);
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- if ((off_T)vim_ftell(fp) > search_info.match_offset) {
- break; // past last match
- } else {
- continue; // before first match
- }
- }
- } else {
- // skip this match if it can't match
- assert(cmplen >= 0);
- }
- if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) {
- continue;
- }
-
- // Can be a matching tag, isolate the file name and command.
- tagp.fname = tagp.tagname_end + 1;
- tagp.fname_end = (char_u *)vim_strchr((char *)tagp.fname, TAB);
- tagp.command = tagp.fname_end + 1;
- if (tagp.fname_end == NULL) {
- i = FAIL;
- } else {
- i = OK;
- }
- } else {
- i = parse_tag_line(lbuf,
- &tagp);
- }
- if (i == FAIL) {
- line_error = true;
- break;
- }
-
- /*
- * First try matching with the pattern literally (also when it is
- * a regexp).
- */
- cmplen = (int)(tagp.tagname_end - tagp.tagname);
- if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength'
- cmplen = (int)p_tl;
- }
- // if tag length does not match, don't try comparing
- if (orgpat.len != cmplen) {
- match = FALSE;
- } else {
- if (orgpat.regmatch.rm_ic) {
- assert(cmplen >= 0);
- match = mb_strnicmp(tagp.tagname, orgpat.pat, (size_t)cmplen) == 0;
- if (match) {
- match_no_ic = (STRNCMP(tagp.tagname, orgpat.pat,
- cmplen) == 0);
- }
- } else {
- match = (STRNCMP(tagp.tagname, orgpat.pat, cmplen) == 0);
- }
- }
-
- /*
- * Has a regexp: Also find tags matching regexp.
- */
- match_re = FALSE;
- if (!match && orgpat.regmatch.regprog != NULL) {
- int cc;
-
- cc = *tagp.tagname_end;
- *tagp.tagname_end = NUL;
- match = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
- if (match) {
- matchoff = (int)(orgpat.regmatch.startp[0] - tagp.tagname);
- if (orgpat.regmatch.rm_ic) {
- orgpat.regmatch.rm_ic = false;
- match_no_ic = vim_regexec(&orgpat.regmatch, (char *)tagp.tagname, (colnr_T)0);
- orgpat.regmatch.rm_ic = true;
- }
- }
- *tagp.tagname_end = (char_u)cc;
- match_re = true;
- }
-
- // If a match is found, add it to ht_match[] and ga_match[].
- if (match) {
- size_t len = 0;
-
- if (use_cscope) {
- // Don't change the ordering, always use the same table.
- mtt = MT_GL_OTH;
- } else {
- // Decide in which array to store this match.
- is_current = test_for_current(tagp.fname, tagp.fname_end, tag_fname,
- buf_ffname);
- is_static = test_for_static(&tagp);
-
- // Decide in which of the sixteen tables to store this match.
- if (is_static) {
- if (is_current) {
- mtt = MT_ST_CUR;
- } else {
- mtt = MT_ST_OTH;
- }
- } else {
- if (is_current) {
- mtt = MT_GL_CUR;
- } else {
- mtt = MT_GL_OTH;
- }
- }
- if (orgpat.regmatch.rm_ic && !match_no_ic) {
- mtt += MT_IC_OFF;
- }
- if (match_re) {
- mtt += MT_RE_OFF;
- }
- }
-
- // Add the found match in ht_match[mtt] and ga_match[mtt].
- // Store the info we need later, which depends on the kind of
- // tags we are dealing with.
- if (help_only) {
-#define ML_EXTRA 3
- // Append the help-heuristic number after the tagname, for
- // sorting it later. The heuristic is ignored for
- // detecting duplicates.
- // The format is {tagname}@{lang}NUL{heuristic}NUL
- *tagp.tagname_end = NUL;
- len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
-
- p = (char_u *)mfp;
- STRCPY(p, tagp.tagname);
- p[len] = '@';
- STRCPY(p + len + 1, help_lang);
- snprintf((char *)p + len + 1 + ML_EXTRA, STRLEN(p) + len + 1 + ML_EXTRA, "%06d",
- help_heuristic((char *)tagp.tagname,
- match_re ? matchoff : 0, !match_no_ic)
- + help_pri);
-
- *tagp.tagname_end = TAB;
- } else if (name_only) {
- if (get_it_again) {
- char_u *temp_end = tagp.command;
-
- if (*temp_end == '/') {
- while (*temp_end && *temp_end != '\r'
- && *temp_end != '\n'
- && *temp_end != '$') {
- temp_end++;
- }
- }
-
- if (tagp.command + 2 < temp_end) {
- len = (size_t)(temp_end - tagp.command - 2);
- mfp = xmalloc(len + 2);
- STRLCPY(mfp, tagp.command + 2, len + 1);
- } else {
- mfp = NULL;
- }
- get_it_again = false;
- } else {
- len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char) + len + 1);
- STRLCPY(mfp, tagp.tagname, len + 1);
-
- // if wanted, re-read line to get long form too
- if (State & MODE_INSERT) {
- get_it_again = p_sft;
- }
- }
- } else {
- size_t tag_fname_len = STRLEN(tag_fname);
- // Save the tag in a buffer.
- // Use 0x02 to separate fields (Can't use NUL, because the
- // hash key is terminated by NUL).
- // Emacs tag: <mtt><tag_fname><0x02><ebuf><0x02><lbuf><NUL>
- // other tag: <mtt><tag_fname><0x02><0x02><lbuf><NUL>
- // without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
- // Here <mtt> is the "mtt" value plus 1 to avoid NUL.
- len = tag_fname_len + STRLEN(lbuf) + 3;
- mfp = xmalloc(sizeof(char) + len + 1);
- p = (char_u *)mfp;
- p[0] = (char_u)(mtt + 1);
- STRCPY(p + 1, tag_fname);
-#ifdef BACKSLASH_IN_FILENAME
- // Ignore differences in slashes, avoid adding
- // both path/file and path\file.
- slash_adjust(p + 1);
-#endif
- p[tag_fname_len + 1] = TAG_SEP;
- s = p + 1 + tag_fname_len + 1;
- STRCPY(s, lbuf);
- }
-
- if (mfp != NULL) {
- hashitem_T *hi;
-
- // Don't add identical matches.
- // Add all cscope tags, because they are all listed.
- // "mfp" is used as a hash key, there is a NUL byte to end
- // the part that matters for comparing, more bytes may
- // follow after it. E.g. help tags store the priority
- // after the NUL.
- if (use_cscope) {
- hash++;
- } else {
- hash = hash_hash((char_u *)mfp);
- }
- hi = hash_lookup(&ht_match[mtt], (const char *)mfp,
- STRLEN(mfp), hash);
- if (HASHITEM_EMPTY(hi)) {
- hash_add_item(&ht_match[mtt], hi, (char_u *)mfp, hash);
- ga_grow(&ga_match[mtt], 1);
- ((char **)(ga_match[mtt].ga_data))[ga_match[mtt].ga_len++] = mfp;
- match_count++;
- } else {
- // duplicate tag, drop it
- xfree(mfp);
- }
- }
- }
- if (use_cscope && eof) {
- break;
- }
- } // forever
-
- if (line_error) {
- semsg(_("E431: Format error in tags file \"%s\""), tag_fname);
- if (!use_cscope) {
- semsg(_("Before byte %" PRId64), (int64_t)vim_ftell(fp));
- }
- stop_searching = true;
- line_error = false;
- }
-
- if (!use_cscope) {
- fclose(fp);
- }
- if (vimconv.vc_type != CONV_NONE) {
- convert_setup(&vimconv, NULL, NULL);
- }
-
- tag_file_sorted = NUL;
- if (sort_error) {
- semsg(_("E432: Tags file not sorted: %s"), tag_fname);
- sort_error = false;
- }
-
- /*
- * Stop searching if sufficient tags have been found.
- */
- if (match_count >= mincount) {
+ findtags_in_file(&st, flags, buf_ffname);
+ if (st.stop_searching) {
retval = OK;
- stop_searching = true;
- }
-
- if (stop_searching || use_cscope) {
break;
}
} // end of for-each-file loop
- if (!use_cscope) {
- tagname_free(&tn);
- }
+ tagname_free(&tn);
// stop searching when already did a linear search, or when TAG_NOIC
// used, and 'ignorecase' not set or already did case-ignore search
- if (stop_searching || linear || (!p_ic && noic) || orgpat.regmatch.rm_ic) {
- break;
- }
- if (use_cscope) {
+ if (st.stop_searching || st.linear || (!p_ic && noic)
+ || st.orgpat->regmatch.rm_ic) {
break;
}
- orgpat.regmatch.rm_ic = TRUE; // try another time while ignoring case
+
+ // try another time while ignoring case
+ st.orgpat->regmatch.rm_ic = true;
}
- if (!stop_searching) {
- if (!did_open && verbose) { // never opened any tags file
+ if (!st.stop_searching) {
+ if (!st.did_open && verbose) { // never opened any tags file
emsg(_("E433: No tags file"));
}
retval = OK; // It's OK even when no tag found
}
findtag_end:
- xfree(lbuf);
- vim_regfree(orgpat.regmatch.regprog);
- xfree(tag_fname);
+ findtags_state_free(&st);
- /*
- * Move the matches from the ga_match[] arrays into one list of
- * matches. When retval == FAIL, free the matches.
- */
+ // Move the matches from the ga_match[] arrays into one list of
+ // matches. When retval == FAIL, free the matches.
if (retval == FAIL) {
- match_count = 0;
+ st.match_count = 0;
}
- if (match_count > 0) {
- matches = xmalloc((size_t)match_count * sizeof(char *));
- } else {
- matches = NULL;
- }
- match_count = 0;
- for (mtt = 0; mtt < MT_COUNT; mtt++) {
- for (i = 0; i < ga_match[mtt].ga_len; i++) {
- mfp = ((char **)(ga_match[mtt].ga_data))[i];
- if (matches == NULL) {
- xfree(mfp);
- } else {
- if (!name_only) {
- // Change mtt back to zero-based.
- *mfp = (char)(*mfp - 1);
-
- // change the TAG_SEP back to NUL
- for (p = (char_u *)mfp + 1; *p != NUL; p++) {
- if (*p == TAG_SEP) {
- *p = NUL;
- }
- }
- }
- matches[match_count++] = mfp;
- }
- }
-
- ga_clear(&ga_match[mtt]);
- hash_clear(&ht_match[mtt]);
- }
-
- *matchesp = matches;
- *num_matches = match_count;
+ *num_matches = findtags_copy_matches(&st, matchesp);
curbuf->b_help = help_save;
xfree(saved_pat);
@@ -2303,19 +2480,17 @@ findtag_end:
static garray_T tag_fnames = GA_EMPTY_INIT_VALUE;
-/*
- * Callback function for finding all "tags" and "tags-??" files in
- * 'runtimepath' doc directories.
- */
+// Callback function for finding all "tags" and "tags-??" files in
+// 'runtimepath' doc directories.
static void found_tagfile_cb(char *fname, void *cookie)
{
- char_u *const tag_fname = vim_strsave((char_u *)fname);
+ char *const tag_fname = xstrdup(fname);
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(tag_fname);
#endif
simplify_filename(tag_fname);
- GA_APPEND(char_u *, &tag_fnames, tag_fname);
+ GA_APPEND(char *, &tag_fnames, tag_fname);
}
#if defined(EXITFREE)
@@ -2334,25 +2509,23 @@ void free_tag_stuff(void)
/// For help files, use "tags" file only.
///
/// @param tnp holds status info
-/// @param first TRUE when first file name is wanted
+/// @param first true when first file name is wanted
/// @param buf pointer to buffer of MAXPATHL chars
///
/// @return FAIL if no more tag file names, OK otherwise.
-int get_tagfname(tagname_T *tnp, int first, char_u *buf)
+int get_tagfname(tagname_T *tnp, int first, char *buf)
{
- char_u *fname = NULL;
- char_u *r_ptr;
+ char *fname = NULL;
+ char *r_ptr;
if (first) {
CLEAR_POINTER(tnp);
}
if (curbuf->b_help) {
- /*
- * For help files it's done in a completely different way:
- * Find "doc/tags" and "doc/tags-??" in all directories in
- * 'runtimepath'.
- */
+ // For help files it's done in a completely different way:
+ // Find "doc/tags" and "doc/tags-??" in all directories in
+ // 'runtimepath'.
if (first) {
ga_clear_strings(&tag_fnames);
ga_init(&tag_fnames, (int)sizeof(char *), 10);
@@ -2366,7 +2539,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) {
return FAIL;
}
- ++tnp->tn_hf_idx;
+ tnp->tn_hf_idx++;
STRCPY(buf, p_hf);
STRCPY(path_tail((char *)buf), "tags");
#ifdef BACKSLASH_IN_FILENAME
@@ -2375,12 +2548,12 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
simplify_filename(buf);
for (int i = 0; i < tag_fnames.ga_len; i++) {
- if (STRCMP(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
+ if (strcmp(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
return FAIL; // avoid duplicate file names
}
}
} else {
- STRLCPY(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
+ xstrlcpy(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
}
return OK;
}
@@ -2388,16 +2561,14 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (first) {
// Init. We make a copy of 'tags', because autocommands may change
// the value without notifying us.
- tnp->tn_tags = vim_strsave((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags);
- tnp->tn_np = (char *)tnp->tn_tags;
+ tnp->tn_tags = xstrdup((*curbuf->b_p_tags != NUL) ? curbuf->b_p_tags : p_tags);
+ tnp->tn_np = tnp->tn_tags;
}
- /*
- * Loop until we have found a file name that can be used.
- * There are two states:
- * tnp->tn_did_filefind_init == FALSE: setup for next part in 'tags'.
- * tnp->tn_did_filefind_init == TRUE: find next file in this part.
- */
+ // Loop until we have found a file name that can be used.
+ // There are two states:
+ // tnp->tn_did_filefind_init == false: setup for next part in 'tags'.
+ // tnp->tn_did_filefind_init == true: find next file in this part.
for (;;) {
if (tnp->tn_did_filefind_init) {
fname = vim_findfile(tnp->tn_search_ctx);
@@ -2405,9 +2576,9 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
break;
}
- tnp->tn_did_filefind_init = FALSE;
+ tnp->tn_did_filefind_init = false;
} else {
- char_u *filename = NULL;
+ char *filename = NULL;
// Stop when used all parts of 'tags'.
if (*tnp->tn_np == NUL) {
@@ -2416,26 +2587,24 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
return FAIL;
}
- /*
- * Copy next file name into buf.
- */
+ // Copy next file name into buf.
buf[0] = NUL;
- (void)copy_option_part(&tnp->tn_np, (char *)buf, MAXPATHL - 1, " ,");
+ (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
r_ptr = vim_findfile_stopdir(buf);
// move the filename one char forward and truncate the
// filepath with a NUL
- filename = (char_u *)path_tail((char *)buf);
+ filename = path_tail(buf);
STRMOVE(filename + 1, filename);
*filename++ = NUL;
tnp->tn_search_ctx = vim_findfile_init(buf, filename,
r_ptr, 100,
- FALSE, // don't free visited list
+ false, // don't free visited list
FINDFILE_FILE, // we search for a file
- tnp->tn_search_ctx, true, (char_u *)curbuf->b_ffname);
+ tnp->tn_search_ctx, true, curbuf->b_ffname);
if (tnp->tn_search_ctx != NULL) {
- tnp->tn_did_filefind_init = TRUE;
+ tnp->tn_did_filefind_init = true;
}
}
}
@@ -2445,9 +2614,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
return OK;
}
-/*
- * Free the contents of a tagname_T that was filled by get_tagfname().
- */
+// Free the contents of a tagname_T that was filled by get_tagfname().
void tagname_free(tagname_T *tnp)
{
xfree(tnp->tn_tags);
@@ -2459,18 +2626,18 @@ void tagname_free(tagname_T *tnp)
/// Parse one line from the tags file. Find start/end of tag name, start/end of
/// file name and start of search pattern.
///
-/// If is_etag is TRUE, tagp->fname and tagp->fname_end are not set.
+/// If is_etag is true, tagp->fname and tagp->fname_end are not set.
///
/// @param lbuf line to be parsed
///
/// @return FAIL if there is a format error in this line, OK otherwise.
-static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
+static int parse_tag_line(char *lbuf, tagptrs_T *tagp)
{
- char_u *p;
+ char *p;
// Isolate the tagname, from lbuf up to the first white
tagp->tagname = lbuf;
- p = (char_u *)vim_strchr((char *)lbuf, TAB);
+ p = vim_strchr(lbuf, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2481,7 +2648,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
p++;
}
tagp->fname = p;
- p = (char_u *)vim_strchr((char *)p, TAB);
+ p = vim_strchr(p, TAB);
if (p == NULL) {
return FAIL;
}
@@ -2499,44 +2666,42 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
return OK;
}
-/*
- * Check if tagname is a static tag
- *
- * Static tags produced by the older ctags program have the format:
- * 'file:tag file /pattern'.
- * This is only recognized when both occurrence of 'file' are the same, to
- * avoid recognizing "string::string" or ":exit".
- *
- * Static tags produced by the new ctags program have the format:
- * 'tag file /pattern/;"<Tab>file:' "
- *
- * Return TRUE if it is a static tag and adjust *tagname to the real tag.
- * Return FALSE if it is not a static tag.
- */
+// Check if tagname is a static tag
+//
+// Static tags produced by the older ctags program have the format:
+// 'file:tag file /pattern'.
+// This is only recognized when both occurrence of 'file' are the same, to
+// avoid recognizing "string::string" or ":exit".
+//
+// Static tags produced by the new ctags program have the format:
+// 'tag file /pattern/;"<Tab>file:' "
+//
+// Return true if it is a static tag and adjust *tagname to the real tag.
+// Return false if it is not a static tag.
static bool test_for_static(tagptrs_T *tagp)
{
- char_u *p;
+ char *p;
// Check for new style static tag ":...<Tab>file:[<Tab>...]"
p = tagp->command;
- while ((p = (char_u *)vim_strchr((char *)p, '\t')) != NULL) {
+ while ((p = vim_strchr(p, '\t')) != NULL) {
p++;
- if (STRNCMP(p, "file:", 5) == 0) {
- return TRUE;
+ if (strncmp(p, "file:", 5) == 0) {
+ return true;
}
}
- return FALSE;
+ return false;
}
-// Returns the length of a matching tag line.
-static size_t matching_line_len(const char_u *const lbuf)
+/// @return the length of a matching tag line.
+static size_t matching_line_len(const char *const lbuf)
{
- const char_u *p = lbuf + 1;
+ const char *p = lbuf + 1;
// does the same thing as parse_match()
- p += STRLEN(p) + 1;
- return (size_t)(p - lbuf) + STRLEN(p);
+ p += strlen(p) + 1;
+ return (size_t)(p - lbuf) + strlen(p);
}
/// Parse a line from a matching tag. Does not change the line itself.
@@ -2550,18 +2715,17 @@ static size_t matching_line_len(const char_u *const lbuf)
/// @param tagp output: pointers into the line
///
/// @return OK or FAIL.
-static int parse_match(char_u *lbuf, tagptrs_T *tagp)
+static int parse_match(char *lbuf, tagptrs_T *tagp)
{
int retval;
- char_u *p;
- char_u *pc, *pt;
+ char *p;
+ char *pc, *pt;
tagp->tag_fname = lbuf + 1;
- lbuf += STRLEN(tagp->tag_fname) + 2;
+ lbuf += strlen(tagp->tag_fname) + 2;
// Find search pattern and the file name for non-etags.
- retval = parse_tag_line(lbuf,
- tagp);
+ retval = parse_tag_line(lbuf, tagp);
tagp->tagkind = NULL;
tagp->user_data = NULL;
@@ -2580,20 +2744,20 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
if (*p++ == TAB) {
// Accept ASCII alphabetic kind characters and any multi-byte
// character.
- while (ASCII_ISALPHA(*p) || utfc_ptr2len((char *)p) > 1) {
- if (STRNCMP(p, "kind:", 5) == 0) {
+ while (ASCII_ISALPHA(*p) || utfc_ptr2len(p) > 1) {
+ if (strncmp(p, "kind:", 5) == 0) {
tagp->tagkind = p + 5;
- } else if (STRNCMP(p, "user_data:", 10) == 0) {
+ } else if (strncmp(p, "user_data:", 10) == 0) {
tagp->user_data = p + 10;
- } else if (STRNCMP(p, "line:", 5) == 0) {
- tagp->tagline = atoi((char *)p + 5);
+ } else if (strncmp(p, "line:", 5) == 0) {
+ tagp->tagline = atoi(p + 5);
}
if (tagp->tagkind != NULL && tagp->user_data != NULL) {
break;
}
- pc = (char_u *)vim_strchr((char *)p, ':');
- pt = (char_u *)vim_strchr((char *)p, '\t');
+ pc = vim_strchr(p, ':');
+ pt = vim_strchr(p, '\t');
if (pc == NULL || (pt != NULL && pc > pt)) {
tagp->tagkind = p;
}
@@ -2621,17 +2785,15 @@ static int parse_match(char_u *lbuf, tagptrs_T *tagp)
return retval;
}
-/*
- * Find out the actual file name of a tag. Concatenate the tags file name
- * with the matching tag file name.
- * Returns an allocated string.
- */
-static char_u *tag_full_fname(tagptrs_T *tagp)
+// Find out the actual file name of a tag. Concatenate the tags file name
+// with the matching tag file name.
+// Returns an allocated string.
+static char *tag_full_fname(tagptrs_T *tagp)
{
- int c = *tagp->fname_end;
+ char c = *tagp->fname_end;
*tagp->fname_end = NUL;
- char_u *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
- *tagp->fname_end = (char_u)c;
+ char *fullname = expand_tag_fname(tagp->fname, tagp->tag_fname, false);
+ *tagp->fname_end = c;
return fullname;
}
@@ -2640,30 +2802,29 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
///
/// @param lbuf_arg line from the tags file for this tag
/// @param forceit :ta with !
-/// @param keep_help keep help flag (FALSE for cscope)
+/// @param keep_help keep help flag
///
/// @return OK for success, NOTAGFILE when file not found, FAIL otherwise.
-static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
+static int jumpto_tag(const char *lbuf_arg, int forceit, int keep_help)
{
- int save_magic;
bool save_p_ws;
int save_p_scs, save_p_ic;
linenr_T save_lnum;
- char_u *str;
- char_u *pbuf; // search pattern buffer
- char_u *pbuf_end;
- char_u *tofree_fname = NULL;
- char_u *fname;
+ char *str;
+ char *pbuf; // search pattern buffer
+ char *pbuf_end;
+ char *tofree_fname = NULL;
+ char *fname;
tagptrs_T tagp;
int retval = FAIL;
int getfile_result = GETFILE_UNUSED;
int search_options;
win_T *curwin_save = NULL;
- char_u *full_fname = NULL;
+ char *full_fname = NULL;
const bool old_KeyTyped = KeyTyped; // getting the file may reset it
const int l_g_do_tagpreview = g_do_tagpreview;
const size_t len = matching_line_len(lbuf_arg) + 1;
- char_u *lbuf = xmalloc(len);
+ char *lbuf = xmalloc(len);
memmove(lbuf, lbuf_arg, len);
pbuf = xmalloc(LSIZE);
@@ -2689,9 +2850,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
*pbuf_end = NUL;
{
- /*
- * Remove the "<Tab>fieldname:value" stuff; we don't need it here.
- */
+ // Remove the "<Tab>fieldname:value" stuff; we don't need it here.
str = pbuf;
if (find_extra(&str) == OK) {
pbuf_end = str;
@@ -2699,24 +2858,19 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
}
}
- /*
- * Expand file name, when needed (for environment variables).
- * If 'tagrelative' option set, may change file name.
- */
+ // Expand file name, when needed (for environment variables).
+ // If 'tagrelative' option set, may change file name.
fname = expand_tag_fname(fname, tagp.tag_fname, true);
tofree_fname = fname; // free() it later
- /*
- * Check if the file with the tag exists before abandoning the current
- * file. Also accept a file name for which there is a matching BufReadCmd
- * autocommand event (e.g., http://sys/file).
- */
+ // Check if the file with the tag exists before abandoning the current
+ // file. Also accept a file name for which there is a matching BufReadCmd
+ // autocommand event (e.g., http://sys/file).
if (!os_path_exists(fname)
- && !has_autocmd(EVENT_BUFREADCMD, (char *)fname,
- NULL)) {
+ && !has_autocmd(EVENT_BUFREADCMD, fname, NULL)) {
retval = NOTAGFILE;
xfree(nofile_fname);
- nofile_fname = vim_strsave(fname);
+ nofile_fname = xstrdup(fname);
goto erret;
}
@@ -2726,19 +2880,15 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
postponed_split = 0; // don't split again below
curwin_save = curwin; // Save current window
- /*
- * If we are reusing a window, we may change dir when
- * entering it (autocommands) so turn the tag filename
- * into a fullpath
- */
+ // If we are reusing a window, we may change dir when
+ // entering it (autocommands) so turn the tag filename
+ // into a fullpath
if (!curwin->w_p_pvw) {
- full_fname = (char_u *)FullName_save((char *)fname, FALSE);
+ full_fname = FullName_save(fname, false);
fname = full_fname;
- /*
- * Make the preview window the current window.
- * Open a preview window when needed.
- */
+ // Make the preview window the current window.
+ // Open a preview window when needed.
prepare_tagpreview(true);
}
}
@@ -2746,7 +2896,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
// If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
// open a new tab page.
if (postponed_split && (swb_flags & (SWB_USEOPEN | SWB_USETAB))) {
- buf_T *const existing_buf = buflist_findname_exp((char *)fname);
+ buf_T *const existing_buf = buflist_findname_exp(fname);
if (existing_buf != NULL) {
const win_T *wp = NULL;
@@ -2791,7 +2941,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
if (getfile_result == GETFILE_UNUSED) {
// Careful: getfile() may trigger autocommands and call jumpto_tag()
// recursively.
- getfile_result = getfile(0, (char *)fname, NULL, true, (linenr_T)0, forceit);
+ getfile_result = getfile(0, fname, NULL, true, (linenr_T)0, forceit);
}
keep_help_flag = false;
@@ -2799,41 +2949,37 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
curwin->w_set_curswant = true;
postponed_split = 0;
- save_magic = p_magic;
- p_magic = false; // always execute with 'nomagic'
+ const optmagic_T save_magic_overruled = magic_overruled;
+ magic_overruled = OPTION_MAGIC_OFF; // always execute with 'nomagic'
// Save value of no_hlsearch, jumping to a tag is not a real search
const bool save_no_hlsearch = no_hlsearch;
- /*
- * If 'cpoptions' contains 't', store the search pattern for the "n"
- * command. If 'cpoptions' does not contain 't', the search pattern
- * is not stored.
- */
+ // If 'cpoptions' contains 't', store the search pattern for the "n"
+ // command. If 'cpoptions' does not contain 't', the search pattern
+ // is not stored.
if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) {
search_options = 0;
} else {
search_options = SEARCH_KEEP;
}
- /*
- * If the command is a search, try here.
- *
- * Reset 'smartcase' for the search, since the search pattern was not
- * typed by the user.
- * Only use do_search() when there is a full search command, without
- * anything following.
- */
+ // If the command is a search, try here.
+ //
+ // Reset 'smartcase' for the search, since the search pattern was not
+ // typed by the user.
+ // Only use do_search() when there is a full search command, without
+ // anything following.
str = pbuf;
if (pbuf[0] == '/' || pbuf[0] == '?') {
- str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1;
+ str = skip_regexp(pbuf + 1, pbuf[0], false) + 1;
}
if (str > pbuf_end - 1) { // search command with nothing following
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
p_ws = true; // need 'wrapscan' for backward searches
- p_ic = FALSE; // don't ignore case now
- p_scs = FALSE;
+ p_ic = false; // don't ignore case now
+ p_scs = false;
save_lnum = curwin->w_cursor.lnum;
if (tagp.tagline > 0) {
// start search before line from "line:" field
@@ -2847,11 +2993,9 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
retval = OK;
} else {
int found = 1;
- int cc;
+ char cc;
- /*
- * try again, ignore case now
- */
+ // try again, ignore case now
p_ic = true;
if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, (long)1,
search_options, NULL)) {
@@ -2860,26 +3004,23 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
(void)test_for_static(&tagp);
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
+ snprintf(pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
if (!do_search(NULL, '/', '/', pbuf, (long)1, search_options, NULL)) {
// Guess again: "^char * \<func ("
- snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
+ snprintf(pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
tagp.tagname);
- if (!do_search(NULL, '/', '/', pbuf, (long)1,
- search_options, NULL)) {
+ if (!do_search(NULL, '/', '/', pbuf, (long)1, search_options, NULL)) {
found = 0;
}
}
- *tagp.tagname_end = (char_u)cc;
+ *tagp.tagname_end = cc;
}
if (found == 0) {
emsg(_("E434: Can't find tag pattern"));
curwin->w_cursor.lnum = save_lnum;
} else {
- /*
- * Only give a message when really guessed, not when 'ic'
- * is set and match found while ignoring case.
- */
+ // Only give a message when really guessed, not when 'ic'
+ // is set and match found while ignoring case.
if (found == 2 || !save_p_ic) {
msg(_("E435: Couldn't find tag, just guessing!"));
if (!msg_scrolled && msg_silent == 0) {
@@ -2904,7 +3045,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
secure = 1;
sandbox++;
curwin->w_cursor.lnum = 1; // start command in line 1
- do_cmdline_cmd((char *)pbuf);
+ do_cmdline_cmd(pbuf);
retval = OK;
// When the command has done something that is not allowed make sure
@@ -2916,7 +3057,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
sandbox--;
}
- p_magic = save_magic;
+ magic_overruled = save_magic_overruled;
// restore no_hlsearch when keeping the old search pattern
if (search_options) {
set_no_hlsearch(save_no_hlsearch);
@@ -2928,10 +3069,8 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
}
if (retval == OK) {
- /*
- * For a help buffer: Put the cursor line at the top of the window,
- * the help subject will be below it.
- */
+ // For a help buffer: Put the cursor line at the top of the window,
+ // the help subject will be below it.
if (curbuf->b_help) {
set_topline(curwin, curwin->w_cursor.lnum);
}
@@ -2944,7 +3083,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
&& curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
@@ -2967,19 +3106,18 @@ erret:
return retval;
}
-// If "expand" is true, expand wildcards in fname.
-// If 'tagrelative' option set, change fname (name of file containing tag)
-// according to tag_fname (name of tag file containing fname).
-// Returns a pointer to allocated memory.
-static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bool expand)
+/// If "expand" is true, expand wildcards in fname.
+/// If 'tagrelative' option set, change fname (name of file containing tag)
+/// according to tag_fname (name of tag file containing fname).
+///
+/// @return a pointer to allocated memory.
+static char *expand_tag_fname(char *fname, char *const tag_fname, const bool expand)
{
- char_u *p;
- char_u *expanded_fname = NULL;
+ char *p;
+ char *expanded_fname = NULL;
expand_T xpc;
- /*
- * Expand file name (for environment variables) when needed.
- */
+ // Expand file name (for environment variables) when needed.
if (expand && path_has_wildcard(fname)) {
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
@@ -2990,20 +3128,17 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
}
}
- char_u *retval;
+ char *retval;
if ((p_tr || curbuf->b_help)
&& !vim_isAbsName(fname)
- && (p = (char_u *)path_tail((char *)tag_fname)) != tag_fname) {
+ && (p = path_tail(tag_fname)) != tag_fname) {
retval = xmalloc(MAXPATHL);
STRCPY(retval, tag_fname);
- STRLCPY(retval + (p - tag_fname), fname,
- MAXPATHL - (p - tag_fname));
- /*
- * Translate names like "src/a/../b/file.c" into "src/b/file.c".
- */
+ xstrlcpy(retval + (p - tag_fname), fname, (size_t)(MAXPATHL - (p - tag_fname)));
+ // Translate names like "src/a/../b/file.c" into "src/b/file.c".
simplify_filename(retval);
} else {
- retval = vim_strsave(fname);
+ retval = xstrdup(fname);
}
xfree(expanded_fname);
@@ -3011,47 +3146,43 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
return retval;
}
-/*
- * Check if we have a tag for the buffer with name "buf_ffname".
- * This is a bit slow, because of the full path compare in path_full_compare().
- * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
- * file.
- */
-static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname, char_u *buf_ffname)
+/// Check if we have a tag for the buffer with name "buf_ffname".
+/// This is a bit slow, because of the full path compare in path_full_compare().
+///
+/// @return true if tag for file "fname" if tag file "tag_fname" is for current
+/// file.
+static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname)
{
- int c;
- int retval = FALSE;
- char_u *fullname;
+ char c;
+ int retval = false;
if (buf_ffname != NULL) { // if the buffer has a name
{
c = *fname_end;
*fname_end = NUL;
}
- fullname = expand_tag_fname(fname, tag_fname, true);
- retval = (path_full_compare((char *)fullname, (char *)buf_ffname, true, true) & kEqualFiles);
+ char *fullname = expand_tag_fname(fname, tag_fname, true);
+ retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles);
xfree(fullname);
- *fname_end = (char_u)c;
+ *fname_end = c;
}
return retval;
}
-/*
- * Find the end of the tagaddress.
- * Return OK if ";\"" is following, FAIL otherwise.
- */
-static int find_extra(char_u **pp)
+// Find the end of the tagaddress.
+// Return OK if ";\"" is following, FAIL otherwise.
+static int find_extra(char **pp)
{
- char_u *str = *pp;
- char_u first_char = **pp;
+ char *str = *pp;
+ char first_char = **pp;
// Repeat for addresses separated with ';'
for (;;) {
if (ascii_isdigit(*str)) {
- str = (char_u *)skipdigits((char *)str + 1);
+ str = skipdigits(str + 1);
} else if (*str == '/' || *str == '?') {
- str = skip_regexp(str + 1, *str, false, NULL);
+ str = skip_regexp(str + 1, *str, false);
if (*str != first_char) {
str = NULL;
} else {
@@ -3059,7 +3190,7 @@ static int find_extra(char_u **pp)
}
} else {
// not a line number or search string, look for terminator.
- str = (char_u *)strstr((char *)str, "|;\"");
+ str = strstr(str, "|;\"");
if (str != NULL) {
str++;
break;
@@ -3073,7 +3204,7 @@ static int find_extra(char_u **pp)
first_char = *str;
}
- if (str != NULL && STRNCMP(str, ";\"", 2) == 0) {
+ if (str != NULL && strncmp(str, ";\"", 2) == 0) {
*pp = str;
return OK;
}
@@ -3090,11 +3221,11 @@ static void tagstack_clear_entry(taggy_T *item)
}
/// @param tagnames expand tag names
-int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
+int expand_tags(int tagnames, char *pat, int *num_file, char ***file)
{
int i;
int extra_flag;
- char_u *name_buf;
+ char *name_buf;
size_t name_buf_size = 100;
tagptrs_T t_p;
int ret;
@@ -3109,11 +3240,11 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
if (pat[0] == '/') {
ret = find_tags(pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
} else {
ret = find_tags(pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
}
if (ret == OK && !tagnames) {
// Reorganize the tags for display and matching as strings of:
@@ -3121,10 +3252,10 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
for (i = 0; i < *num_file; i++) {
size_t len;
- parse_match((char_u *)(*file)[i], &t_p);
+ parse_match((*file)[i], &t_p);
len = (size_t)(t_p.tagname_end - t_p.tagname);
if (len > name_buf_size - 3) {
- char_u *buf;
+ char *buf;
name_buf_size = len + 3;
buf = xrealloc(name_buf, name_buf_size);
@@ -3150,8 +3281,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
///
/// @param start start of the value
/// @param end after the value; can be NULL
-static int add_tag_field(dict_T *dict, const char *field_name, const char_u *start,
- const char_u *end)
+static int add_tag_field(dict_T *dict, const char *field_name, const char *start, const char *end)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
int len = 0;
@@ -3166,10 +3296,10 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
}
return FAIL;
}
- char_u *buf = xmalloc(MAXPATHL);
+ char *buf = xmalloc(MAXPATHL);
if (start != NULL) {
if (end == NULL) {
- end = start + STRLEN(start);
+ end = start + strlen(start);
while (end > start && (end[-1] == '\r' || end[-1] == '\n')) {
end--;
}
@@ -3178,22 +3308,21 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
if (len > MAXPATHL - 1) {
len = MAXPATHL - 1;
}
- STRLCPY(buf, start, len + 1);
+ xstrlcpy(buf, start, (size_t)len + 1);
}
buf[len] = NUL;
- retval = tv_dict_add_str(dict, field_name, STRLEN(field_name),
- (const char *)buf);
+ retval = tv_dict_add_str(dict, field_name, strlen(field_name), buf);
xfree(buf);
return retval;
}
/// Add the tags matching the specified pattern "pat" to the list "list"
/// as a dictionary. Use "buf_fname" for priority, unless NULL.
-int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
+int get_tags(list_T *list, char *pat, char *buf_fname)
{
int num_matches, i, ret;
char **matches;
- char_u *full_fname;
+ char *full_fname;
dict_T *dict;
tagptrs_T tp;
bool is_static;
@@ -3202,7 +3331,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname);
if (ret == OK && num_matches > 0) {
for (i = 0; i < num_matches; i++) {
- int parse_result = parse_match((char_u *)matches[i], &tp);
+ int parse_result = parse_match(matches[i], &tp);
// Avoid an unused variable warning in release builds.
(void)parse_result;
@@ -3211,7 +3340,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
is_static = test_for_static(&tp);
// Skip pseudo-tag lines.
- if (STRNCMP(tp.tagname, "!_TAG_", 6) == 0) {
+ if (strncmp(tp.tagname, "!_TAG_", 6) == 0) {
xfree(matches[i]);
continue;
}
@@ -3232,18 +3361,18 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
xfree(full_fname);
if (tp.command_end != NULL) {
- for (char_u *p = tp.command_end + 3;
+ for (char *p = tp.command_end + 3;
*p != NUL && *p != '\n' && *p != '\r';
MB_PTR_ADV(p)) {
if (p == tp.tagkind
- || (p + 5 == tp.tagkind && STRNCMP(p, "kind:", 5) == 0)) {
+ || (p + 5 == tp.tagkind && strncmp(p, "kind:", 5) == 0)) {
// skip "kind:<kind>" and "<kind>"
p = tp.tagkind_end - 1;
- } else if (STRNCMP(p, "file:", 5) == 0) {
+ } else if (strncmp(p, "file:", 5) == 0) {
// skip "file:" (static tag)
p += 4;
} else if (!ascii_iswhite(*p)) {
- char_u *s, *n;
+ char *s, *n;
int len;
// Add extra field as a dict entry. Fields are
@@ -3255,17 +3384,17 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
len = (int)(p - n);
if (*p == ':' && len > 0) {
s = ++p;
- while (*p != NUL && *p >= ' ') {
+ while (*p != NUL && (uint8_t)(*p) >= ' ') {
p++;
}
n[len] = NUL;
- if (add_tag_field(dict, (char *)n, s, p) == FAIL) {
+ if (add_tag_field(dict, n, s, p) == FAIL) {
ret = FAIL;
}
n[len] = ':';
} else {
// Skip field without colon.
- while (*p != NUL && *p >= ' ') {
+ while (*p != NUL && (uint8_t)(*p) >= ' ') {
p++;
}
}
@@ -3351,9 +3480,9 @@ static void tagstack_shift(win_T *wp)
wp->w_tagstacklen--;
}
-// Push a new item to the tag stack
-static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur_match, pos_T mark,
- int fnum, char_u *user_data)
+/// Push a new item to the tag stack
+static void tagstack_push_item(win_T *wp, char *tagname, int cur_fnum, int cur_match, pos_T mark,
+ int fnum, char *user_data)
{
taggy_T *tagstack = wp->w_tagstack;
int idx = wp->w_tagstacklen; // top of the stack
@@ -3376,13 +3505,13 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
tagstack[idx].user_data = user_data;
}
-// Add a list of items to the tag stack in the specified window
+/// Add a list of items to the tag stack in the specified window
static void tagstack_push_items(win_T *wp, list_T *l)
{
listitem_T *li;
dictitem_T *di;
dict_T *itemdict;
- char_u *tagname;
+ char *tagname;
pos_T mark;
int fnum;
@@ -3401,8 +3530,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
if (list2fpos(&di->di_tv, &mark, &fnum, NULL, false) != OK) {
continue;
}
- if ((tagname = (char_u *)tv_dict_get_string(itemdict, "tagname", true))
- == NULL) {
+ if ((tagname = tv_dict_get_string(itemdict, "tagname", true)) == NULL) {
continue;
}
@@ -3414,7 +3542,7 @@ static void tagstack_push_items(win_T *wp, list_T *l)
(int)tv_dict_get_number(itemdict, "bufnr"),
(int)tv_dict_get_number(itemdict, "matchnr") - 1,
mark, fnum,
- (char_u *)tv_dict_get_string(itemdict, "user_data", true));
+ tv_dict_get_string(itemdict, "user_data", true));
}
}
@@ -3459,10 +3587,12 @@ int set_tagstack(win_T *wp, const dict_T *d, int action)
if ((di = tv_dict_find(d, "curidx", -1)) != NULL) {
tagstack_set_curidx(wp, (int)tv_get_number(&di->di_tv) - 1);
}
+
if (action == 't') { // truncate the stack
taggy_T *const tagstack = wp->w_tagstack;
const int tagstackidx = wp->w_tagstackidx;
int tagstacklen = wp->w_tagstacklen;
+
// delete all the tag stack entries above the current entry
while (tagstackidx < tagstacklen) {
tagstack_clear_entry(&tagstack[--tagstacklen]);