Commit f5082d0a authored by John Schmidt's avatar John Schmidt Committed by Thiago de Arruda

Move simplify_filename from tag.c

parent 7fd140b9
......@@ -1301,3 +1301,172 @@ addfile (
((char_u **)gap->ga_data)[gap->ga_len++] = p;
}
#endif /* !NO_EXPANDPATH */
/*
* 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)
{
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. */
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;
struct stat 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) {
struct stat 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);
}
......@@ -22,4 +22,5 @@ char_u *get_past_head(char_u *path);
char_u *concat_str(char_u *str1, char_u *str2);
void add_pathsep(char_u *p);
char_u *FullName_save(char_u *fname, int force);
void simplify_filename(char_u *filename);
#endif
......@@ -2721,175 +2721,6 @@ static char_u *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)
{
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. */
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;
struct stat 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) {
struct stat 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);
}
/*
* 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().
......
......@@ -21,7 +21,6 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp,
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment