Merge branch 'ds/bundle-uri'

Preliminary code refactoring around transport and bundle code.

* ds/bundle-uri:
  bundle.h: make "fd" version of read_bundle_header() public
  remote: allow relative_url() to return an absolute url
  remote: move relative_url()
  http: make http_get_file() external
  fetch-pack: move --keep=* option filling to a function
  fetch-pack: add a deref_without_lazy_fetch_extended()
  dir API: add a generalized path_match_flags() function
  connect.c: refactor sending of agent & object-format
This commit is contained in:
Junio C Hamano 2022-06-03 14:30:34 -07:00
commit b3b2ddced2
17 changed files with 322 additions and 192 deletions

View File

@ -72,135 +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 ':'.
*/
static int chop_last_dir(char **remoteurl, int is_relative)
{
char *rfind = find_last_dir_sep(*remoteurl);
if (rfind) {
*rfind = '\0';
return 0;
}
rfind = strrchr(*remoteurl, ':');
if (rfind) {
*rfind = '\0';
return 1;
}
if (is_relative || !strcmp(".", *remoteurl))
die(_("cannot strip one component off url '%s'"),
*remoteurl);
free(*remoteurl);
*remoteurl = xstrdup(".");
return 0;
}
/*
* The `url` argument is the URL that navigates to the submodule origin
* repo. When relative, this URL is relative to the superproject origin
* URL repo. The `up_path` argument, if specified, is the relative
* path that navigates from the submodule working tree to the superproject
* working tree. Returns the origin URL of the submodule.
*
* Return either an absolute URL or filesystem path (if the superproject
* origin URL is an absolute URL or filesystem path, respectively) or a
* relative file system path (if the superproject origin URL is a relative
* file system path).
*
* When the output is a relative file system path, the path is either
* relative to the submodule working tree, if up_path is specified, or to
* the superproject working tree otherwise.
*
* NEEDSWORK: This works incorrectly on the domain and protocol part.
* remote_url url outcome expectation
* http://a.com/b ../c http://a.com/c as is
* http://a.com/b/ ../c http://a.com/c same as previous line, but
* ignore trailing slash in url
* http://a.com/b ../../c http://c error out
* http://a.com/b ../../../c http:/c error out
* http://a.com/b ../../../../c http:c error out
* http://a.com/b ../../../../../c .:c error out
* NEEDSWORK: Given how chop_last_dir() works, this function is broken
* when a local part has a colon in its path component, too.
*/
static char *relative_url(const char *remote_url,
const char *url,
const char *up_path)
{
int is_relative = 0;
int colonsep = 0;
char *out;
char *remoteurl = xstrdup(remote_url);
struct strbuf sb = STRBUF_INIT;
size_t len = strlen(remoteurl);
if (is_dir_sep(remoteurl[len-1]))
remoteurl[len-1] = '\0';
if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
is_relative = 0;
else {
is_relative = 1;
/*
* Prepend a './' to ensure all relative
* remoteurls start with './' or '../'
*/
if (!starts_with_dot_slash(remoteurl) &&
!starts_with_dot_dot_slash(remoteurl)) {
strbuf_reset(&sb);
strbuf_addf(&sb, "./%s", remoteurl);
free(remoteurl);
remoteurl = strbuf_detach(&sb, NULL);
}
}
/*
* When the url starts with '../', remove that and the
* last directory in remoteurl.
*/
while (url) {
if (starts_with_dot_dot_slash(url)) {
url += 3;
colonsep |= chop_last_dir(&remoteurl, is_relative);
} else if (starts_with_dot_slash(url))
url += 2;
else
break;
}
strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
if (ends_with(url, "/"))
strbuf_setlen(&sb, sb.len - 1);
free(remoteurl);
if (starts_with_dot_slash(sb.buf))
out = xstrdup(sb.buf + 2);
else
out = xstrdup(sb.buf);
if (!up_path || !is_relative) {
strbuf_release(&sb);
return out;
}
strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s", up_path, out);
free(out);
return strbuf_detach(&sb, NULL);
}
static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
{
char *remoteurl, *resolved_url;
@ -592,6 +463,18 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
return 0;
}
static int starts_with_dot_slash(const char *const path)
{
return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
PATH_MATCH_XPLATFORM);
}
static int starts_with_dot_dot_slash(const char *const path)
{
return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
PATH_MATCH_XPLATFORM);
}
struct init_cb {
const char *prefix;
const char *superprefix;

View File

@ -66,8 +66,8 @@ static int parse_bundle_signature(struct bundle_header *header, const char *line
return -1;
}
static int parse_bundle_header(int fd, struct bundle_header *header,
const char *report_path)
int read_bundle_header_fd(int fd, struct bundle_header *header,
const char *report_path)
{
struct strbuf buf = STRBUF_INIT;
int status = 0;
@ -143,7 +143,7 @@ int read_bundle_header(const char *path, struct bundle_header *header)
if (fd < 0)
return error(_("could not open '%s'"), path);
return parse_bundle_header(fd, header, path);
return read_bundle_header_fd(fd, header, path);
}
int is_bundle(const char *path, int quiet)
@ -153,7 +153,7 @@ int is_bundle(const char *path, int quiet)
if (fd < 0)
return 0;
fd = parse_bundle_header(fd, &header, quiet ? NULL : path);
fd = read_bundle_header_fd(fd, &header, quiet ? NULL : path);
if (fd >= 0)
close(fd);
bundle_header_release(&header);

View File

@ -24,6 +24,8 @@ void bundle_header_release(struct bundle_header *header);
int is_bundle(const char *path, int quiet);
int read_bundle_header(const char *path, struct bundle_header *header);
int read_bundle_header_fd(int fd, struct bundle_header *header,
const char *report_path);
int create_bundle(struct repository *r, const char *path,
int argc, const char **argv, struct strvec *pack_options,
int version);

View File

@ -2830,7 +2830,7 @@ not_a_reserved_name:
}
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 */

