lib/url: Add pb_join_url
authorJeremy Kerr <jk@ozlabs.org>
Mon, 18 Mar 2013 08:48:19 +0000 (16:48 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Mon, 29 Apr 2013 03:55:32 +0000 (13:55 +1000)
Add a a function to join a string to a base URL

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
lib/url/url.c
lib/url/url.h
test/urls/Makefile.am
test/urls/parse-url.c

index 4e4b9610cd96f1016a6d38aca78e258f5a97452c..ae72b103de62153e2a8e779286c7a8d3a01d2a55 100644 (file)
@@ -22,6 +22,7 @@
 
 #define _GNU_SOURCE
 #include <assert.h>
+#include <stdbool.h>
 #include <string.h>
 
 #include "log/log.h"
@@ -36,6 +37,7 @@ struct pb_scheme_info {
        enum pb_url_scheme scheme;
        const char *str;
        unsigned int str_len;
+       bool has_host;
 };
 
 static const struct pb_scheme_info schemes[] = {
@@ -43,36 +45,43 @@ static const struct pb_scheme_info schemes[] = {
                .scheme = pb_url_file,
                .str = "file",
                .str_len = sizeof("file") - 1,
+               .has_host = false,
        },
        {
                .scheme = pb_url_ftp,
                .str = "ftp",
                .str_len = sizeof("ftp") - 1,
+               .has_host = true,
        },
        {
                .scheme = pb_url_http,
                .str = "http",
                .str_len = sizeof("http") - 1,
+               .has_host = true,
        },
        {
                .scheme = pb_url_https,
                .str = "https",
                .str_len = sizeof("https") - 1,
+               .has_host = true,
        },
        {
                .scheme = pb_url_nfs,
                .str = "nfs",
                .str_len = sizeof("nfs") - 1,
+               .has_host = true,
        },
        {
                .scheme = pb_url_sftp,
                .str = "sftp",
                .str_len = sizeof("sftp") - 1,
+               .has_host = true,
        },
        {
                .scheme = pb_url_tftp,
                .str = "tftp",
                .str_len = sizeof("tftp") - 1,
+               .has_host = true,
        },
 };
 
@@ -82,6 +91,21 @@ static const struct pb_scheme_info *file_scheme = &schemes[0];
  * pb_url_find_scheme - Find the pb_scheme_info for a URL string.
  */
 
+static const struct pb_scheme_info *pb_url_scheme_info(
+               enum pb_url_scheme scheme)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++) {
+               const struct pb_scheme_info *info = &schemes[i];
+
+               if (info->scheme == scheme)
+                       return info;
+
+       }
+       return NULL;
+}
+
 static const struct pb_scheme_info *pb_url_find_scheme(const char *url)
 {
        static const int sep_len = sizeof("://") - 1;
@@ -110,6 +134,23 @@ static const struct pb_scheme_info *pb_url_find_scheme(const char *url)
        return file_scheme;
 }
 
