From b4a6020ac6a0638167013f1e45ff440ddc8a1671 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 31 Mar 2019 19:40:07 +0200 Subject: 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) --- src/findfile.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) (limited to 'src/findfile.c') diff --git a/src/findfile.c b/src/findfile.c index 690cd8fb1..3d3aec0df 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 +} -- cgit v1.2.1