#include #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 == '.'); }