]> granicus.if.org Git - vim/commitdiff
patch 8.1.1099: the do_tag() function is too long v8.1.1099
authorBram Moolenaar <Bram@vim.org>
Sun, 31 Mar 2019 17:40:07 +0000 (19:40 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 31 Mar 2019 17:40:07 +0000 (19:40 +0200)
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)

src/findfile.c
src/proto/findfile.pro
src/proto/tag.pro
src/tag.c
src/version.c

index 690cd8fb19176e476a753bc53eb6587d8897cc7b..3d3aec0df967feee33968621aac366421dd85626 100644 (file)
@@ -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
+}
index 5b80b83c695a997a2cb28dfb3474b0e6afb8720e..ebab7927336b63768a55e813d54228934baa619b 100644 (file)
@@ -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 : */
index c9bcb384eee7145e698067d46907924287656230..3046406a3f01f4b66b436e532b16b258fb42db53 100644 (file)
@@ -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);
index 0f4a70d97574968ac95fa3909ab6cfc934d772b3..10039b78736e34bca7ad3e4288591a92d1c99ca4 100644 (file)
--- 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
@@ -1088,6 +768,348 @@ end_do_tag:
 #endif
 }
 
+/*
+ * 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.
  */
@@ -3430,218 +3452,6 @@ expand_tag_fname(char_u *fname, char_u *tag_fname, int expand)
     return retval;
 }
 
-/*
- * 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 */
-}
-
 /*
  * 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 fullpathcmp().
index c82a153740b878244dddb494c32a90f801236437..1b38d299acf1d56b5363d4746a1476727fc72ccd 100644 (file)
@@ -771,6 +771,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1099,
 /**/
     1098,
 /**/