diff options
Diffstat (limited to 'libstore/argp.c')
-rw-r--r-- | libstore/argp.c | 425 |
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: |