diff src/findfile.c @ 16188:848d4c6e391e v8.1.1099

patch 8.1.1099: the do_tag() function is too long commit https://github.com/vim/vim/commit/b4a6020ac6a0638167013f1e45ff440ddc8a1671 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Mar 31 19:40:07 2019 +0200 patch 8.1.1099: the do_tag() function is too long 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 31 Mar 2019 19:45:05 +0200
parents 58610c4d785c
children ef00b6bc186b
line wrap: on
line diff
--- 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
+}