[Libguestfs] [PATCH 10/67] fish: Split off URI handling (for -a argument) from general options parsing.

Richard W.M. Jones rjones at redhat.com
Sat Aug 24 11:04:10 UTC 2013


From: "Richard W.M. Jones" <rjones at redhat.com>

This is so we will be able to reuse the same code in the OCaml tools.

This is just code motion.

(cherry picked from commit 4d7c14fdbb372d6ad5ef729b6969078b5fdb68da)
---
 align/Makefile.am          |   4 +-
 cat/Makefile.am            |   4 +-
 cat/virt-cat.c             |   6 +-
 cat/virt-ls.c              |   6 +-
 df/Makefile.am             |   4 +-
 df/main.c                  |   6 +-
 edit/Makefile.am           |   4 +-
 edit/virt-edit.c           |   6 +-
 fish/Makefile.am           |   4 +-
 fish/fish.c                |   6 +-
 fish/options.c             | 283 ++++------------------------------------
 fish/uri.c                 | 318 +++++++++++++++++++++++++++++++++++++++++++++
 fish/uri.h                 |  46 +++++++
 format/Makefile.am         |   4 +-
 fuse/Makefile.am           |   4 +-
 inspector/Makefile.am      |   4 +-
 inspector/virt-inspector.c |   6 +-
 po/POTFILES                |   1 +
 rescue/Makefile.am         |   4 +-
 rescue/virt-rescue.c       |  12 +-
 20 files changed, 456 insertions(+), 276 deletions(-)
 create mode 100644 fish/uri.c
 create mode 100644 fish/uri.h

diff --git a/align/Makefile.am b/align/Makefile.am
index 285f9f1..b243823 100644
--- a/align/Makefile.am
+++ b/align/Makefile.am
@@ -38,7 +38,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_alignment_scan_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/cat/Makefile.am b/cat/Makefile.am
index 5f6c2fc..b9df9cd 100644
--- a/cat/Makefile.am
+++ b/cat/Makefile.am
@@ -35,7 +35,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_cat_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/cat/virt-cat.c b/cat/virt-cat.c
index f752a1a..e86ecf3 100644
--- a/cat/virt-cat.c
+++ b/cat/virt-cat.c
@@ -186,7 +186,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/cat/virt-ls.c b/cat/virt-ls.c
index 3ebd633..1c449c2 100644
--- a/cat/virt-ls.c
+++ b/cat/virt-ls.c
@@ -291,7 +291,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/df/Makefile.am b/df/Makefile.am
index 1288d10..38a628c 100644
--- a/df/Makefile.am
+++ b/df/Makefile.am
@@ -33,7 +33,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_df_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/df/main.c b/df/main.c
index 5524070..0eee3cb 100644
--- a/df/main.c
+++ b/df/main.c
@@ -220,7 +220,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/edit/Makefile.am b/edit/Makefile.am
index 01828a7..9d92be7 100644
--- a/edit/Makefile.am
+++ b/edit/Makefile.am
@@ -31,7 +31,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_edit_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/edit/virt-edit.c b/edit/virt-edit.c
index 05e88e2..5690f8d 100644
--- a/edit/virt-edit.c
+++ b/edit/virt-edit.c
@@ -223,7 +223,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/fish/Makefile.am b/fish/Makefile.am
index 3e4995c..0c8ad17 100644
--- a/fish/Makefile.am
+++ b/fish/Makefile.am
@@ -70,7 +70,9 @@ SHARED_SOURCE_FILES = \
 	options.h \
 	options.c \
 	progress.h \
-	progress.c
+	progress.c \
+	uri.h \
+	uri.c
 
 guestfish_SOURCES = \
 	$(generator_built) \
diff --git a/fish/fish.c b/fish/fish.c
index 2b5a877..7ca03d1 100644
--- a/fish/fish.c
+++ b/fish/fish.c
@@ -429,7 +429,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/fish/options.c b/fish/options.c
index 5c55b38..6d63afa 100644
--- a/fish/options.c
+++ b/fish/options.c
@@ -25,22 +25,15 @@
 #include <errno.h>
 #include <libintl.h>
 
-#include <libxml/uri.h>
-
-#include "c-ctype.h"
-
 #include "guestfs.h"
 #include "options.h"
