diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index 2c87ef9364fa6d..b68102bb3edf9f 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -72,16 +72,6 @@ static char *get_default_remote(void) return repo_get_default_remote(the_repository); } -static int starts_with_dot_slash(const char *str) -{ - return str[0] == '.' && is_dir_sep(str[1]); -} - -static int starts_with_dot_dot_slash(const char *str) -{ - return str[0] == '.' && str[1] == '.' && is_dir_sep(str[2]); -} - /* * Returns 1 if it was the last chop before ':'. */ @@ -108,6 +98,16 @@ static int chop_last_dir(char **remoteurl, int is_relative) return 0; } +static int starts_with_dot_slash(const char *const path) +{ + return starts_with_dot_slash_native(path);; +} + +static int starts_with_dot_dot_slash(const char *const path) +{ + return starts_with_dot_dot_slash_native(path); +} + /* * The `url` argument is the URL that navigates to the submodule origin * repo. When relative, this URL is relative to the superproject origin diff --git a/compat/mingw.c b/compat/mingw.c index 6fe80fdf014021..b94b473d97857f 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2830,7 +2830,7 @@ int is_valid_win32_path(const char *path, int allow_literal_nul) } c = path[i]; - if (c && c != '.' && c != ':' && c != '/' && c != '\\') + if (c && c != '.' && c != ':' && !is_xplatform_dir_sep(c)) goto not_a_reserved_name; /* contains reserved name */ diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h index bba2b644080f06..65fa3b9263a47c 100644 --- a/compat/win32/path-utils.h +++ b/compat/win32/path-utils.h @@ -6,11 +6,7 @@ int win32_has_dos_drive_prefix(const char *path); int win32_skip_dos_drive_prefix(char **path); #define skip_dos_drive_prefix win32_skip_dos_drive_prefix -static inline int win32_is_dir_sep(int c) -{ - return c == '/' || c == '\\'; -} -#define is_dir_sep win32_is_dir_sep +#define is_dir_sep is_xplatform_dir_sep static inline char *win32_find_last_dir_sep(const char *path) { char *ret = NULL; diff --git a/dir.c b/dir.c index f2b0f242101b29..d25aa6ade5571e 100644 --- a/dir.c +++ b/dir.c @@ -3890,3 +3890,32 @@ void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_ connect_work_tree_and_git_dir(path, new_git_dir, 0); } + +int path_match_flags(const char *const str, const enum path_match_flags flags) +{ + const char *p = str; + + if (flags & PATH_MATCH_NATIVE && + flags & PATH_MATCH_XPLATFORM) + BUG("path_match_flags() must get one match kind, not multiple!"); + else if (!(flags & PATH_MATCH_KINDS_MASK)) + BUG("path_match_flags() must get at least one match kind!"); + + if (flags & PATH_MATCH_STARTS_WITH_DOT_SLASH && + flags & PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH) + BUG("path_match_flags() must get one platform kind, not multiple!"); + else if (!(flags & PATH_MATCH_PLATFORM_MASK)) + BUG("path_match_flags() must get at least one platform kind!"); + + if (*p++ != '.') + return 0; + if (flags & PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH && + *p++ != '.') + return 0; + + if (flags & PATH_MATCH_NATIVE) + return is_dir_sep(*p); + else if (flags & PATH_MATCH_XPLATFORM) + return is_xplatform_dir_sep(*p); + BUG("unreachable"); +} diff --git a/dir.h b/dir.h index 8e02dfb505d163..7bc862030cfb3b 100644 --- a/dir.h +++ b/dir.h @@ -578,4 +578,67 @@ void connect_work_tree_and_git_dir(const char *work_tree, void relocate_gitdir(const char *path, const char *old_git_dir, const char *new_git_dir); + +/** + * The "enum path_matches_kind" determines how path_match_flags() will + * behave. The flags come in sets, and one (and only one) must be + * provided out of each "set": + * + * PATH_MATCH_NATIVE: + * Path separator is is_dir_sep() + * PATH_MATCH_XPLATFORM: + * Path separator is is_xplatform_dir_sep() + * + * Do we use is_dir_sep() to check for a directory separator + * (*_NATIVE), or do we always check for '/' or '\' (*_XPLATFORM). The + * "*_NATIVE" version on Windows is the same as "*_XPLATFORM", + * everywhere else "*_NATIVE" means "only /". + * + * PATH_MATCH_STARTS_WITH_DOT_SLASH: + * Match a path starting with "./" + * PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH: + * Match a path starting with "../" + * + * The "/" in the above is adjusted based on the "*_NATIVE" and + * "*_XPLATFORM" flags. + */ +enum path_match_flags { + PATH_MATCH_NATIVE = 1 << 0, + PATH_MATCH_XPLATFORM = 1 << 1, + PATH_MATCH_STARTS_WITH_DOT_SLASH = 1 << 2, + PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH = 1 << 3, +}; +#define PATH_MATCH_KINDS_MASK (PATH_MATCH_STARTS_WITH_DOT_SLASH | \ + PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH) +#define PATH_MATCH_PLATFORM_MASK (PATH_MATCH_NATIVE | PATH_MATCH_XPLATFORM) + +/** + * path_match_flags() checks if a given "path" matches a given "enum + * path_match_flags" criteria. + */ +int path_match_flags(const char *const path, const enum path_match_flags f); + +/** + * starts_with_dot_slash_native(): convenience wrapper for + * path_match_flags() with PATH_MATCH_STARTS_WITH_DOT_SLASH and + * PATH_MATCH_NATIVE. + */ +static inline int starts_with_dot_slash_native(const char *const path) +{ + const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_SLASH; + + return path_match_flags(path, what | PATH_MATCH_NATIVE); +} + +/** + * starts_with_dot_slash_native(): convenience wrapper for + * path_match_flags() with PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH and + * PATH_MATCH_NATIVE. + */ +static inline int starts_with_dot_dot_slash_native(const char *const path) +{ + const enum path_match_flags what = PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH; + + return path_match_flags(path, what | PATH_MATCH_NATIVE); +} #endif diff --git a/fsck.c b/fsck.c index 3ec500d707aaf6..dd4822ba1be7fe 100644 --- a/fsck.c +++ b/fsck.c @@ -975,27 +975,16 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer, return ret; } -/* - * Like builtin/submodule--helper.c's starts_with_dot_slash, but without - * relying on the platform-dependent is_dir_sep helper. - * - * This is for use in checking whether a submodule URL is interpreted as - * relative to the current directory on any platform, since \ is a - * directory separator on Windows but not on other platforms. - */ -static int starts_with_dot_slash(const char *str) +static int starts_with_dot_slash(const char *const path) { - return str[0] == '.' && (str[1] == '/' || str[1] == '\\'); + return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH | + PATH_MATCH_XPLATFORM); } -/* - * Like starts_with_dot_slash, this is a variant of submodule--helper's - * helper of the same name with the twist that it accepts backslash as a - * directory separator even on non-Windows platforms. - */ -static int starts_with_dot_dot_slash(const char *str) +static int starts_with_dot_dot_slash(const char *const path) { - return str[0] == '.' && starts_with_dot_slash(str + 1); + return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH | + PATH_MATCH_XPLATFORM); } static int submodule_url_is_relative(const char *url) diff --git a/git-compat-util.h b/git-compat-util.h index 58fd813bd012f1..ba3436db9a1d0a 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -236,6 +236,12 @@ #include #endif +/* Used by compat/win32/path-utils.h, and more */ +static inline int is_xplatform_dir_sep(int c) +{ + return c == '/' || c == '\\'; +} + #if defined(__CYGWIN__) #include "compat/win32/path-utils.h" #endif @@ -416,11 +422,11 @@ static inline int git_skip_dos_drive_prefix(char **path) #define skip_dos_drive_prefix git_skip_dos_drive_prefix #endif -#ifndef is_dir_sep static inline int git_is_dir_sep(int c) { return c == '/'; } +#ifndef is_dir_sep #define is_dir_sep git_is_dir_sep #endif diff --git a/path.c b/path.c index d73146b6cd266a..2ab78278943a1d 100644 --- a/path.c +++ b/path.c @@ -1413,7 +1413,7 @@ int is_ntfs_dotgit(const char *name) for (;;) { c = *(name++); - if (!c || c == '\\' || c == '/' || c == ':') + if (!c || is_xplatform_dir_sep(c) || c == ':') return 1; if (c != '.' && c != ' ') return 0; diff --git a/submodule-config.c b/submodule-config.c index 29668b0620dd91..ce3beaf5d4f0d6 100644 --- a/submodule-config.c +++ b/submodule-config.c @@ -204,17 +204,17 @@ int check_submodule_name(const char *name) return -1; /* - * Look for '..' as a path component. Check both '/' and '\\' as + * Look for '..' as a path component. Check is_xplatform_dir_sep() as * separators rather than is_dir_sep(), because we want the name rules * to be consistent across platforms. */ goto in_component; /* always start inside component */ while (*name) { char c = *name++; - if (c == '/' || c == '\\') { + if (is_xplatform_dir_sep(c)) { in_component: if (name[0] == '.' && name[1] == '.' && - (!name[2] || name[2] == '/' || name[2] == '\\')) + (!name[2] || is_xplatform_dir_sep(name[2]))) return -1; } }