summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-03-31 19:40:07 +0200
committerBram Moolenaar <Bram@vim.org>2019-03-31 19:40:07 +0200
commitb4a6020ac6a0638167013f1e45ff440ddc8a1671 (patch)
treec862765096dc61d14e1e8a4c7825e30189c9fe8a
parent95946f1209ad088bfe55c83256c299156c11d8e0 (diff)
patch 8.1.1099: the do_tag() function is too longv8.1.1099
Problem: The do_tag() function is too long. Solution: Factor parts out to separate functions. Move simplify_filename() to a file where it fits better. (Andy Massimino, closes #4195)
-rw-r--r--src/findfile.c212
-rw-r--r--src/proto/findfile.pro1
-rw-r--r--src/proto/tag.pro1
-rw-r--r--src/tag.c908
-rw-r--r--src/version.c2
5 files changed, 574 insertions, 550 deletions
diff --git a/src/findfile.c b/src/findfile.c
index 690cd8fb19..3d3aec0df9 100644
--- a/src/findfile.c
+++ b/src/findfile.c
@@ -2605,3 +2605,215 @@ expand_in_path(
}
#endif // FEAT_SEARCHPATH
+
+/*
+ * Converts a file name into a canonical form. It simplifies a file name into
+ * its simplest form by stripping out unneeded components, if any. The
+ * resulting file name is simplified in place and will either be the same
+ * length as that supplied, or shorter.
+ */
+ void
+simplify_filename(char_u *filename)
+{
+#ifndef AMIGA // Amiga doesn't have "..", it uses "/"
+ int components = 0;
+ char_u *p, *tail, *start;
+ int stripping_disabled = FALSE;
+ int relative = TRUE;
+
+ p = filename;
+# ifdef BACKSLASH_IN_FILENAME
+ if (p[1] == ':') // skip "x:"
+ p += 2;
+# endif
+
+ if (vim_ispathsep(*p))
+ {
+ relative = FALSE;
+ do
+ ++p;
+ while (vim_ispathsep(*p));
+ }
+ start = p; // remember start after "c:/" or "/" or "///"
+
+ do
+ {
+ // At this point "p" is pointing to the char following a single "/"
+ // or "p" is at the "start" of the (absolute or relative) path name.
+# ifdef VMS
+ // VMS allows device:[path] - don't strip the [ in directory
+ if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
+ {
+ // :[ or :< composition: vms directory component
+ ++components;
+ p = getnextcomp(p + 1);
+ }
+ // allow remote calls as host"user passwd"::device:[path]
+ else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
+ {
+ // ":: composition: vms host/passwd component
+ ++components;
+ p = getnextcomp(p + 2);
+ }
+ else
+# endif
+ if (vim_ispathsep(*p))
+ STRMOVE(p, p + 1); // remove duplicate "/"
+ else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
+ {
+ if (p == start && relative)
+ p += 1 + (p[1] != NUL); // keep single "." or leading "./"
+ else
+ {
+ // Strip "./" or ".///". If we are at the end of the file name
+ // and there is no trailing path separator, either strip "/." if
+ // we are after "start", or strip "." if we are at the beginning
+ // of an absolute path name .
+ tail = p + 1;
+ if (p[1] != NUL)
+ while (vim_ispathsep(*tail))
+ MB_PTR_ADV(tail);
+ else if (p > start)
+ --p; // strip preceding path separator
+ STRMOVE(p, tail);
+ }
+ }
+ else if (p[0] == '.' && p[1] == '.' &&
+ (vim_ispathsep(p[2]) || p[2] == NUL))
+ {
+ // Skip to after ".." or "../" or "..///".
+ tail = p + 2;
+ while (vim_ispathsep(*tail))
+ MB_PTR_ADV(tail);
+
+ if (components > 0) // strip one preceding component
+ {
+ int do_strip = FALSE;
+ char_u saved_char;
+ stat_T st;
+
+ /* Don't strip for an erroneous file name. */
+ if (!stripping_disabled)
+ {
+ // If the preceding component does not exist in the file
+ // system, we strip it. On Unix, we don't accept a symbolic
+ // link that refers to a non-existent file.
+ saved_char = p[-1];
+ p[-1] = NUL;
+# ifdef UNIX
+ if (mch_lstat((char *)filename, &st) < 0)
+# else
+ if (mch_stat((char *)filename, &st) < 0)
+# endif
+ do_strip = TRUE;
+ p[-1] = saved_char;
+
+ --p;
+ // Skip back to after previous '/'.
+ while (p > start && !after_pathsep(start, p))
+ MB_PTR_BACK(start, p);
+
+ if (!do_strip)
+ {
+ // If the component exists in the file system, check
+ // that stripping it won't change the meaning of the
+ // file name. First get information about the
+ // unstripped file name. This may fail if the component
+ // to strip is not a searchable directory (but a regular
+ // file, for instance), since the trailing "/.." cannot
+ // be applied then. We don't strip it then since we
+ // don't want to replace an erroneous file name by
+ // a valid one, and we disable stripping of later
+ // components.
+ saved_char = *tail;
+ *tail = NUL;
+ if (mch_stat((char *)filename, &st) >= 0)
+ do_strip = TRUE;
+ else
+ stripping_disabled = TRUE;
+ *tail = saved_char;
+# ifdef UNIX
+ if (do_strip)
+ {
+ stat_T new_st;
+
+ // On Unix, the check for the unstripped file name
+ // above works also for a symbolic link pointing to
+ // a searchable directory. But then the parent of
+ // the directory pointed to by the link must be the
+ // same as the stripped file name. (The latter
+ // exists in the file system since it is the
+ // component's parent directory.)
+ if (p == start && relative)
+ (void)mch_stat(".", &new_st);
+ else
+ {
+ saved_char = *p;
+ *p = NUL;
+ (void)mch_stat((char *)filename, &new_st);
+ *p = saved_char;
+ }
+
+ if (new_st.st_ino != st.st_ino ||
+ new_st.st_dev != st.st_dev)
+ {
+ do_strip = FALSE;
+ // We don't disable stripping of later
+ // components since the unstripped path name is
+ // still valid.
+ }
+ }
+# endif
+ }
+ }
+
+ if (!do_strip)
+ {
+ // Skip the ".." or "../" and reset the counter for the
+ // components that might be stripped later on.
+ p = tail;
+ components = 0;
+ }
+ else
+ {
+ // Strip previous component. If the result would get empty
+ // and there is no trailing path separator, leave a single
+ // "." instead. If we are at the end of the file name and
+ // there is no trailing path separator and a preceding
+ // component is left after stripping, strip its trailing
+ // path separator as well.
+ if (p == start && relative && tail[-1] == '.')
+ {
+ *p++ = '.';
+ *p = NUL;
+ }
+ else
+ {
+ if (p > start && tail[-1] == '.')
+ --p;
+ STRMOVE(p, tail); // strip previous component
+ }
+
+ --components;
+ }
+ }
+ else if (p == start && !relative) // leading "/.." or "/../"
+ STRMOVE(p, tail); // strip ".." or "../"
+ else
+ {
+ if (p == start + 2 && p[-2] == '.') // leading "./../"
+ {
+ STRMOVE(p - 2, p); // strip leading "./"
+ tail -= 2;
+ }
+ p = tail; // skip to char after ".." or "../"
+ }
+ }
+ else
+ {
+ ++components; // simple path component
+ p = getnextcomp(p);
+ }
+ } while (*p != NUL);
+#endif // !AMIGA
+}
diff --git a/src/proto/findfile.pro b/src/proto/findfile.pro
index 5b80b83c69..ebab792733 100644
--- a/src/proto/findfile.pro
+++ b/src/proto/findfile.pro
@@ -15,4 +15,5 @@ char_u *find_file_name_in_path(char_u *ptr, int len, int options, long count, ch
int vim_ispathlistsep(int c);
void uniquefy_paths(garray_T *gap, char_u *pattern);
int expand_in_path(garray_T *gap, char_u *pattern, int flags);
+void simplify_filename(char_u *filename);
/* vim: set ft=c : */
diff --git a/src/proto/tag.pro b/src/proto/tag.pro
index c9bcb384ee..3046406a3f 100644
--- a/src/proto/tag.pro
+++ b/src/proto/tag.pro
@@ -6,7 +6,6 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
void free_tag_stuff(void);
int get_tagfname(tagname_T *tnp, int first, char_u *buf);
void tagname_free(tagname_T *tnp);
-void simplify_filename(char_u *filename);
int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file);
int get_tags(list_T *list, char_u *pat, char_u *buf_fname);
void get_tagstack(win_T *wp, dict_T *retdict);
diff --git a/src/tag.c b/src/tag.c
index 0f4a70d975..10039b7873 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -74,6 +74,10 @@ static int test_for_current(int, char_u *, char_u *, char_u *, char_u *);
static int test_for_current(char_u *, char_u *, char_u *, char_u *);
#endif
static int find_extra(char_u **pp);
+static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches);
+#if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
+static int add_llist_tags(char_u *tag, int num_matches, char_u **matches);
+#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");
@@ -125,25 +129,17 @@ do_tag(
int prevtagstackidx = tagstackidx;
int prev_num_matches;
int new_tag = FALSE;
- int other_name;
- int i, j, k;
- int idx;
+ int i;
int ic;
- char_u *p;
- char_u *name;
int no_regexp = FALSE;
int error_cur_match = 0;
- char_u *command_end;
int save_pos = FALSE;
fmark_T saved_fmark;
- int taglen;
#ifdef FEAT_CSCOPE
int jumped_to_tag = FALSE;
#endif
- tagptrs_T tagp, tagp2;
int new_num_matches;
char_u **new_matches;
- int attr;
int use_tagstack;
int skip_msg = FALSE;
char_u *buf_ffname = curbuf->b_ffname; /* name to use for
@@ -482,6 +478,9 @@ do_tag(
*/
for (;;)
{
+ int other_name;
+ char_u *name;
+
/*
* When desired match not found yet, try to find it (and others).
*/
@@ -541,9 +540,12 @@ do_tag(
* ":tnext" and jumping to another file. */
if (!new_tag && !other_name)
{
+ int j, k;
+ int idx = 0;
+ tagptrs_T tagp, tagp2;
+
/* Find the position of each old match in the new list. Need
* to use parse_match() to find the tag line. */
- idx = 0;
for (j = 0; j < num_matches; ++j)
{
parse_match(matches[j], &tagp);
@@ -552,7 +554,7 @@ do_tag(
parse_match(new_matches[i], &tagp2);
if (STRCMP(tagp.tagname, tagp2.tagname) == 0)
{
- p = new_matches[i];
+ char_u *p = new_matches[i];
for (k = i; k > idx; --k)
new_matches[k] = new_matches[k - 1];
new_matches[idx++] = p;
@@ -587,341 +589,19 @@ do_tag(
else
#endif
if (type == DT_TAG && *tag != NUL)
- /*
- * If a count is supplied to the ":tag <name>" command, then
- * jump to count'th matching tag.
- */
+ // If a count is supplied to the ":tag <name>" command, then
+ // jump to count'th matching tag.
cur_match = count > 0 ? count - 1 : 0;
else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1))
{
- /*
- * List all the matching tags.
- * Assume that the first match indicates how long the tags can
- * be, and align the file names to that.
- */
- parse_match(matches[0], &tagp);
- taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
- if (taglen < 18)
- taglen = 18;
- if (taglen > Columns - 25)
- taglen = MAXCOL;
- if (msg_col == 0)
- msg_didout = FALSE; /* overwrite previous message */
- msg_start();
- msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T));
- msg_clr_eos();
- taglen_advance(taglen);
- msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
-
- for (i = 0; i < num_matches && !got_int; ++i)
- {
- parse_match(matches[i], &tagp);
- if (!new_tag && (
-#if defined(FEAT_QUICKFIX)
- (g_do_tagpreview != 0
- && i == ptag_entry.cur_match) ||
-#endif
- (use_tagstack
- && i == tagstack[tagstackidx].cur_match)))
- *IObuff = '>';
- else
- *IObuff = ' ';
- vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
- "%2d %s ", i + 1,
- mt_names[matches[i][0] & MT_MASK]);
- msg_puts((char *)IObuff);
- if (tagp.tagkind != NULL)
- msg_outtrans_len(tagp.tagkind,
- (int)(tagp.tagkind_end - tagp.tagkind));
- msg_advance(13);
- msg_outtrans_len_attr(tagp.tagname,
- (int)(tagp.tagname_end - tagp.tagname),
- HL_ATTR(HLF_T));
- msg_putchar(' ');
- taglen_advance(taglen);
-
- /* Find out the actual file name. If it is long, truncate
- * it and put "..." in the middle */
- p = tag_full_fname(&tagp);
- if (p != NULL)
- {
- msg_outtrans_long_attr(p, HL_ATTR(HLF_D));
- vim_free(p);
- }
- if (msg_col > 0)
- msg_putchar('\n');
- if (got_int)
- break;
- msg_advance(15);
-
- /* print any extra fields */
- command_end = tagp.command_end;
- if (command_end != NULL)
- {
- p = command_end + 3;
- while (*p && *p != '\r' && *p != '\n')
- {
- while (*p == TAB)
- ++p;
-
- /* skip "file:" without a value (static tag) */
- if (STRNCMP(p, "file:", 5) == 0
- && vim_isspace(p[5]))
- {
- p += 5;
- continue;
- }
- /* skip "kind:<kind>" and "<kind>" */
- if (p == tagp.tagkind
- || (p + 5 == tagp.tagkind
- && 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(p) >= Columns)
- {
- msg_putchar('\n');
- if (got_int)
- break;
- msg_advance(15);
- }
- p = msg_outtrans_one(p, attr);
- if (*p == TAB)
- {
- msg_puts_attr(" ", attr);
- break;
- }
- if (*p == ':')
- attr = 0;
- }
- }
- if (msg_col > 15)
- {
- msg_putchar('\n');
- if (got_int)
- break;
- msg_advance(15);
- }
- }
- else
- {
- for (p = tagp.command;
- *p && *p != '\r' && *p != '\n'; ++p)
- ;
- command_end = p;
- }
-
- /*
- * Put the info (in several lines) at column 15.
- * Don't display "/^" and "?^".
- */
- p = tagp.command;
- if (*p == '/' || *p == '?')
- {
- ++p;
- if (*p == '^')
- ++p;
- }
- /* Remove leading whitespace from pattern */
- while (p != command_end && vim_isspace(*p))
- ++p;
-
- while (p != command_end)
- {
- if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns)
- msg_putchar('\n');
- if (got_int)
- break;
- msg_advance(15);
-
- /* skip backslash used for escaping a command char or
- * a backslash */
- if (*p == '\\' && (*(p + 1) == *tagp.command
- || *(p + 1) == '\\'))
- ++p;
-
- if (*p == TAB)
- {
- msg_putchar(' ');
- ++p;
- }
- else
- p = msg_outtrans_one(p, 0);
-
- /* don't display the "$/;\"" and "$?;\"" */
- if (p == command_end - 2 && *p == '$'
- && *(p + 1) == *tagp.command)
- break;
- /* don't display matching '/' or '?' */
- if (p == command_end - 1 && *p == *tagp.command
- && (*p == '/' || *p == '?'))
- break;
- }
- if (msg_col)
- msg_putchar('\n');
- ui_breakcheck();
- }
- if (got_int)
- got_int = FALSE; /* only stop the listing */
+ print_tag_list(new_tag, use_tagstack, num_matches, matches);
ask_for_selection = TRUE;
}
#if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
else if (type == DT_LTAG)
{
- list_T *list;
- char_u tag_name[128 + 1];
- char_u *fname;
- char_u *cmd;
-
- /*
- * Add the matching tags to the location list for the current
- * window.
- */
-
- fname = alloc(MAXPATHL + 1);
- cmd = alloc(CMDBUFFSIZE + 1);
- list = list_alloc();
- if (list == NULL || fname == NULL || cmd == NULL)
- {
- vim_free(cmd);
- vim_free(fname);
- if (list != NULL)
- list_free(list);
+ if (add_llist_tags(tag, num_matches, matches) == FAIL)
goto end_do_tag;
- }
-
- for (i = 0; i < num_matches; ++i)
- {
- int len, cmd_len;
- long lnum;
- dict_T *dict;
-
- parse_match(matches[i], &tagp);
-
- /* Save the tag name */
- len = (int)(tagp.tagname_end - tagp.tagname);
- if (len > 128)
- len = 128;
- vim_strncpy(tag_name, tagp.tagname, len);
- tag_name[len] = NUL;
-
- /* Save the tag file name */
- p = tag_full_fname(&tagp);
- if (p == NULL)
- continue;
- vim_strncpy(fname, p, MAXPATHL);
- vim_free(p);
-
- /*
- * Get the line number or the search pattern used to locate
- * the tag.
- */
- lnum = 0;
- if (isdigit(*tagp.command))
- /* Line number is used to locate the tag */
- lnum = atol((char *)tagp.command);
- else
- {
- char_u *cmd_start, *cmd_end;
-
- /* Search pattern is used to locate the tag */
-
- /* Locate the end of the command */
- cmd_start = tagp.command;
- cmd_end = tagp.command_end;
- if (cmd_end == NULL)
- {
- for (p = tagp.command;
- *p && *p != '\r' && *p != '\n'; ++p)
- ;
- cmd_end = p;
- }
-
- /*
- * Now, cmd_end points to the character after the
- * command. Adjust it to point to the last
- * character of the command.
- */
- cmd_end--;
-
- /*
- * Skip the '/' and '?' characters at the
- * beginning and end of the search pattern.
- */
- if (*cmd_start == '/' || *cmd_start == '?')
- cmd_start++;
-
- if (*cmd_end == '/' || *cmd_end == '?')
- cmd_end--;
-
- len = 0;
- cmd[0] = NUL;
-
- /*
- * If "^" is present in the tag search pattern, then
- * copy it first.
- */
- if (*cmd_start == '^')
- {
- STRCPY(cmd, "^");
- cmd_start++;
- len++;
- }
-
- /*
- * Precede the tag pattern with \V to make it very
- * nomagic.
- */
- STRCAT(cmd, "\\V");
- len += 2;
-
- cmd_len = (int)(cmd_end - cmd_start + 1);
- if (cmd_len > (CMDBUFFSIZE - 5))
- cmd_len = CMDBUFFSIZE - 5;
- STRNCAT(cmd, cmd_start, cmd_len);
- len += cmd_len;
-
- if (cmd[len - 1] == '$')
- {
- /*
- * Replace '$' at the end of the search pattern
- * with '\$'
- */
- cmd[len - 1] = '\\';
- cmd[len] = '$';
- len++;
- }
-
- cmd[len] = NUL;
- }
-
- if ((dict = dict_alloc()) == NULL)
- continue;
- if (list_append_dict(list, dict) == FAIL)
- {
- vim_free(dict);
- continue;
- }
-
- dict_add_string(dict, "text", tag_name);
- dict_add_string(dict, "filename", fname);
- dict_add_number(dict, "lnum", lnum);
- if (lnum == 0)
- dict_add_string(dict, "pattern", cmd);
- }
-
- vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
- set_errorlist(curwin, list, ' ', IObuff, NULL);
-
- list_free(list);
- vim_free(fname);
- vim_free(cmd);
-
cur_match = 0; /* Jump to the first tag */
}
#endif
@@ -1089,6 +769,348 @@ end_do_tag:
}
/*
+ * List all the matching tags.
+ */
+ static void
+print_tag_list(
+ int new_tag,
+ int use_tagstack,
+ int num_matches,
+ char_u **matches)
+{
+ taggy_T *tagstack = curwin->w_tagstack;
+ int tagstackidx = curwin->w_tagstackidx;
+ int i;
+ char_u *p;
+ char_u *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(matches[0], &tagp);
+ taglen = (int)(tagp.tagname_end - tagp.tagname + 2);
+ if (taglen < 18)
+ taglen = 18;
+ if (taglen > Columns - 25)
+ taglen = MAXCOL;
+ if (msg_col == 0)
+ msg_didout = FALSE; // overwrite previous message
+ msg_start();
+ msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T));
+ msg_clr_eos();
+ taglen_advance(taglen);
+ msg_puts_attr(_("file\n"), HL_ATTR(HLF_T));
+
+ for (i = 0; i < num_matches && !got_int; ++i)
+ {
+ parse_match(matches[i], &tagp);
+ if (!new_tag && (
+#if defined(FEAT_QUICKFIX)
+ (g_do_tagpreview != 0
+ && i == ptag_entry.cur_match) ||
+#endif
+ (use_tagstack
+ && i == tagstack[tagstackidx].cur_match)))
+ *IObuff = '>';
+ else
+ *IObuff = ' ';
+ vim_snprintf((char *)IObuff + 1, IOSIZE - 1,
+ "%2d %s ", i + 1,
+ mt_names[matches[i][0] & MT_MASK]);
+ msg_puts((char *)IObuff);
+ if (tagp.tagkind != NULL)
+ msg_outtrans_len(tagp.tagkind,
+ (int)(tagp.tagkind_end - tagp.tagkind));
+ msg_advance(13);
+ msg_outtrans_len_attr(tagp.tagname,
+ (int)(tagp.tagname_end - tagp.tagname),
+ HL_ATTR(HLF_T));
+ msg_putchar(' ');
+ taglen_advance(taglen);
+
+ // Find out the actual file name. If it is long, truncate
+ // it and put "..." in the middle
+ p = tag_full_fname(&tagp);
+ if (p != NULL)
+ {
+ msg_outtrans_long_attr(p, HL_ATTR(HLF_D));
+ vim_free(p);
+ }
+ if (msg_col > 0)
+ msg_putchar('\n');
+ if (got_int)
+ break;
+ msg_advance(15);
+
+ // print any extra fields
+ command_end = tagp.command_end;
+ if (command_end != NULL)
+ {
+ p = command_end + 3;
+ while (*p && *p != '\r' && *p != '\n')
+ {
+ while (*p == TAB)
+ ++p;
+
+ // skip "file:" without a value (static tag)
+ if (STRNCMP(p, "file:", 5) == 0
+ && vim_isspace(p[5]))
+ {
+ p += 5;
+ continue;
+ }
+ // skip "kind:<kind>" and "<kind>"
+ if (p == tagp.tagkind
+ || (p + 5 == tagp.tagkind
+ && 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(p) >= Columns)
+ {
+ msg_putchar('\n');
+ if (got_int)
+ break;
+ msg_advance(15);
+ }
+ p = msg_outtrans_one(p, attr);
+ if (*p == TAB)
+ {
+ msg_puts_attr(" ", attr);
+ break;
+ }
+ if (*p == ':')
+ attr = 0;
+ }
+ }
+ if (msg_col > 15)
+ {
+ msg_putchar('\n');
+ if (got_int)
+ break;
+ msg_advance(15);
+ }
+ }
+ else
+ {
+ for (p = tagp.command;
+ *p && *p != '\r' && *p != '\n'; ++p)
+ ;
+ command_end = p;
+ }
+
+ // Put the info (in several lines) at column 15.
+ // Don't display "/^" and "?^".
+ p = tagp.command;
+ if (*p == '/' || *p == '?')
+ {
+ ++p;
+ if (*p == '^')
+ ++p;
+ }
+ // Remove leading whitespace from pattern
+ while (p != command_end && vim_isspace(*p))
+ ++p;
+
+ while (p != command_end)
+ {
+ if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns)
+ msg_putchar('\n');
+ if (got_int)
+ break;
+ msg_advance(15);
+
+ // skip backslash used for escaping a command char or
+ // a backslash
+ if (*p == '\\' && (*(p + 1) == *tagp.command
+ || *(p + 1) == '\\'))
+ ++p;
+
+ if (*p == TAB)
+ {
+ msg_putchar(' ');
+ ++p;
+ }
+ else
+ p = msg_outtrans_one(p, 0);
+
+ // don't display the "$/;\"" and "$?;\""
+ if (p == command_end - 2 && *p == '$'
+ && *(p + 1) == *tagp.command)
+ break;
+ // don't display matching '/' or '?'
+ if (p == command_end - 1 && *p == *tagp.command
+ && (*p == '/' || *p == '?'))
+ break;
+ }
+ if (msg_col)
+ msg_putchar('\n');
+ ui_breakcheck();
+ }
+ if (got_int)
+ got_int = FALSE; // only stop the listing
+}
+
+#if defined(FEAT_QUICKFIX) && defined(FEAT_EVAL)
+/*
+ * Add the matching tags to the location list for the current
+ * window.
+ */
+ static int
+add_llist_tags(
+ char_u *tag,
+ int num_matches,
+ char_u **matches)
+{
+ list_T *list;
+ char_u tag_name[128 + 1];
+ char_u *fname;
+ char_u *cmd;
+ int i;
+ char_u *p;
+ tagptrs_T tagp;
+
+ fname = alloc(MAXPATHL + 1);
+ cmd = alloc(CMDBUFFSIZE + 1);
+ list = list_alloc();
+ if (list == NULL || fname == NULL || cmd == NULL)
+ {
+ vim_free(cmd);
+ vim_free(fname);
+ if (list != NULL)
+ list_free(list);
+ return FAIL;
+ }
+
+ for (i = 0; i < num_matches; ++i)
+ {
+ int len, cmd_len;
+ long lnum;
+ dict_T *dict;
+
+ parse_match(matches[i], &tagp);
+
+ /* Save the tag name */
+ len = (int)(tagp.tagname_end - tagp.tagname);
+ if (len > 128)
+ len = 128;
+ vim_strncpy(tag_name, tagp.tagname, len);
+ tag_name[len] = NUL;
+
+ // Save the tag file name
+ p = tag_full_fname(&tagp);
+ if (p == NULL)
+ continue;
+ vim_strncpy(fname, p, MAXPATHL);
+ vim_free(p);
+
+ // Get the line number or the search pattern used to locate
+ // the tag.
+ lnum = 0;
+ if (isdigit(*tagp.command))
+ // Line number is used to locate the tag
+ lnum = atol((char *)tagp.command);
+ else
+ {
+ char_u *cmd_start, *cmd_end;
+
+ // Search pattern is used to locate the tag
+
+ // Locate the end of the command
+ cmd_start = tagp.command;
+ cmd_end = tagp.command_end;
+ if (cmd_end == NULL)
+ {
+ for (p = tagp.command;
+ *p && *p != '\r' && *p != '\n'; ++p)
+ ;
+ cmd_end = p;
+ }
+
+ // Now, cmd_end points to the character after the
+ // command. Adjust it to point to the last
+ // character of the command.
+ cmd_end--;
+
+ // Skip the '/' and '?' characters at the
+ // beginning and end of the search pattern.
+ if (*cmd_start == '/' || *cmd_start == '?')
+ cmd_start++;
+
+ if (*cmd_end == '/' || *cmd_end == '?')
+ cmd_end--;
+
+ len = 0;
+ cmd[0] = NUL;
+
+ // If "^" is present in the tag search pattern, then
+ // copy it first.
+ if (*cmd_start == '^')
+ {
+ STRCPY(cmd, "^");
+ cmd_start++;
+ len++;
+ }
+
+ // Precede the tag pattern with \V to make it very
+ // nomagic.
+ STRCAT(cmd, "\\V");
+ len += 2;
+
+ cmd_len = (int)(cmd_end - cmd_start + 1);
+ if (cmd_len > (CMDBUFFSIZE - 5))
+ cmd_len = CMDBUFFSIZE - 5;
+ STRNCAT(cmd, cmd_start, cmd_len);
+ len += cmd_len;
+
+ if (cmd[len - 1] == '$')
+ {
+ // Replace '$' at the end of the search pattern
+ // with '\$'
+ cmd[len - 1] = '\\';
+ cmd[len] = '$';
+ len++;
+ }
+
+ cmd[len] = NUL;
+ }
+
+ if ((dict = dict_alloc()) == NULL)
+ continue;
+ if (list_append_dict(list, dict) == FAIL)
+ {
+ vim_free(dict);
+ continue;
+ }
+
+ dict_add_string(dict, "text", tag_name);
+ dict_add_string(dict, "filename", fname);
+ dict_add_number(dict, "lnum", lnum);
+ if (lnum == 0)
+ dict_add_string(dict, "pattern", cmd);
+ }
+
+ vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
+ set_errorlist(curwin, list, ' ', IObuff, NULL);
+
+ list_free(list);
+ vim_free(fname);
+ vim_free(cmd);
+
+ return OK;
+}
+#endif
+
+/*
* Free cached tags.
*/
void
@@ -3431,218 +3453,6 @@ expand_tag_fname(char_u *fname, char_u *tag_fname, int expand)
}
/*
- * Converts a file name into a canonical form. It simplifies a file name into
- * its simplest form by stripping out unneeded components, if any. The
- * resulting file name is simplified in place and will either be the same
- * length as that supplied, or shorter.
- */
- void
-simplify_filename(char_u *filename)
-{
-#ifndef AMIGA /* Amiga doesn't have "..", it uses "/" */
- int components = 0;
- char_u *p, *tail, *start;
- int stripping_disabled = FALSE;
- int relative = TRUE;
-
- p = filename;
-#ifdef BACKSLASH_IN_FILENAME
- if (p[1] == ':') /* skip "x:" */
- p += 2;
-#endif
-
- if (vim_ispathsep(*p))
- {
- relative = FALSE;
- do
- ++p;
- while (vim_ispathsep(*p));
- }
- start = p; /* remember start after "c:/" or "/" or "///" */
-
- do
- {
- /* At this point "p" is pointing to the char following a single "/"
- * or "p" is at the "start" of the (absolute or relative) path name. */
-#ifdef VMS
- /* VMS allows device:[path] - don't strip the [ in directory */
- if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
- {
- /* :[ or :< composition: vms directory component */
- ++components;
- p = getnextcomp(p + 1);
- }
- /* allow remote calls as host"user passwd"::device:[path] */
- else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
- {
- /* ":: composition: vms host/passwd component */
- ++components;
- p = getnextcomp(p + 2);
- }
- else
-#endif
- if (vim_ispathsep(*p))
- STRMOVE(p, p + 1); /* remove duplicate "/" */
- else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
- {
- if (p == start && relative)
- p += 1 + (p[1] != NUL); /* keep single "." or leading "./" */
- else
- {
- /* Strip "./" or ".///". If we are at the end of the file name
- * and there is no trailing path separator, either strip "/." if
- * we are after "start", or strip "." if we are at the beginning
- * of an absolute path name . */
- tail = p + 1;
- if (p[1] != NUL)
- while (vim_ispathsep(*tail))
- MB_PTR_ADV(tail);
- else if (p > start)
- --p; /* strip preceding path separator */
- STRMOVE(p, tail);
- }
- }
- else if (p[0] == '.' && p[1] == '.' &&
- (vim_ispathsep(p[2]) || p[2] == NUL))
- {
- /* Skip to after ".." or "../" or "..///". */
- tail = p + 2;
- while (vim_ispathsep(*tail))
- MB_PTR_ADV(tail);
-
- if (components > 0) /* strip one preceding component */
- {
- int do_strip = FALSE;
- char_u saved_char;
- stat_T st;
-
- /* Don't strip for an erroneous file name. */
- if (!stripping_disabled)
- {
- /* If the preceding component does not exist in the file
- * system, we strip it. On Unix, we don't accept a symbolic
- * link that refers to a non-existent file. */
- saved_char = p[-1];
- p[-1] = NUL;
-#ifdef UNIX
- if (mch_lstat((char *)filename, &st) < 0)
-#else
- if (mch_stat((char *)filename, &st) < 0)
-#endif
- do_strip = TRUE;
- p[-1] = saved_char;
-
- --p;
- /* Skip back to after previous '/'. */
- while (p > start && !after_pathsep(start, p))
- MB_PTR_BACK(start, p);
-
- if (!do_strip)
- {
- /* If the component exists in the file system, check
- * that stripping it won't change the meaning of the
- * file name. First get information about the
- * unstripped file name. This may fail if the component
- * to strip is not a searchable directory (but a regular
- * file, for instance), since the trailing "/.." cannot
- * be applied then. We don't strip it then since we
- * don't want to replace an erroneous file name by
- * a valid one, and we disable stripping of later
- * components. */
- saved_char = *tail;
- *tail = NUL;
- if (mch_stat((char *)filename, &st) >= 0)
- do_strip = TRUE;
- else
- stripping_disabled = TRUE;
- *tail = saved_char;
-#ifdef UNIX
- if (do_strip)
- {
- stat_T new_st;
-
- /* On Unix, the check for the unstripped file name
- * above works also for a symbolic link pointing to
- * a searchable directory. But then the parent of
- * the directory pointed to by the link must be the
- * same as the stripped file name. (The latter
- * exists in the file system since it is the
- * component's parent directory.) */
- if (p == start && relative)
- (void)mch_stat(".", &new_st);
- else
- {
- saved_char = *p;
- *p = NUL;
- (void)mch_stat((char *)filename, &new_st);
- *p = saved_char;
- }
-
- if (new_st.st_ino != st.st_ino ||
- new_st.st_dev != st.st_dev)
- {
- do_strip = FALSE;
- /* We don't disable stripping of later
- * components since the unstripped path name is
- * still valid. */
- }
- }
-#endif
- }
- }
-
- if (!do_strip)
- {
- /* Skip the ".." or "../" and reset the counter for the