summaryrefslogtreecommitdiff
path: root/src/libwebvi/urlutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libwebvi/urlutils.c')
-rw-r--r--src/libwebvi/urlutils.c132
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 == '.');
+}