View File

@ -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;

View File

@ -473,22 +473,9 @@ void check_stateless_delimiter(int stateless_rpc,
die("%s", error);
}
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
struct transport_ls_refs_options *transport_options,
const struct string_list *server_options,
int stateless_rpc)
static void send_capabilities(int fd_out, struct packet_reader *reader)
{
int i;
const char *hash_name;
struct strvec *ref_prefixes = transport_options ?
&transport_options->ref_prefixes : NULL;
const char **unborn_head_target = transport_options ?
&transport_options->unborn_head_target : NULL;
*list = NULL;
if (server_supports_v2("ls-refs", 1))
packet_write_fmt(fd_out, "command=ls-refs\n");
if (server_supports_v2("agent", 0))
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
@ -502,6 +489,26 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
} else {
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
}
}
struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
struct ref **list, int for_push,
struct transport_ls_refs_options *transport_options,
const struct string_list *server_options,
int stateless_rpc)
{
int i;
struct strvec *ref_prefixes = transport_options ?
&transport_options->ref_prefixes : NULL;
const char **unborn_head_target = transport_options ?
&transport_options->unborn_head_target : NULL;
*list = NULL;
if (server_supports_v2("ls-refs", 1))
packet_write_fmt(fd_out, "command=ls-refs\n");
/* Send capabilities */
send_capabilities(fd_out, reader);
if (server_options && server_options->nr &&
server_supports_v2("server-option", 1))

29
dir.c
View File

@ -3955,3 +3955,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");
}

63
dir.h
View File

@ -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

View File