+static void pb_url_parse_path(struct pb_url *url)
+{
+       const char *p = strrchr(url->path, '/');
+
+       talloc_free(url->dir);
+       talloc_free(url->file);
+
+       if (p) {
+               p++;
+               url->dir = talloc_strndup(url, url->path, p - url->path);
+               url->file = talloc_strdup(url, p);
+       } else {
+               url->dir = NULL;
+               url->file = talloc_strdup(url, url->path);
+       }
+}
+
 /**
  * pb_url_parse - Parse a remote file URL.
  * @ctx: The talloc context to associate with the returned string.
@@ -178,16 +219,7 @@ struct pb_url *pb_url_parse(void *ctx, const char *url_str)
                url->path = talloc_strdup(url, path);
        }
 
-       p = strrchr(url->path, '/');
-
-       if (p) {
-               p++;
-               url->dir = talloc_strndup(url, url->path, p - url->path);
-               url->file = talloc_strdup(url, p);
-       } else {
-               url->dir = NULL;
-               url->file = talloc_strdup(url, url->path);
-       }
+       pb_url_parse_path(url);
 
        pb_log(" scheme %d\n", url->scheme);
        pb_log(" host '%s'\n", url->host);
@@ -203,13 +235,74 @@ fail:
        return NULL;
 }
 
-const char *pb_url_scheme_name(enum pb_url_scheme scheme)
+static bool is_url(const char *str)
 {
-       unsigned int i;
+       return strstr(str, "://") != NULL;
+}
 
-       for (i = 0; i < sizeof(schemes) / sizeof(schemes[0]); i++)
-               if (schemes[i].scheme == scheme)
-                       return schemes[i].str;
+static void pb_url_update_full(struct pb_url *url)
+{
+       const struct pb_scheme_info *scheme = pb_url_scheme_info(url->scheme);
 
-       return NULL;
+       assert(scheme);
+
+       talloc_free(url->full);
+
+       url->full = talloc_asprintf(url, "%s://%s%s", scheme->str,
+                       scheme->has_host ? url->host : "", url->path);
+}
+
+static struct pb_url *pb_url_copy(void *ctx, const struct pb_url *url)
+{
+       struct pb_url *new_url;
+
+       new_url = talloc(ctx, struct pb_url);
+       new_url->scheme = url->scheme;
+       new_url->full = talloc_strdup(url, url->full);
+
+       new_url->host = url->host ? talloc_strdup(url, url->host) : NULL;
+       new_url->port = url->port ? talloc_strdup(url, url->port) : NULL;
+       new_url->path = url->path ? talloc_strdup(url, url->path) : NULL;
+       new_url->dir  = url->dir  ? talloc_strdup(url, url->dir)  : NULL;
+       new_url->file = url->file ? talloc_strdup(url, url->file) : NULL;
+
+       return new_url;
+}
+
+struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s)
+{
+       struct pb_url *new_url;
+
+       /* complete url: just parse all info from s */
+       if (is_url(s))
+               return pb_url_parse(ctx, s);
+
+       new_url = pb_url_copy(ctx, url);
+
+       if (s[0] == '/') {
+               /* absolute path: replace path of new_url */
+               talloc_free(new_url->path);
+               new_url->path = talloc_strdup(new_url, s);
+
+       } else {
+               /* relative path: join s to existing path. We know that
+                * url->dir ends with a slash. */
+               char *tmp = new_url->path;
+               new_url->path = talloc_asprintf(new_url, "%s%s", url->dir, s);
+               talloc_free(tmp);
+       }
+
+       /* replace ->dir and ->file with components from ->path */
+       pb_url_parse_path(new_url);
+
+       /* and re-generate the full URL */
+       pb_url_update_full(new_url);
+
+       return new_url;
+}
+
+const char *pb_url_scheme_name(enum pb_url_scheme scheme)
+{
+       const struct pb_scheme_info *info = pb_url_scheme_info(scheme);
+       return info ? info->str : NULL;
 }
index 3cb7cd876b0ac228c8c76570944eb4c4ef116ffa..40c11645877907d67617c23866799d1c6b3010df 100644 (file)
@@ -59,6 +59,7 @@ struct pb_url {
 };
 
 struct pb_url *pb_url_parse(void *ctx, const char *url_str);
+struct pb_url *pb_url_join(void *ctx, const struct pb_url *url, const char *s);
 
 const char *pb_url_scheme_name(enum pb_url_scheme scheme);
 
index d16ffdc21547a51b0db669a159626784d09da4ae..21630181e8398b9bb2a0e3deb8da2c3fd44119af 100644 (file)
@@ -30,7 +30,11 @@ check_PROGRAMS = parse-url
 check_SCRIPTS = run-url-test
 
 TESTS = data/double-slash.test \
-       data/http-simple.test
+       data/http-simple.test \
+       data/join-full.test \
+       data/join-absolute.test \
+       data/join-relative.test
+
 TEST_EXTENSIONS = .test
 TEST_LOG_COMPILER = $(builddir)/run-url-test
 
index d748cdb2277d7d5b4fe834c76c40443a06fc1b6c..cfa67628a96b63ccc564c85adc81d1b9283240bd 100644 (file)
@@ -10,8 +10,8 @@ int main(int argc, char **argv)
        struct pb_url *url;
        FILE *null;
 
-       if (argc != 2) {
-               fprintf(stderr, "Usage: %s <URL>\n", argv[0]);
+       if (argc != 2 && argc != 3) {
+               fprintf(stderr, "Usage: %s <URL> [update]\n", argv[0]);
                return EXIT_FAILURE;
        }
 
@@ -23,7 +23,14 @@ int main(int argc, char **argv)
        if (!url)
                return EXIT_FAILURE;
 
-       printf("%s\n", argv[1]);
+       if (argc == 2) {
+               printf("%s\n", argv[1]);
+
+       } else {
+               printf("%s %s\n", argv[1], argv[2]);
+               url = pb_url_join(NULL, url, argv[2]);
+       }
+
        printf("scheme\t%s\n", pb_url_scheme_name(url->scheme));
        printf("host\t%s\n", url->host);
        printf("port\t%s\n", url->port);