remote: move relative_url()

This method was initially written in 63e95beb0 (submodule: port
resolve_relative_url from shell to C, 2016-05-15). As we will need
similar functionality in the bundle URI feature, extract this to be
available in remote.h.

The code is almost exactly the same, except for the following trivial
differences:

 * Fix whitespace and wrapping issues with the prototype and argument
   lists.

 * Let's call starts_with_dot_{,dot_}slash_native() instead of the
   functionally identical "starts_with_dot_{,dot_}slash()" wrappers
   "builtin/submodule--helper.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Derrick Stolee <derrickstolee@github.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee
2022-05-16 20:11:03 +00:00
committed by Junio C Hamano
parent c1d024b843
commit 1d04e719e7
3 changed files with 134 additions and 129 deletions

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 };
@ -2727,3 +2728,93 @@ 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 = 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_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);
}