@ -115,11 +115,12 @@ static void for_each_cached_alternate(struct fetch_negotiator *negotiator,
cb(negotiator, cache.items[i]);
}
static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
int mark_tags_complete)
static struct commit *deref_without_lazy_fetch_extended(const struct object_id *oid,
int mark_tags_complete,
enum object_type *type,
unsigned int oi_flags)
{
enum object_type type;
struct object_info info = { .typep = &type };
struct object_info info = { .typep = type };
struct commit *commit;
commit = lookup_commit_in_graph(the_repository, oid);
@ -128,9 +129,9 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
while (1) {
if (oid_object_info_extended(the_repository, oid, &info,
OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK))
oi_flags))
return NULL;
if (type == OBJ_TAG) {
if (*type == OBJ_TAG) {
struct tag *tag = (struct tag *)
parse_object(the_repository, oid);
@ -144,7 +145,7 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
}
}
if (type == OBJ_COMMIT) {
if (*type == OBJ_COMMIT) {
struct commit *commit = lookup_commit(the_repository, oid);
if (!commit || repo_parse_commit(the_repository, commit))
return NULL;
@ -154,6 +155,16 @@ static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
return NULL;
}
static struct commit *deref_without_lazy_fetch(const struct object_id *oid,
int mark_tags_complete)
{
enum object_type type;
unsigned flags = OBJECT_INFO_SKIP_FETCH_OBJECT | OBJECT_INFO_QUICK;
return deref_without_lazy_fetch_extended(oid, mark_tags_complete,
&type, flags);
}
static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
const struct object_id *oid)
{
@ -836,6 +847,16 @@ static void parse_gitmodules_oids(int fd, struct oidset *gitmodules_oids)
} while (1);
}
static void add_index_pack_keep_option(struct strvec *args)
{
char hostname[HOST_NAME_MAX + 1];
if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
strvec_pushf(args, "--keep=fetch-pack %"PRIuMAX " on %s",
(uintmax_t)getpid(), hostname);
}
/*
* If packfile URIs were provided, pass a non-NULL pointer to index_pack_args.
* The strings to pass as the --index-pack-arg arguments to http-fetch will be
@ -905,14 +926,8 @@ static int get_pack(struct fetch_pack_args *args,
strvec_push(&cmd.args, "-v");
if (args->use_thin_pack)
strvec_push(&cmd.args, "--fix-thin");
if ((do_keep || index_pack_args) && (args->lock_pack || unpack_limit)) {
char hostname[HOST_NAME_MAX + 1];
if (xgethostname(hostname, sizeof(hostname)))
xsnprintf(hostname, sizeof(hostname), "localhost");
strvec_pushf(&cmd.args,
"--keep=fetch-pack %"PRIuMAX " on %s",
(uintmax_t)getpid(), hostname);
}
if ((do_keep || index_pack_args) && (args->lock_pack || unpack_limit))
add_index_pack_keep_option(&cmd.args);
if (!index_pack_args && args->check_self_contained_and_connected)
strvec_push(&cmd.args, "--check-self-contained-and-connected");
else

23
fsck.c
View File

@ -975,27 +975,16 @@ done:
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)

View File

@ -236,6 +236,12 @@
#include <sys/sysctl.h>
#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

4
http.c
View File

@ -1989,8 +1989,8 @@ int http_get_strbuf(const char *url,
* If a previous interrupted download is detected (i.e. a previous temporary
* file is still around) the download is resumed.
*/
static int http_get_file(const char *url, const char *filename,
struct http_get_options *options)
int http_get_file(const char *url, const char *filename,
struct http_get_options *options)
{
int ret;
struct strbuf tmpfile = STRBUF_INIT;

9
http.h
View File

@ -163,6 +163,15 @@ struct http_get_options {
*/
int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
/*
* Downloads a URL and stores the result in the given file.
*
* If a previous interrupted download is detected (i.e. a previous temporary
* file is still around) the download is resumed.
*/
int http_get_file(const char *url, const char *filename,
struct http_get_options *options);
int http_fetch_ref(const char *base, struct ref *ref);
/* Helpers for fetching packs */

2
path.c
View File

@ -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;

View File

@ -14,6 +14,7 @@
#include "strvec.h"
#include "commit-reach.h"
#include "advice.h"
#include "connect.h"
enum map_direction { FROM_SRC, FROM_DST };
@ -2729,3 +2730,101 @@ void remote_state_clear(struct remote_state *remote_state)
hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);
hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent);
}
/*
* Returns 1 if it was the last chop before ':'.
*/
static int chop_last_dir(char **remoteurl, int is_relative)
{
char *rfind = find_last_dir_sep(*remoteurl);
if (rfind) {
*rfind = '\0';
return 0;
}
rfind = strrchr(*remoteurl, ':');
if (rfind) {
*rfind = '\0';
return 1;
}
if (is_relative || !strcmp(".", *remoteurl))
die(_("cannot strip one component off url '%s'"),
*remoteurl);
free(*remoteurl);
*remoteurl = xstrdup(".");
return 0;
}
char *relative_url(const char *remote_url, const char *url,
const char *up_path)
{
int is_relative = 0;
int colonsep = 0;
char *out;
char *remoteurl;
struct strbuf sb = STRBUF_INIT;
size_t len;
if (!url_is_local_not_ssh(url) || is_absolute_path(url))
return xstrdup(url);
len = strlen(remote_url);
if (!len)
BUG("invalid empty remote_url");
remoteurl = xstrdup(remote_url);
if (is_dir_sep(remoteurl[len-1]))
remoteurl[len-1] = '\0';
if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
is_relative = 0;
else {
is_relative = 1;
/*
* Prepend a './' to ensure all relative
* remoteurls start with './' or '../'
*/
if (!starts_with_dot_slash_native(remoteurl) &&
!starts_with_dot_dot_slash_native(remoteurl)) {
strbuf_reset(&sb);
strbuf_addf(&sb, "./%s", remoteurl);
free(remoteurl);
remoteurl = strbuf_detach(&sb, NULL);
}
}
/*
* When the url starts with '../', remove that and the
* last directory in remoteurl.
*/
while (url) {
if (starts_with_dot_dot_slash_native(url)) {
url += 3;
colonsep |= chop_last_dir(&remoteurl, is_relative);
} else if (starts_with_dot_slash_native(url))
url += 2;
else
break;
}
strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
if (ends_with(url, "/"))
strbuf_setlen(&sb, sb.len - 1);
free(remoteurl);
if (starts_with_dot_slash_native(sb.buf))
out = xstrdup(sb.buf + 2);
else
out = xstrdup(sb.buf);
if (!up_path || !is_relative) {
strbuf_release(&sb);
return out;
}
strbuf_reset(&sb);
strbuf_addf(&sb, "%s%s", up_path, out);
free(out);
return strbuf_detach(&sb, NULL);
}

