Skip to content

Commit b4a6020

Browse files
committed
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)
1 parent 95946f1 commit b4a6020

5 files changed

Lines changed: 574 additions & 550 deletions

File tree

src/findfile.c

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,3 +2605,215 @@ expand_in_path(
26052605
}
26062606

26072607
#endif // FEAT_SEARCHPATH
2608+
2609+
/*
2610+
* Converts a file name into a canonical form. It simplifies a file name into
2611+
* its simplest form by stripping out unneeded components, if any. The
2612+
* resulting file name is simplified in place and will either be the same
2613+
* length as that supplied, or shorter.
2614+
*/
2615+
void
2616+
simplify_filename(char_u *filename)
2617+
{
2618+
#ifndef AMIGA // Amiga doesn't have "..", it uses "/"
2619+
int components = 0;
2620+
char_u *p, *tail, *start;
2621+
int stripping_disabled = FALSE;
2622+
int relative = TRUE;
2623+
2624+
p = filename;
2625+
# ifdef BACKSLASH_IN_FILENAME
2626+
if (p[1] == ':') // skip "x:"
2627+
p += 2;
2628+
# endif
2629+
2630+
if (vim_ispathsep(*p))
2631+
{
2632+
relative = FALSE;
2633+
do
2634+
++p;
2635+
while (vim_ispathsep(*p));
2636+
}
2637+
start = p; // remember start after "c:/" or "/" or "///"
2638+
2639+
do
2640+
{
2641+
// At this point "p" is pointing to the char following a single "/"
2642+
// or "p" is at the "start" of the (absolute or relative) path name.
2643+
# ifdef VMS
2644+
// VMS allows device:[path] - don't strip the [ in directory
2645+
if ((*p == '[' || *p == '<') && p > filename && p[-1] == ':')
2646+
{
2647+
// :[ or :< composition: vms directory component
2648+
++components;
2649+
p = getnextcomp(p + 1);
2650+
}
2651+
// allow remote calls as host"user passwd"::device:[path]
2652+
else if (p[0] == ':' && p[1] == ':' && p > filename && p[-1] == '"' )
2653+
{
2654+
// ":: composition: vms host/passwd component
2655+
++components;
2656+
p = getnextcomp(p + 2);
2657+
}
2658+
else
2659+
# endif
2660+
if (vim_ispathsep(*p))
2661+
STRMOVE(p, p + 1); // remove duplicate "/"
2662+
else if (p[0] == '.' && (vim_ispathsep(p[1]) || p[1] == NUL))
2663+
{
2664+
if (p == start && relative)
2665+
p += 1 + (p[1] != NUL); // keep single "." or leading "./"
2666+
else
2667+
{
2668+
// Strip "./" or ".///". If we are at the end of the file name
2669+
// and there is no trailing path separator, either strip "/." if
2670+
// we are after "start", or strip "." if we are at the beginning
2671+
// of an absolute path name .
2672+
tail = p + 1;
2673+
if (p[1] != NUL)
2674+
while (vim_ispathsep(*tail))
2675+
MB_PTR_ADV(tail);
2676+
else if (p > start)
2677+
--p; // strip preceding path separator
2678+
STRMOVE(p, tail);
2679+
}
2680+
}
2681+
else if (p[0] == '.' && p[1] == '.' &&
2682+
(vim_ispathsep(p[2]) || p[2] == NUL))
2683+
{
2684+
// Skip to after ".." or "../" or "..///".
2685+
tail = p + 2;
2686+
while (vim_ispathsep(*tail))
2687+
MB_PTR_ADV(tail);
2688+
2689+
if (components > 0) // strip one preceding component
2690+
{
2691+
int do_strip = FALSE;
2692+
char_u saved_char;
2693+
stat_T st;
2694+
2695+
/* Don't strip for an erroneous file name. */
2696+
if (!stripping_disabled)
2697+
{
2698+
// If the preceding component does not exist in the file
2699+
// system, we strip it. On Unix, we don't accept a symbolic
2700+
// link that refers to a non-existent file.
2701+
saved_char = p[-1];
2702+
p[-1] = NUL;
2703+
# ifdef UNIX
2704+
if (mch_lstat((char *)filename, &st) < 0)
2705+
# else
2706+
if (mch_stat((char *)filename, &st) < 0)
2707+
# endif
2708+
do_strip = TRUE;
2709+
p[-1] = saved_char;
2710+
2711+
--p;
2712+
// Skip back to after previous '/'.
2713+
while (p > start && !after_pathsep(start, p))
2714+
MB_PTR_BACK(start, p);
2715+
2716+
if (!do_strip)
2717+
{
2718+
// If the component exists in the file system, check
2719+
// that stripping it won't change the meaning of the
2720+
// file name. First get information about the
2721+
// unstripped file name. This may fail if the component
2722+
// to strip is not a searchable directory (but a regular
2723+
// file, for instance), since the trailing "/.." cannot
2724+
// be applied then. We don't strip it then since we
2725+
// don't want to replace an erroneous file name by
2726+
// a valid one, and we disable stripping of later
2727+
// components.
2728+
saved_char = *tail;
2729+
*tail = NUL;
2730+
if (mch_stat((char *)filename, &st) >= 0)
2731+
do_strip = TRUE;
2732+
else
2733+
stripping_disabled = TRUE;
2734+
*tail = saved_char;
2735+
# ifdef UNIX
2736+
if (do_strip)
2737+
{
2738+
stat_T new_st;
2739+
2740+
// On Unix, the check for the unstripped file name
2741+
// above works also for a symbolic link pointing to
2742+
// a searchable directory. But then the parent of
2743+
// the directory pointed to by the link must be the
2744+
// same as the stripped file name. (The latter
2745+
// exists in the file system since it is the
2746+
// component's parent directory.)
2747+
if (p == start && relative)
2748+
(void)mch_stat(".", &new_st);
2749+
else
2750+
{
2751+
saved_char = *p;
2752+
*p = NUL;
2753+
(void)mch_stat((char *)filename, &new_st);
2754+
*p = saved_char;
2755+
}
2756+
2757+
if (new_st.st_ino != st.st_ino ||
2758+
new_st.st_dev != st.st_dev)
2759+
{
2760+
do_strip = FALSE;
2761+
// We don't disable stripping of later
2762+
// components since the unstripped path name is
2763+
// still valid.
2764+
}
2765+
}
2766+
# endif
2767+
}
2768+
}
2769+
2770+
if (!do_strip)
2771+
{
2772+
// Skip the ".." or "../" and reset the counter for the
2773+
// components that might be stripped later on.
2774+
p = tail;
2775+
components = 0;
2776+
}
2777+
else
2778+
{
2779+
// Strip previous component. If the result would get empty
2780+
// and there is no trailing path separator, leave a single
2781+
// "." instead. If we are at the end of the file name and
2782+
// there is no trailing path separator and a preceding
2783+
// component is left after stripping, strip its trailing
2784+
// path separator as well.
2785+
if (p == start && relative && tail[-1] == '.')
2786+
{
2787+
*p++ = '.';
2788+
*p = NUL;
2789+
}
2790+
else
2791+
{
2792+
if (p > start && tail[-1] == '.')
2793+
--p;
2794+
STRMOVE(p, tail); // strip previous component
2795+
}
2796+
2797+
--components;
2798+
}
2799+
}
2800+
else if (p == start && !relative) // leading "/.." or "/../"
2801+
STRMOVE(p, tail); // strip ".." or "../"
2802+
else
2803+
{
2804+
if (p == start + 2 && p[-2] == '.') // leading "./../"
2805+
{
2806+
STRMOVE(p - 2, p); // strip leading "./"
2807+
tail -= 2;
2808+
}
2809+
p = tail; // skip to char after ".." or "../"
2810+
}
2811+
}
2812+
else
2813+
{
2814+
++components; // simple path component
2815+
p = getnextcomp(p);
2816+
}
2817+
} while (*p != NUL);
2818+
#endif // !AMIGA
2819+
}

src/proto/findfile.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ char_u *find_file_name_in_path(char_u *ptr, int len, int options, long count, ch
1515
int vim_ispathlistsep(int c);
1616
void uniquefy_paths(garray_T *gap, char_u *pattern);
1717
int expand_in_path(garray_T *gap, char_u *pattern, int flags);
18+
void simplify_filename(char_u *filename);
1819
/* vim: set ft=c : */

src/proto/tag.pro

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int
66
void free_tag_stuff(void);
77
int get_tagfname(tagname_T *tnp, int first, char_u *buf);
88
void tagname_free(tagname_T *tnp);
9-
void simplify_filename(char_u *filename);
109
int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file);
1110
int get_tags(list_T *list, char_u *pat, char_u *buf_fname);
1211
void get_tagstack(win_T *wp, dict_T *retdict);

0 commit comments

Comments
 (0)