-
-static int is_uri (const char *arg);
-static void parse_uri (const char *arg, const char *format, struct drv *drv);
-static char *query_get (xmlURIPtr uri, const char *search_name);
-static char **make_server (xmlURIPtr uri, const char *socket);
+#include "uri.h"
 
 /* Handle the '-a' option when passed on the command line. */
 void
 option_a (const char *arg, const char *format, struct drv **drvsp)
 {
+  struct uri uri;
   struct drv *drv;
 
   drv = calloc (1, sizeof (struct drv));
@@ -49,268 +42,39 @@ option_a (const char *arg, const char *format, struct drv **drvsp)
     exit (EXIT_FAILURE);
   }
 
-  /* Does it look like a URI? */
-  if (is_uri (arg))
-    parse_uri (arg, format, drv);
-  else {
+  if (parse_uri (arg, &uri) == -1)
+    exit (EXIT_FAILURE);
+
+  if (STREQ (uri.protocol, "file")) {
     /* Ordinary file. */
-    if (access (arg, R_OK) != 0) {
-      perror (arg);
+    if (access (uri.path, R_OK) != 0) {
+      perror (uri.path);
       exit (EXIT_FAILURE);
     }
 
     drv->type = drv_a;
     drv->nr_drives = -1;
-    drv->a.filename = (char *) arg;
+    drv->a.filename = uri.path;
     drv->a.format = format;
+
+    free (uri.protocol);
+  }
+  else {
+    /* Remote storage. */
+    drv->type = drv_uri;
+    drv->nr_drives = -1;
+    drv->uri.path = uri.path;
+    drv->uri.protocol = uri.protocol;
+    drv->uri.server = uri.server;
+    drv->uri.username = uri.username;
+    drv->uri.format = format;
+    drv->uri.orig_uri = arg;
   }
 
   drv->next = *drvsp;
   *drvsp = drv;
 }
 
-/* Does it "look like" a URI?  A short lower-case ASCII string
- * followed by "://" will do.  Note that we properly parse the URI
- * later on using libxml2.
- */
-static int
-is_uri (const char *arg)
-{
-  const char *p;
-
-  p = strstr (arg, "://");
-  if (!p)
-    return 0;
-
-  if (p - arg >= 8)
-    return 0;
-
-  for (p--; p >= arg; p--) {
-    if (!c_islower (*p))
-      return 0;
-  }
-
-  return 1;
-}
-
-static void
-parse_uri (const char *arg, const char *format, struct drv *drv)
-{
-  CLEANUP_XMLFREEURI xmlURIPtr uri = NULL;
-  CLEANUP_FREE char *socket = NULL;
-  char *path;
-  char *protocol;
-  char **server;
-  char *username;
-
-  uri = xmlParseURI (arg);
-  if (!uri) {
-    fprintf (stderr, _("%s: --add: could not parse URI '%s'\n"),
-             program_name, arg);
-    exit (EXIT_FAILURE);
-  }
-
-  /* Note we don't do much checking of the parsed URI, since the
-   * underlying function 'guestfs_add_drive_opts' will check for us.
-   * So just the basics here.
-   */
-  if (uri->scheme == NULL) {
-    /* Probably can never happen. */
-    fprintf (stderr, _("%s: --add %s: scheme of URI is NULL\n"),
-             program_name, arg);
-    exit (EXIT_FAILURE);
-  }
-
-  socket = query_get (uri, "socket");
-
-  if (uri->server && STRNEQ (uri->server, "") && socket) {
-    fprintf (stderr, _("%s: --add %s: cannot both a server name and a socket query parameter\n"),
-             program_name, arg);
-    exit (EXIT_FAILURE);
-  }
-
-  /* Is this needed? XXX
-  if (socket && socket[0] != '/') {
-    fprintf (stderr, _("%s: --add %s: socket query parameter must be an absolute path\n"),
-             program_name, arg);
-    exit (EXIT_FAILURE);
-  }
-  */
-
-  protocol = strdup (uri->scheme);
-  if (protocol == NULL) {
-    perror ("strdup");
-    exit (EXIT_FAILURE);
-  }
-
-  server = make_server (uri, socket);
-
-  if (uri->user && STRNEQ (uri->user, "")) {
-    username = strdup (uri->user);
-    if (!username) {
-      perror ("username");
-      exit (EXIT_FAILURE);
-    }
-  }
-  else username = NULL;
-
-  path = strdup (uri->path ? uri->path : "");
-  if (!path) {
-    perror ("path");
-    exit (EXIT_FAILURE);
-  }
-
-  drv->type = drv_uri;
-  drv->nr_drives = -1;
-  drv->uri.path = path;
-  drv->uri.protocol = protocol;
-  drv->uri.server = server;
-  drv->uri.username = username;
-  drv->uri.format = format;
-  drv->uri.orig_uri = arg;
-}
-
-/* Code inspired by libvirt src/util/viruri.c, written by danpb,
- * released under a compatible license.
- */
-static char *
-query_get (xmlURIPtr uri, const char *search_name)
-{
-  /* XXX libvirt uses deprecated uri->query field.  Why? */
-  const char *query = uri->query_raw;
-  const char *end, *eq;
-
-  if (!query || STREQ (query, ""))
-    return NULL;
-
-  while (*query) {
-    CLEANUP_FREE char *name = NULL;
-    char *value = NULL;
-
-    /* Find the next separator, or end of the string. */
-    end = strchr (query, '&');
-    if (!end)
-      end = strchr(query, ';');
-    if (!end)
-      end = query + strlen (query);
-
-    /* Find the first '=' character between here and end. */
-    eq = strchr(query, '=');
-    if (eq && eq >= end) eq = NULL;
-
-    /* Empty section (eg. "&&"). */
-    if (end == query)
-      goto next;
-
-    /* If there is no '=' character, then we have just "name"
-     * and consistent with CGI.pm we assume value is "".
-     */
-    else if (!eq) {
-      name = xmlURIUnescapeString (query, end - query, NULL);
-      if (!name) goto no_memory;
-    }
-    /* Or if we have "name=" here (works around annoying
-     * problem when calling xmlURIUnescapeString with len = 0).
-     */
-    else if (eq+1 == end) {
-      name = xmlURIUnescapeString (query, eq - query, NULL);
-      if (!name) goto no_memory;
-    }
-    /* If the '=' character is at the beginning then we have
-     * "=value" and consistent with CGI.pm we _ignore_ this.
-     */
-    else if (query == eq)
-      goto next;
-
-    /* Otherwise it's "name=value". */
-    else {
-      name = xmlURIUnescapeString (query, eq - query, NULL);
-      if (!name)
-        goto no_memory;
-      value = xmlURIUnescapeString (eq+1, end - (eq+1), NULL);
-      if (!value) {
-        goto no_memory;
-      }
-    }
-
-    /* Is it the name we're looking for? */
-    if (STREQ (name, search_name)) {
-      if (!value) {
-        value = strdup ("");
-        if (!value)
-          goto no_memory;
-      }
-      return value;
-    }
-
-    free (value);
-
-  next:
-    query = end;
-    if (*query)
-      query++; /* skip '&' separator */
-  }
-
-  /* search_name not found */
-  return NULL;
-
- no_memory:
-  perror ("malloc");
-  exit (EXIT_FAILURE);
-}
-
-/* Construct either a tcp: server list of a unix: server list or
- * nothing at all from '-a' option URI.
- */
-static char **
-make_server (xmlURIPtr uri, const char *socket)
-{
-  char **ret;
-  char *server;
-
-  /* If the server part of the URI is specified, then this is a TCP
-   * connection.
-   */
-  if (uri->server && STRNEQ (uri->server, "")) {
-    if (uri->port == 0) {
-      if (asprintf (&server, "tcp:%s", uri->server) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
-    }
-    else {
-      if (asprintf (&server, "tcp:%s:%d", uri->server, uri->port) == -1) {
-        perror ("asprintf");
-        exit (EXIT_FAILURE);
-      }
-    }
-  }
-  /* Otherwise, ?socket query parameter means it's a Unix domain
-   * socket connection.
-   */
-  else if (socket != NULL) {
-    if (asprintf (&server, "unix:%s", socket) == -1) {
-      perror ("asprintf");
-      exit (EXIT_FAILURE);
-    }
-  }
-  /* Otherwise, no server parameter is needed. */
-  else return NULL;
-
-  /* The .server parameter is in fact a list of strings, although
-   * only a singleton is passed by us.
-   */
-  ret = malloc (sizeof (char *) * 2);
-  if (ret == NULL) {
-    perror ("malloc");
-    exit (EXIT_FAILURE);
-  }
-  ret[0] = server;
-  ret[1] = NULL;
-
-  return ret;
-}
-
 char
 add_drives (struct drv *drv, char next_drive)
 {
@@ -488,7 +252,8 @@ free_drives (struct drv *drv)
 
   switch (drv->type) {
   case drv_a:
-    /* a.filename and a.format are optargs, don't free them */
+    free (drv->a.filename);
+    /* a.format is an optarg, so don't free it */
     break;
   case drv_uri:
     free (drv->uri.path);
diff --git a/fish/uri.c b/fish/uri.c
new file mode 100644
index 0000000..9c0892f
--- /dev/null
+++ b/fish/uri.c
@@ -0,0 +1,318 @@
+/* libguestfs - mini library for parsing -a URI parameters
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libintl.h>
+
+#include <libxml/uri.h>
+
+#include "c-ctype.h"
+
+#include "guestfs.h"
+#include "guestfs-internal-frontend.h"
+#include "uri.h"
+
+static int is_uri (const char *arg);
+static int parse (const char *arg, char **path_ret, char **protocol_ret, char ***server_ret, char **username_ret);
+static char *query_get (xmlURIPtr uri, const char *search_name);
+static int make_server (xmlURIPtr uri, const char *socket, char ***ret);
+
+int
+parse_uri (const char *arg, struct uri *uri_ret)
+{
+  char *path;
+  char *protocol;
+  char **server;
+  char *username;
+
+  /* Does it look like a URI? */
+  if (is_uri (arg)) {
+    if (parse (arg, &path, &protocol, &server, &username) == -1)
+      return -1;
+  }
+  else {
+    /* Ordinary file. */
+    path = strdup (arg);
+    if (!path) {
+      perror ("strdup");
+      return -1;
+    }
+    protocol = strdup ("file");
+    if (!protocol) {
+      perror ("strdup");
+      free (path);
+      return -1;
+    }
+    server = NULL;
+    username = NULL;
+  }
+
+  uri_ret->path = path;
+  uri_ret->protocol = protocol;
+  uri_ret->server = server;
+  uri_ret->username = username;
+  return 0;
+}
+
+/* Does it "look like" a URI?  A short lower-case ASCII string
+ * followed by "://" will do.  Note that we properly parse the URI
+ * later on using libxml2.
+ */
+static int
+is_uri (const char *arg)
+{
+  const char *p;
+
+  p = strstr (arg, "://");
+  if (!p)
+    return 0;
+
+  if (p - arg >= 8)
+    return 0;
+
+  for (p--; p >= arg; p--) {
+    if (!c_islower (*p))
+      return 0;
+  }
+
+  return 1;
+}
+
+static int
+parse (const char *arg, char **path_ret, char **protocol_ret,
+           char ***server_ret, char **username_ret)
+{
+  CLEANUP_XMLFREEURI xmlURIPtr uri = NULL;
+  CLEANUP_FREE char *socket = NULL;
+
+  uri = xmlParseURI (arg);
+  if (!uri) {
+    fprintf (stderr, _("%s: --add: could not parse URI '%s'\n"),
+             program_name, arg);
+    return -1;
+  }
+
+  /* Note we don't do much checking of the parsed URI, since the
+   * underlying function 'guestfs_add_drive_opts' will check for us.
+   * So just the basics here.
+   */
+  if (uri->scheme == NULL || STREQ (uri->scheme, "")) {
+    /* Probably can never happen. */
+    fprintf (stderr, _("%s: %s: scheme of URI is NULL or empty\n"),
+             program_name, arg);
+    return -1;
+  }
+
+  socket = query_get (uri, "socket");
+
+  if (uri->server && STRNEQ (uri->server, "") && socket) {
+    fprintf (stderr, _("%s: %s: cannot both a server name and a socket query parameter\n"),
+             program_name, arg);
+    return -1;
+  }
+
+  /* Is this needed? XXX
+  if (socket && socket[0] != '/') {
+    fprintf (stderr, _("%s: --add %s: socket query parameter must be an absolute path\n"),
+             program_name, arg);
+    return -1;
+  }
+  */
+
+  *protocol_ret = strdup (uri->scheme);
+  if (*protocol_ret == NULL) {
+    perror ("strdup");
+    return -1;
+  }
+
+  if (make_server (uri, socket, server_ret) == -1) {
+    free (*protocol_ret);
+    return -1;
+  }
+
+  if (uri->user && STRNEQ (uri->user, "")) {
+    *username_ret = strdup (uri->user);
+    if (*username_ret == NULL) {
+      perror ("username");
+      free (*protocol_ret);
+      guestfs___free_string_list (*server_ret);
+      return -1;
+    }
+  }
+  else *username_ret = NULL;
+
+  *path_ret = strdup (uri->path ? uri->path : "");
+  if (!*path_ret) {
+    perror ("path");
+    free (*protocol_ret);
+    guestfs___free_string_list (*server_ret);
+    free (*username_ret);
+    return -1;
+  }
+
+  return 0;
+}
+
+/* Code inspired by libvirt src/util/viruri.c, written by danpb,
+ * released under a compatible license.
+ */
+static char *
+query_get (xmlURIPtr uri, const char *search_name)
+{
+  /* XXX libvirt uses deprecated uri->query field.  Why? */
+  const char *query = uri->query_raw;
+  const char *end, *eq;
+
+  if (!query || STREQ (query, ""))
+    return NULL;
+
+  while (*query) {
+    CLEANUP_FREE char *name = NULL;
+    char *value = NULL;
+
+    /* Find the next separator, or end of the string. */
+    end = strchr (query, '&');
+    if (!end)
+      end = strchr(query, ';');
+    if (!end)
+      end = query + strlen (query);
+
+    /* Find the first '=' character between here and end. */
+    eq = strchr(query, '=');
+    if (eq && eq >= end) eq = NULL;
+
+    /* Empty section (eg. "&&"). */
+    if (end == query)
+      goto next;
+
+    /* If there is no '=' character, then we have just "name"
+     * and consistent with CGI.pm we assume value is "".
+     */
+    else if (!eq) {
+      name = xmlURIUnescapeString (query, end - query, NULL);
+      if (!name) goto no_memory;
+    }
+    /* Or if we have "name=" here (works around annoying
+     * problem when calling xmlURIUnescapeString with len = 0).
+     */
+    else if (eq+1 == end) {
+      name = xmlURIUnescapeString (query, eq - query, NULL);
+      if (!name) goto no_memory;
+    }
+    /* If the '=' character is at the beginning then we have
+     * "=value" and consistent with CGI.pm we _ignore_ this.
+     */
+    else if (query == eq)
+      goto next;
+
+    /* Otherwise it's "name=value". */
+    else {
+      name = xmlURIUnescapeString (query, eq - query, NULL);
+      if (!name)
+        goto no_memory;
+      value = xmlURIUnescapeString (eq+1, end - (eq+1), NULL);
+      if (!value) {
+        goto no_memory;
+      }
+    }
+
+    /* Is it the name we're looking for? */
+    if (STREQ (name, search_name)) {
+      if (!value) {
+        value = strdup ("");
+        if (!value)
+          goto no_memory;
+      }
+      return value;
+    }
+
+    free (value);
+
+  next:
+    query = end;
+    if (*query)
+      query++; /* skip '&' separator */
+  }
+
+  /* search_name not found */
+  return NULL;
+
+ no_memory:
+  perror ("malloc");
+  return NULL;
+}
+
+/* Construct either a tcp: server list of a unix: server list or
+ * nothing at all from '-a' option URI.
+ */
+static int
+make_server (xmlURIPtr uri, const char *socket, char ***ret)
+{
+  char *server;
+
+  /* If the server part of the URI is specified, then this is a TCP
+   * connection.
+   */
+  if (uri->server && STRNEQ (uri->server, "")) {
+    if (uri->port == 0) {
+      if (asprintf (&server, "tcp:%s", uri->server) == -1) {
+        perror ("asprintf");
+        return -1;
+      }
+    }
+    else {
+      if (asprintf (&server, "tcp:%s:%d", uri->server, uri->port) == -1) {
+        perror ("asprintf");
+        return -1;
+      }
+    }
+  }
+  /* Otherwise, ?socket query parameter means it's a Unix domain
+   * socket connection.
+   */
+  else if (socket != NULL) {
+    if (asprintf (&server, "unix:%s", socket) == -1) {
+      perror ("asprintf");
+      return -1;
+    }
+  }
+  /* Otherwise, no server parameter is needed. */
+  else {
+    *ret = NULL;
+    return 0;
+  }
+
+  /* The .server parameter is in fact a list of strings, although
+   * only a singleton is passed by us.
+   */
+  *ret = malloc (sizeof (char *) * 2);
+  if (*ret == NULL) {
+    perror ("malloc");
+    return -1;
+  }
+  (*ret)[0] = server;
+  (*ret)[1] = NULL;
+
+  return 0;
+}
diff --git a/fish/uri.h b/fish/uri.h
new file mode 100644
index 0000000..420d20c
--- /dev/null
+++ b/fish/uri.h
@@ -0,0 +1,46 @@
+/* libguestfs - mini library for parsing -a URI parameters
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <config.h>
+
+#ifndef FISH_URI_H
+#define FISH_URI_H
+
+struct uri {
+  char *path;                   /* disk path */
+  char *protocol;               /* protocol (eg. "file", "nbd") */
+  char **server;                /* server(s) - can be NULL */
+  char *username;               /* username - can be NULL */
+};
+
+/* Parse the '-a' option parameter 'arg', and place the result in
+ * '*uri_ret'.
+ *
+ * If it doesn't look like a URI then uri_ret->path will be the same
+ * as 'arg' (copied) and uri_ret->protocol will be "file".
+ *
+ * If it looks like a URI and can be parsed, then the other fields will
+ * be filled in as appropriate.
+ *
+ * The caller should free the fields from the struct after use.
+ *
+ * Returns 0 if parsing went OK, or -1 if there was an error.
+ */
+extern int parse_uri (const char *arg, struct uri *uri_ret);
+
+#endif /* FISH_URI_H */
diff --git a/format/Makefile.am b/format/Makefile.am
index a0d1ed4..c93edc9 100644
--- a/format/Makefile.am
+++ b/format/Makefile.am
@@ -30,7 +30,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_format_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
index 6335bbb..d10ff7d 100644
--- a/fuse/Makefile.am
+++ b/fuse/Makefile.am
@@ -42,7 +42,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 # guestmount
 
diff --git a/inspector/Makefile.am b/inspector/Makefile.am
index 1967fe8..a5aa3c1 100644
--- a/inspector/Makefile.am
+++ b/inspector/Makefile.am
@@ -57,7 +57,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_inspector_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/inspector/virt-inspector.c b/inspector/virt-inspector.c
index 146f211..a070a3a 100644
--- a/inspector/virt-inspector.c
+++ b/inspector/virt-inspector.c
@@ -199,7 +199,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
diff --git a/po/POTFILES b/po/POTFILES
index 457e129..bcefe16 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -143,6 +143,7 @@ fish/setenv.c
 fish/supported.c
 fish/tilde.c
 fish/time.c
+fish/uri.c
 format/format.c
 fuse/guestmount.c
 fuse/guestunmount.c
diff --git a/rescue/Makefile.am b/rescue/Makefile.am
index f694223..7d23ee2 100644
--- a/rescue/Makefile.am
+++ b/rescue/Makefile.am
@@ -31,7 +31,9 @@ SHARED_SOURCE_FILES = \
 	../fish/inspect.c \
 	../fish/keys.c \
 	../fish/options.h \
-	../fish/options.c
+	../fish/options.c \
+	../fish/uri.h \
+	../fish/uri.c
 
 virt_rescue_SOURCES = \
 	$(SHARED_SOURCE_FILES) \
diff --git a/rescue/virt-rescue.c b/rescue/virt-rescue.c
index 6c6e306..65dd473 100644
--- a/rescue/virt-rescue.c
+++ b/rescue/virt-rescue.c
@@ -256,7 +256,11 @@ main (int argc, char *argv[])
           exit (EXIT_FAILURE);
         }
         drv->type = drv_a;
-        drv->a.filename = argv[optind];
+        drv->a.filename = strdup (argv[optind]);
+        if (!drv->a.filename) {
+          perror ("strdup");
+          exit (EXIT_FAILURE);
+        }
         drv->next = drvs;
         drvs = drv;
       } else {                  /* simulate -d option */
@@ -570,7 +574,11 @@ add_scratch_disk (struct drv **drvs)
   }
   drv->type = drv_a;
   drv->nr_drives = -1;
-  drv->a.filename = filename;
+  drv->a.filename = strdup (filename);
+  if (!drv->a.filename) {
+    perror ("strdup");
+    exit (EXIT_FAILURE);
+  }
   drv->a.format = "raw";
   drv->next = *drvs;
   *drvs = drv;
-- 
1.8.3.1




More information about the Libguestfs mailing list