View File

@ -409,4 +409,36 @@ int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
int is_empty_cas(const struct push_cas_option *);
void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
/*
* The `url` argument is the URL that navigates to the submodule origin
* repo. When relative, this URL is relative to the superproject origin
* URL repo. The `up_path` argument, if specified, is the relative
* path that navigates from the submodule working tree to the superproject
* working tree. Returns the origin URL of the submodule.
*
* Return either an absolute URL or filesystem path (if the superproject
* origin URL is an absolute URL or filesystem path, respectively) or a
* relative file system path (if the superproject origin URL is a relative
* file system path).
*
* When the output is a relative file system path, the path is either
* relative to the submodule working tree, if up_path is specified, or to
* the superproject working tree otherwise.
*
* NEEDSWORK: This works incorrectly on the domain and protocol part.
* remote_url url outcome expectation
* http://a.com/b ../c http://a.com/c as is
* http://a.com/b/ ../c http://a.com/c same as previous line, but
* ignore trailing slash in url
* http://a.com/b ../../c http://c error out
* http://a.com/b ../../../c http:/c error out
* http://a.com/b ../../../../c http:c error out
* http://a.com/b ../../../../../c .:c error out
* http://a.com/b http://d.org/e http://d.org/e as is
* NEEDSWORK: Given how chop_last_dir() works, this function is broken
* when a local part has a colon in its path component, too.
*/
char *relative_url(const char *remote_url, const char *url,
const char *up_path);
#endif

View File

@ -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;
}
}