aboutsummaryrefslogtreecommitdiff
path: root/libstore/argp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libstore/argp.c')
-rw-r--r--libstore/argp.c425
1 files changed, 302 insertions, 123 deletions
diff --git a/libstore/argp.c b/libstore/argp.c
index 86b7eae2..6ed79964 100644
--- a/libstore/argp.c
+++ b/libstore/argp.c
@@ -1,9 +1,7 @@
/* Store argument parsing
- Copyright (C) 1996 Free Software Foundation, Inc.
-
- Written by Miles Bader <miles@gnu.ai.mit.edu>
-
+ Copyright (C) 1996,97,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
This file is part of the GNU Hurd.
The GNU Hurd is free software; you can redistribute it and/or
@@ -18,190 +16,371 @@
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.,
- 675 Mass Ave, Cambridge, MA 02139, USA. */
+ 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
-#include <stdlib.h>
#include <string.h>
-#include <fcntl.h>
+#include <assert.h>
#include <hurd.h>
#include <argp.h>
-#include <error.h>
+#include <argz.h>
#include "store.h"
+/* We use this class variable instead of just the name so that we ensure
+ linking in store_open to define it. */
+#define DEFAULT_STORE_CLASS store_query_class
+
static const struct argp_option options[] = {
- {"machdev", 'm', 0, 0, "DEVICE is a mach device, not a file"},
- {"interleave", 'i', "BLOCKS", 0, "Interleave in runs of length BLOCKS"},
- {"layer", 'l', 0, 0, "Layer multiple devices for redundancy"},
+ {"store-type",'T', "TYPE", 0, "Each DEVICE names a store of type TYPE"},
+ {"machdev", 'm', 0, OPTION_HIDDEN}, /* deprecated */
+ {"interleave",'I', "BLOCKS", 0, "Interleave in runs of length BLOCKS"},
+ {"layer", 'L', 0, 0, "Layer multiple devices for redundancy"},
{0}
};
static const char args_doc[] = "DEVICE...";
-static const char doc[] = "If multiple DEVICEs are specified, they are"
-" concatenated unless either --interleave or --layer is specified (mutually"
-" exlusive).";
+static const char doc[] = "\vIf neither --interleave or --layer is specified,"
+" multiple DEVICEs are concatenated.";
-/* Used to hold data during argument parsing. */
-struct store_parse_hook
+struct store_parsed
{
- /* A malloced vector of stores specified on the command line, NUM_STORES
- long. */
- struct store **stores;
- size_t num_stores;
+ /* Names of devices parsed. */
+ char *names;
+ size_t names_len;
+
+ /* Prefix that should be applied to each member of NAMES. */
+ char *name_prefix;
- /* Pointer to params struct passed in by user. */
- struct store_argp_params *params;
+ /* --store-type specified. This defaults to the `query' type. */
+ const struct store_class *type;
- off_t interleave; /* --interleave value */
- int machdev : 1; /* --machdev specified */
+ /* A vector of class pointers used to lookup class names. Defaults to
+ STORE_STD_CLASSES. */
+ const struct store_class *const *classes;
+
+ /* DEFAULT_TYPE field passed to parser. */
+ const struct store_class *default_type;
+
+ store_offset_t interleave; /* --interleave value */
int layer : 1; /* --layer specified */
};
-/* Free the parse hook H. If FREE_STORES is true, also free the stores in
- H's store vector, otherwise just free the vector itself. */
-static void
-free_hook (struct store_parse_hook *h, int free_stores)
+void
+store_parsed_free (struct store_parsed *parsed)
{
- int i;
- if (free_stores)
- for (i = 0; i < h->num_stores; i++)
- store_free (h->stores[i]);
- if (h->stores)
- free (h->stores);
- free (h);
+ if (parsed->names_len > 0)
+ free (parsed->names);
+ if (parsed->name_prefix)
+ free (parsed->name_prefix);
+ free (parsed);
}
-
-static error_t
-open_file (char *name, struct store_parse_hook *h, struct store **s)
+
+/* Add the arguments PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_append_args (const struct store_parsed *parsed,
+ char **args, size_t *args_len)
{
- error_t err;
- int flags = h->params->flags;
- int open_flags = (flags & STORE_HARD_READONLY) ? O_RDONLY : O_RDWR;
- file_t node = file_name_lookup (name, open_flags, 0);
+ char buf[40];
+ error_t err = 0;
+ size_t num_names = argz_count (parsed->names, parsed->names_len);
- if (node == MACH_PORT_NULL)
- return errno;
+ if (!err && num_names > 1 && (parsed->interleave || parsed->layer))
+ {
+ if (parsed->interleave)
+ snprintf (buf, sizeof buf, "--interleave=%Ld", parsed->interleave);
+ else
+ snprintf (buf, sizeof buf, "--layer=%d", parsed->layer);
+ err = argz_add (args, args_len, buf);
+ }
- err = store_create (node, flags, 0, s);
- if (err)
+ if (!err && parsed->type != parsed->default_type)
{
- if (! h->params->no_file_io)
- /* Try making a store that does file io to NODE. */
- err = store_file_create (node, flags, s);
- if (err)
- mach_port_deallocate (mach_task_self (), node);
+ if (parsed->name_prefix)
+ /* A name prefix of "PFX" is equivalent to appending ":PFX" to the
+ type name. */
+ {
+ size_t npfx_len = strlen (parsed->name_prefix);
+ char tname[strlen ("--store-type=")
+ + strlen (parsed->type->name) + 1 + npfx_len + 1];
+ snprintf (tname, sizeof tname, "--store-type=%s:%.*s",
+ parsed->type->name, (int) npfx_len, parsed->name_prefix);
+ err = argz_add (args, args_len, tname);
+ }
+ else
+ /* A simple type name. */
+ {
+ snprintf (buf, sizeof buf, "--store-type=%s", parsed->type->name);
+ err = argz_add (args, args_len, buf);
+ }
}
+ if (! err)
+ err = argz_append (args, args_len, parsed->names, parsed->names_len);
+
return err;
}
-static error_t
-parse_opt (int opt, char *arg, struct argp_state *state)
+error_t
+store_parsed_name (const struct store_parsed *parsed, char **name)
{
- error_t err = 0;
- struct store_parse_hook *h = state->hook;
+ char buf[40];
+ char *pfx = 0;
+
+ if (argz_count (parsed->names, parsed->names_len) > 1)
+ {
+ if (parsed->interleave)
+ {
+ snprintf (buf, sizeof buf, "interleave(%Ld,", parsed->interleave);
+ pfx = buf;
+ }
+ else if (parsed->layer)
+ pfx = "layer(";
+ }
+
+ if (pfx)
+ *name = malloc (strlen (pfx) + parsed->names_len + 1);
+ else
+ *name = malloc (parsed->names_len);
+
+ if (! *name)
+ return ENOMEM;
+
+ if (pfx)
+ {
+ char *end = stpcpy (*name, pfx);
+ bcopy (parsed->names, end, parsed->names_len);
+ argz_stringify (end, parsed->names_len, ',');
+ strcpy (end + parsed->names_len, ")");
+ }
+ else
+ {
+ bcopy (parsed->names, *name, parsed->names_len);
+ argz_stringify (*name, parsed->names_len, ',');
+ }
+
+ return 0;
+}
+
+/* Open PARSED, and return the corresponding store in STORE. */
+error_t
+store_parsed_open (const struct store_parsed *parsed, int flags,
+ struct store **store)
+{
+ size_t pfx_len = parsed->name_prefix ? strlen (parsed->name_prefix) : 0;
+ size_t num = argz_count (parsed->names, parsed->names_len);
+
+ error_t open (char *name, struct store **store)
+ {
+ const struct store_class *type = parsed->type;
+ if (type->open)
+ {
+ if (parsed->name_prefix)
+ /* If there's a name prefix, we prefix any names we open with that
+ and a colon. */
+ {
+ char pfxed_name[pfx_len + 1 + strlen (name) + 1];
+ stpcpy (stpcpy (stpcpy (pfxed_name, parsed->name_prefix),
+ ":"),
+ name);
+ return (*type->open) (pfxed_name, flags, parsed->classes, store);
+ }
+ else
+ return (*type->open) (name, flags, parsed->classes, store);
+ }
+ else
+ return EOPNOTSUPP;
+ }
+
+ if (num == 1)
+ return open (parsed->names, store);
+ else if (num == 0)
+ return open (0, store);
+ else
+ {
+ int i;
+ char *name;
+ error_t err = 0;
+ struct store **stores = malloc (sizeof (struct store *) * num);
+
+ if (! stores)
+ return ENOMEM;
- /* Print a parsing error message and (if exiting is turned off) return the
- error code ERR. */
+ for (i = 0, name = parsed->names;
+ !err && i < num;
+ i++, name = argz_next (parsed->names, parsed->names_len, name))
+ err = open (name, &stores[i]);
+
+ if (! err)
+ {
+ if (parsed->interleave)
+ err =
+ store_ileave_create (stores, num, parsed->interleave,
+ flags, store);
+ else if (parsed->layer)
+ assert (! parsed->layer);
+ else
+ err = store_concat_create (stores, num, flags, store);
+ }
+
+ if (err)
+ {
+ while (i > 0)
+ store_free (stores[i--]);
+ free (stores);
+ }
+
+ return err;
+ }
+}
+
+static const struct store_class *
+find_class (const char *name, const struct store_class *const *const classes)
+{
+ const struct store_class *const *cl;
+ for (cl = classes ?: __start_store_std_classes;
+ classes ? *cl != 0 : cl < __stop_store_std_classes;
+ ++cl)
+ if ((*cl)->name && strcmp (name, (*cl)->name) == 0)
+ return *cl;
+
+# pragma weak store_module_find_class
+ if (! classes && store_module_find_class)
+ {
+ const struct store_class *cl;
+ if (store_module_find_class (name, strchr (name, '\0'), &cl) == 0)
+ return cl;
+ }
+
+ return 0;
+}
+
+/* Print a parsing error message and (if exiting is turned off) return the
+ error code ERR. Requires a variable called STATE to be in scope. */
#define PERR(err, fmt, args...) \
do { argp_error (state, fmt , ##args); return err; } while (0)
- switch (opt)
+/* Parse a --store-type/-T option. */
+static error_t
+parse_type (char *arg, struct argp_state *state, struct store_parsed *parsed)
+{
+ char *name_prefix = 0;
+ char *type_name = arg;
+ const struct store_class *type;
+ char *class_sep = strchr (arg, ':');
+
+ if (class_sep)
+ /* A `:'-separated class name "T1:T2" is equivalent to prepending "T2:"
+ to the device name passed to T1, and is useful for the case where T1
+ takes typed names of the form "T:NAME". A trailing `:', like "T1:" is
+ equivalent to prefixing `:' to the device name, which causes NAME to
+ be opened with store_open, as a file. */
{
- struct store *s;
+ type_name = strndupa (arg, class_sep - arg);
+ name_prefix = class_sep + 1;
+ }
+
+ type = find_class (type_name, parsed->classes);
+ if (!type || !type->open)
+ PERR (EINVAL, "%s: Invalid argument to --store-type", arg);
+ else if (type != parsed->type && parsed->type != parsed->default_type)
+ PERR (EINVAL, "--store-type specified multiple times");
+
+ parsed->type = type;
+ parsed->name_prefix = name_prefix;
+
+ return 0;
+}
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t err;
+ struct store_parsed *parsed = state->hook;
+ switch (opt)
+ {
case 'm':
- h->machdev = 1; break;
+ arg = "device";
+ /* fall through */
+ case 'T':
+ return parse_type (arg, state, parsed);
- case 'i':
- if (h->layer)
+ case 'I':
+ if (parsed->layer)
PERR (EINVAL, "--layer and --interleave are exclusive");
- if (h->interleave)
+ if (parsed->interleave)
/* Actually no reason why we couldn't support this.... */
PERR (EINVAL, "--interleave specified multiple times");
- h->interleave = atoi (arg);
- if (! h->interleave)
+ parsed->interleave = atoi (arg);
+ if (! parsed->interleave)
PERR (EINVAL, "%s: Bad value for --interleave", arg);
break;
- case 'l':
- if (h->interleave)
+ case 'L':
+#if 1
+ argp_failure (state, 5, 0, "--layer not implemented");
+ return EINVAL;
+#else
+ if (parsed->interleave)
PERR (EINVAL, "--layer and --interleave are exclusive");
- h->layer = 1;
+ parsed->layer = 1;
+#endif
break;
case ARGP_KEY_ARG:
/* A store device to use! */
- if (h->machdev)
- err = store_device_open (arg, h->params->flags, &s);
+ if (parsed->type->validate_name)
+ err = (*parsed->type->validate_name) (arg, parsed->classes);
else
- err = open_file (arg, h, &s);
+ err = 0;
+ if (! err)
+ err = argz_add (&parsed->names, &parsed->names_len, arg);
if (err)
- {
- argp_failure (state, 1, err, "%s", arg);
- return err;
- }
- else
- {
- struct store **stores = realloc (h->stores, h->num_stores + 1);
- if (stores)
- {
- stores[h->num_stores++] = s;
- h->stores = stores;
- }
- else
- return ENOMEM; /* Just fucking lovely */
- }
+ argp_failure (state, 1, err, "%s", arg);
+ return err;
break;
case ARGP_KEY_INIT:
/* Initialize our parsing state. */
- if (! state->input)
- return EINVAL; /* Need at least a way to return a result. */
- h = malloc (sizeof (struct store_parse_hook));
- if (! h)
- return ENOMEM;
- bzero (h, sizeof (struct store_parse_hook));
- h->params = state->input;
- state->hook = h;
+ {
+ struct store_argp_params *params = state->input;
+ if (! params)
+ return EINVAL; /* Need at least a way to return a result. */
+ parsed = state->hook = malloc (sizeof (struct store_parsed));
+ if (! parsed)
+ return ENOMEM;
+ bzero (parsed, sizeof (struct store_parsed));
+ parsed->classes = params->classes;
+ parsed->default_type =
+ find_class (params->default_type ?: DEFAULT_STORE_CLASS.name,
+ parsed->classes);
+ if (! parsed->default_type)
+ {
+ free (parsed);
+ return EINVAL;
+ }
+ parsed->type = parsed->default_type;
+ }
break;
case ARGP_KEY_ERROR:
- /* Parsing error occured, free everything. */
- free_hook (h, 1); break;
+ /* Parsing error occurred, free everything. */
+ store_parsed_free (parsed); break;
case ARGP_KEY_SUCCESS:
/* Successfully finished parsing, return a result. */
-
- if (h->num_stores == 0)
+ if (parsed->names == 0
+ && (!parsed->type->validate_name
+ || (*parsed->type->validate_name) (0, parsed->classes) != 0))
{
- free_hook (h, 1);
- PERR (EINVAL, "No store specified");
+ struct store_argp_params *params = state->input;
+ store_parsed_free (parsed);
+ if (!params->store_optional)
+ PERR (EINVAL, "No store specified");
+ parsed = 0;
}
-
- if (state->input == 0)
- /* No way to return a value! */
- err = EINVAL;
- else if (h->num_stores == 1)
- s = h->stores[0]; /* Just a single store. */
- else if (h->interleave)
- err =
- store_ileave_create (h->stores, h->num_stores, h->interleave,
- h->params->flags, &s);
- else if (h->layer)
- {
- free_hook (h, 1);
- PERR (EINVAL, "--layer not implemented");
- }
- else
- err =
- store_concat_create (h->stores, h->num_stores, h->params->flags, &s);
-
- free_hook (h, err);
- if (! err)
- h->params->result = s;
-
+ ((struct store_argp_params *)state->input)->result = parsed;
break;
default: