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>

Derrick Stolee committed May 16, 2022 at 20:11 UTC 1d04e719e7bda885991cd4566a5bb6f6565fa106
3 files changed +134 -129
builtin/submodule--helper.c
+12 -129
@@ -72,135 +72,6 @@ static char *get_default_remote(void)
72 return repo_get_default_remote(the_repository);
73 }
74
75 -/*
76 - * Returns 1 if it was the last chop before ':'.
77 - */
78 -static int chop_last_dir(char **remoteurl, int is_relative)
79 -{
80 - char *rfind = find_last_dir_sep(*remoteurl);
81 - if (rfind) {
82 - *rfind = '\0';
83 - return 0;
84 - }
85 -
86 - rfind = strrchr(*remoteurl, ':');
87 - if (rfind) {
88 - *rfind = '\0';
89 - return 1;
90 - }
91 -
92 - if (is_relative || !strcmp(".", *remoteurl))
93 - die(_("cannot strip one component off url '%s'"),
94 - *remoteurl);
95 -
96 - free(*remoteurl);
97 - *remoteurl = xstrdup(".");
98 - return 0;
99 -}
100 -
101 -static int starts_with_dot_slash(const char *const path)
102 -{
103 - return starts_with_dot_slash_native(path);;
104 -}
105 -
106 -static int starts_with_dot_dot_slash(const char *const path)
107 -{
108 - return starts_with_dot_dot_slash_native(path);
109 -}
110 -
111 -/*
112 - * The `url` argument is the URL that navigates to the submodule origin
113 - * repo. When relative, this URL is relative to the superproject origin
114 - * URL repo. The `up_path` argument, if specified, is the relative
115 - * path that navigates from the submodule working tree to the superproject
116 - * working tree. Returns the origin URL of the submodule.
117 - *
118 - * Return either an absolute URL or filesystem path (if the superproject
119 - * origin URL is an absolute URL or filesystem path, respectively) or a
120 - * relative file system path (if the superproject origin URL is a relative
121 - * file system path).
122 - *
123 - * When the output is a relative file system path, the path is either
124 - * relative to the submodule working tree, if up_path is specified, or to
125 - * the superproject working tree otherwise.
126 - *
127 - * NEEDSWORK: This works incorrectly on the domain and protocol part.
128 - * remote_url url outcome expectation
129 - * http://a.com/b ../c http://a.com/c as is
130 - * http://a.com/b/ ../c http://a.com/c same as previous line, but
131 - * ignore trailing slash in url
132 - * http://a.com/b ../../c http://c error out
133 - * http://a.com/b ../../../c http:/c error out
134 - * http://a.com/b ../../../../c http:c error out
135 - * http://a.com/b ../../../../../c .:c error out
136 - * NEEDSWORK: Given how chop_last_dir() works, this function is broken
137 - * when a local part has a colon in its path component, too.
138 - */
139 -static char *relative_url(const char *remote_url,
140 - const char *url,
141 - const char *up_path)
142 -{
143 - int is_relative = 0;
144 - int colonsep = 0;
145 - char *out;
146 - char *remoteurl = xstrdup(remote_url);
147 - struct strbuf sb = STRBUF_INIT;
148 - size_t len = strlen(remoteurl);
149 -
150 - if (is_dir_sep(remoteurl[len-1]))
151 - remoteurl[len-1] = '\0';
152 -
153 - if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
154 - is_relative = 0;
155 - else {
156 - is_relative = 1;
157 - /*
158 - * Prepend a './' to ensure all relative
159 - * remoteurls start with './' or '../'
160 - */
161 - if (!starts_with_dot_slash(remoteurl) &&
162 - !starts_with_dot_dot_slash(remoteurl)) {
163 - strbuf_reset(&sb);
164 - strbuf_addf(&sb, "./%s", remoteurl);
165 - free(remoteurl);
166 - remoteurl = strbuf_detach(&sb, NULL);
167 - }
168 - }
169 - /*
170 - * When the url starts with '../', remove that and the
171 - * last directory in remoteurl.
172 - */
173 - while (url) {
174 - if (starts_with_dot_dot_slash(url)) {
175 - url += 3;
176 - colonsep |= chop_last_dir(&remoteurl, is_relative);
177 - } else if (starts_with_dot_slash(url))
178 - url += 2;
179 - else
180 - break;
181 - }
182 - strbuf_reset(&sb);
183 - strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
184 - if (ends_with(url, "/"))
185 - strbuf_setlen(&sb, sb.len - 1);
186 - free(remoteurl);
187 -
188 - if (starts_with_dot_slash(sb.buf))
189 - out = xstrdup(sb.buf + 2);
190 - else
191 - out = xstrdup(sb.buf);
192 -
193 - if (!up_path || !is_relative) {
194 - strbuf_release(&sb);
195 - return out;
196 - }
197 -
198 - strbuf_reset(&sb);
199 - strbuf_addf(&sb, "%s%s", up_path, out);
200 - free(out);
201 - return strbuf_detach(&sb, NULL);
202 -}
203 -
75 static char *resolve_relative_url(const char *rel_url, const char *up_path, int quiet)
76 {
77 char *remoteurl, *resolved_url;
@@ -592,6 +463,18 @@ static int module_foreach(int argc, const char **argv, const char *prefix)
463 return 0;
464 }
465
466 +static int starts_with_dot_slash(const char *const path)
467 +{
468 + return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_SLASH |
469 + PATH_MATCH_XPLATFORM);
470 +}
471 +
472 +static int starts_with_dot_dot_slash(const char *const path)
473 +{
474 + return path_match_flags(path, PATH_MATCH_STARTS_WITH_DOT_DOT_SLASH |
475 + PATH_MATCH_XPLATFORM);
476 +}
477 +
478 struct init_cb {
479 const char *prefix;
480 const char *superprefix;
remote.c
+91
@@ -14,6 +14,7 @@
14 #include "strvec.h"
15 #include "commit-reach.h"
16 #include "advice.h"
17 +#include "connect.h"
18
19 enum map_direction { FROM_SRC, FROM_DST };
20
@@ -2727,3 +2728,93 @@ void remote_state_clear(struct remote_state *remote_state)
2728 hashmap_clear_and_free(&remote_state->remotes_hash, struct remote, ent);
2729 hashmap_clear_and_free(&remote_state->branches_hash, struct remote, ent);
2730 }
2731 +
2732 +/*
2733 + * Returns 1 if it was the last chop before ':'.
2734 + */
2735 +static int chop_last_dir(char **remoteurl, int is_relative)
2736 +{
2737 + char *rfind = find_last_dir_sep(*remoteurl);
2738 + if (rfind) {
2739 + *rfind = '\0';
2740 + return 0;
2741 + }
2742 +
2743 + rfind = strrchr(*remoteurl, ':');
2744 + if (rfind) {
2745 + *rfind = '\0';
2746 + return 1;
2747 + }
2748 +
2749 + if (is_relative || !strcmp(".", *remoteurl))
2750 + die(_("cannot strip one component off url '%s'"),
2751 + *remoteurl);
2752 +
2753 + free(*remoteurl);
2754 + *remoteurl = xstrdup(".");
2755 + return 0;
2756 +}
2757 +
2758 +char *relative_url(const char *remote_url, const char *url,
2759 + const char *up_path)
2760 +{
2761 + int is_relative = 0;
2762 + int colonsep = 0;
2763 + char *out;
2764 + char *remoteurl = xstrdup(remote_url);
2765 + struct strbuf sb = STRBUF_INIT;
2766 + size_t len = strlen(remoteurl);
2767 +
2768 + if (is_dir_sep(remoteurl[len-1]))
2769 + remoteurl[len-1] = '\0';
2770 +
2771 + if (!url_is_local_not_ssh(remoteurl) || is_absolute_path(remoteurl))
2772 + is_relative = 0;
2773 + else {
2774 + is_relative = 1;
2775 + /*
2776 + * Prepend a './' to ensure all relative
2777 + * remoteurls start with './' or '../'
2778 + */
2779 + if (!starts_with_dot_slash_native(remoteurl) &&
2780 + !starts_with_dot_dot_slash_native(remoteurl)) {
2781 + strbuf_reset(&sb);
2782 + strbuf_addf(&sb, "./%s", remoteurl);
2783 + free(remoteurl);
2784 + remoteurl = strbuf_detach(&sb, NULL);
2785 + }
2786 + }
2787 + /*
2788 + * When the url starts with '../', remove that and the
2789 + * last directory in remoteurl.
2790 + */
2791 + while (url) {
2792 + if (starts_with_dot_dot_slash_native(url)) {
2793 + url += 3;
2794 + colonsep |= chop_last_dir(&remoteurl, is_relative);
2795 + } else if (starts_with_dot_slash_native(url))
2796 + url += 2;
2797 + else
2798 + break;
2799 + }
2800 + strbuf_reset(&sb);
2801 + strbuf_addf(&sb, "%s%s%s", remoteurl, colonsep ? ":" : "/", url);
2802 + if (ends_with(url, "/"))
2803 + strbuf_setlen(&sb, sb.len - 1);
2804 + free(remoteurl);
2805 +
2806 + if (starts_with_dot_slash_native(sb.buf))
2807 + out = xstrdup(sb.buf + 2);
2808 + else
2809 + out = xstrdup(sb.buf);
2810 +
2811 + if (!up_path || !is_relative) {
2812 + strbuf_release(&sb);
2813 + return out;
2814 + }
2815 +
2816 + strbuf_reset(&sb);
2817 + strbuf_addf(&sb, "%s%s", up_path, out);
2818 + free(out);
2819 + return strbuf_detach(&sb, NULL);
2820 +}
remote.h
+31
@@ -409,4 +409,35 @@ int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
409 int is_empty_cas(const struct push_cas_option *);
410 void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
411
412 +/*
413 + * The `url` argument is the URL that navigates to the submodule origin
414 + * repo. When relative, this URL is relative to the superproject origin
415 + * URL repo. The `up_path` argument, if specified, is the relative
416 + * path that navigates from the submodule working tree to the superproject
417 + * working tree. Returns the origin URL of the submodule.
418 + *
419 + * Return either an absolute URL or filesystem path (if the superproject
420 + * origin URL is an absolute URL or filesystem path, respectively) or a
421 + * relative file system path (if the superproject origin URL is a relative
422 + * file system path).
423 + *
424 + * When the output is a relative file system path, the path is either
425 + * relative to the submodule working tree, if up_path is specified, or to
426 + * the superproject working tree otherwise.
427 + *
428 + * NEEDSWORK: This works incorrectly on the domain and protocol part.
429 + * remote_url url outcome expectation
430 + * http://a.com/b ../c http://a.com/c as is
431 + * http://a.com/b/ ../c http://a.com/c same as previous line, but
432 + * ignore trailing slash in url
433 + * http://a.com/b ../../c http://c error out
434 + * http://a.com/b ../../../c http:/c error out
435 + * http://a.com/b ../../../../c http:c error out
436 + * http://a.com/b ../../../../../c .:c error out
437 + * NEEDSWORK: Given how chop_last_dir() works, this function is broken
438 + * when a local part has a colon in its path component, too.
439 + */
440 +char *relative_url(const char *remote_url, const char *url,
441 + const char *up_path);
442 +
443 #endif