import-util: tweak url patching helper

let's share some code between import_url_last_component() and
import_url_change_last_component(), and make sure we never eat up the
hostname component of the URL when parsing out the last component.

Let's also make import_url_change_last_component() more generic so that
we can also use it for append components to paths, instead of replacing
suffixes.
This commit is contained in:
Lennart Poettering
2021-07-30 16:23:20 +02:00
parent 1f0fb7d544
commit 56ce4adafe
2 changed files with 95 additions and 25 deletions
+85 -24
View File
@@ -14,53 +14,114 @@
#include "string-table.h"
#include "string-util.h"
int import_url_last_component(const char *url, char **ret) {
const char *e, *p;
char *s;
static const char *skip_protocol_and_hostname(const char *url) {
const char *d;
size_t n;
e = strchrnul(url, '?');
/* A very very lenient implementation of RFC3986 Section 3.2 */
while (e > url && e[-1] == '/')
/* Find colon separating protocol and hostname */
d = strchr(url, ':');
if (!d || url == d)
return NULL;
d++;
/* Skip slashes after colon */
d += strspn(d, "/");
/* Skip everything till next slash or end */
n = strcspn(d, "/?#");
if (n == 0)
return NULL;
return d + n;
}
int import_url_last_component(
const char *url,
char **ret) {
const char *e, *p, *h;
/* This extracts the last path component of the specified URI, i.e. the last non-empty substrings
* between two "/" characters. This ignores "Query" and "Fragment" suffixes (as per RFC3986). */
h = skip_protocol_and_hostname(url);
if (!h)
return -EINVAL;
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
p = e;
while (p > url && p[-1] != '/')
while (p > h && p[-1] != '/') /* Find component before that */
p--;
if (e <= p)
return -EINVAL;
if (e <= p) /* Empty component? */
return -EADDRNOTAVAIL;
s = strndup(p, e - p);
if (!s)
return -ENOMEM;
if (ret) {
char *s;
s = strndup(p, e - p);
if (!s)
return -ENOMEM;
*ret = s;
}
*ret = s;
return 0;
}
int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
const char *e;
int import_url_change_suffix(
const char *url,
size_t n_drop_components,
const char *suffix,
char **ret) {
const char *e, *h;
char *s;
assert(url);
assert(ret);
e = strchrnul(url, '?');
/* This drops the specified number of path components of the specified URI, i.e. the specified number
* of non-empty substring between two "/" characters from the end of the string, and then append the
* specified suffix instead. Before doing all this it chops off the "Query" and "Fragment" suffixes
* (they are *not* readded to the final URL). Note that n_drop_components may be 0 (in which case the
* component are simply added to the end). The suffix may be specified as NULL or empty string in
* which case nothing is appended, only the specified number of components chopped off. Note that the
* function may be called with n_drop_components == 0 and suffix == NULL, in which case the "Query"
* and "Fragment" is chopped off, and ensured the URL ends in a single "/", and that's it. */
while (e > url && e[-1] == '/')
e--;
while (e > url && e[-1] != '/')
e--;
if (e <= url)
h = skip_protocol_and_hostname(url);
if (!h)
return -EINVAL;
s = new(char, (e - url) + strlen(suffix) + 1);
e = h + strcspn(h, "?#"); /* Cut off "Query" and "Fragment" */
while (e > h && e[-1] == '/') /* Eat trailing slashes */
e--;
/* Drop the specified number of components from the end. Note that this is pretty lenient: if there
* are less component we silently drop those and then append the suffix to the top. */
while (n_drop_components > 0) {
while (e > h && e[-1] != '/') /* Eat last word (we don't mind if empty) */
e--;
while (e > h && e[-1] == '/') /* Eat slashes before the last word */
e--;
n_drop_components--;
}
s = new(char, (e - url) + 1 + strlen_ptr(suffix) + 1);
if (!s)
return -ENOMEM;
strcpy(mempcpy(s, url, e - url), suffix);
strcpy(stpcpy(mempcpy(s, url, e - url), "/"), strempty(suffix));
*ret = s;
return 0;
}
+10 -1
View File
@@ -14,7 +14,16 @@ typedef enum ImportVerify {
} ImportVerify;
int import_url_last_component(const char *url, char **ret);
int import_url_change_last_component(const char *url, const char *suffix, char **ret);
int import_url_change_suffix(const char *url, size_t n_drop_components, const char *suffix, char **ret);
static inline int import_url_change_last_component(const char *url, const char *suffix, char **ret) {
return import_url_change_suffix(url, 1, suffix, ret);
}
static inline int import_url_append_component(const char *url, const char *suffix, char **ret) {
return import_url_change_suffix(url, 0, suffix, ret);
}
const char* import_verify_to_string(ImportVerify v) _const_;
ImportVerify import_verify_from_string(const char *s) _pure_;