diff options
Diffstat (limited to 'src/libwebvi/urlutils.c')
-rw-r--r-- | src/libwebvi/urlutils.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/libwebvi/urlutils.c b/src/libwebvi/urlutils.c new file mode 100644 index 0000000..de64e06 --- /dev/null +++ b/src/libwebvi/urlutils.c @@ -0,0 +1,132 @@ +#include <string.h> +#include "urlutils.h" + +static const gchar *skip_scheme(const char *url); +static gboolean is_scheme_character(gchar c); + +gchar *relative_url_to_absolute(const gchar *baseurl, const gchar *href) { + gchar *absolute; + gchar *prefix; + const gchar *postfix = href; + if ((href[0] == '/') && (href[1] == '/')) { + gchar *scheme = url_scheme(baseurl); + prefix = g_strconcat(scheme, ":", NULL); + g_free(scheme); + } else if (href[0] == '/') { + prefix = url_root(baseurl); + if (g_str_has_suffix(prefix, "/")) { + postfix = href+1; + } + } else if (href[0] == '?') { + prefix = url_path_including_file(baseurl); + } else if (href[0] =='#') { + prefix = url_path_and_query(baseurl); + } else if (strstr(href, "://") == NULL) { + prefix = url_path_dirname(baseurl); + } else { + // href is absolute + prefix = NULL; + } + + if (prefix) { + absolute = g_strconcat(prefix, postfix, NULL); + g_free(prefix); + } else { + absolute = g_strdup(href); + } + + return absolute; +} + +gchar *url_scheme(const gchar *url) { + if (!url) + return NULL; + + const gchar *scheme_end = skip_scheme(url); + if (scheme_end == url) { + // no scheme + return g_strdup(""); + } else { + // scheme found + // Do not include :// in the return value + g_assert(scheme_end >= url+3); + return g_strndup(url, scheme_end-3 - url); + } +} + +gchar *url_root(const gchar *url) { + if (!url) + return NULL; + + const gchar *authority = skip_scheme(url); + size_t authority_len = strcspn(authority, "/?#"); + const gchar *authority_end = authority + authority_len; + gchar *root_without_slash = g_strndup(url, authority_end - url); + gchar *root = g_strconcat(root_without_slash, "/", NULL); + g_free(root_without_slash); + return root; +} + +gchar *url_path_including_file(const gchar *url) { + if (!url) + return NULL; + + const gchar *scheme_end = skip_scheme(url); + size_t path_len = strcspn(scheme_end, "?#"); + const gchar *end = scheme_end + path_len; + gchar *path = g_strndup(url, end - url); + if (memchr(scheme_end, '/', path_len) == NULL) { + gchar *path2 = g_strconcat(path, "/", NULL); + g_free(path); + path = path2; + } + + return path; +} + +gchar *url_path_dirname(const gchar *url) { + if (!url) + return NULL; + + const gchar *scheme_end = skip_scheme(url); + size_t path_len = strcspn(scheme_end, "?#"); + const gchar *p = scheme_end + path_len; + while ((p >= url) && (*p != '/')) { + p--; + } + + if (*p == '/') { + return g_strndup(url, (p+1) - url); + } else { + return g_strdup("/"); + } +} + +gchar *url_path_and_query(const gchar *url) { + if (!url) + return NULL; + + const gchar *scheme_end = skip_scheme(url); + size_t path_len = strcspn(scheme_end, "#"); + const gchar *end = scheme_end + path_len; + return g_strndup(url, end - url); +} + +const gchar *skip_scheme(const char *url) { + const gchar *c = url; + while (is_scheme_character(*c)) { + c++; + } + + if (strncmp(c, "://", 3) == 0) { + // scheme found + return c + 3; + } else { + // schemeless url + return url; + } +} + +gboolean is_scheme_character(gchar c) { + return g_ascii_isalnum(c) || (c == '+') || (c == '-') || (c == '.'); +} |