diff options
Diffstat (limited to 'libstore/argp.c')
-rw-r--r-- | libstore/argp.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/libstore/argp.c b/libstore/argp.c new file mode 100644 index 00000000..97640e19 --- /dev/null +++ b/libstore/argp.c @@ -0,0 +1,379 @@ +/* Store argument parsing + + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.ai.mit.edu> + This file is part of the GNU Hurd. + + The GNU Hurd 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, or (at + your option) any later version. + + The GNU Hurd 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., + 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <string.h> +#include <assert.h> +#include <hurd.h> +#include <argp.h> +#include <argz.h> + +#include "store.h" + +#define DEFAULT_STORE_TYPE "query" + +static const struct argp_option options[] = { + {"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[] = "\vIf neither --interleave or --layer is specified," +" multiple DEVICEs are concatenated."; + +struct store_parsed +{ + /* Names of devices parsed. */ + char *names; + size_t names_len; + + /* Prefix that should be applied to each member of NAMES. */ + char *name_prefix; + + /* --store-type specified. This defaults to the `query' type. */ + const struct store_class *type; + + /* 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; + + off_t interleave; /* --interleave value */ + int layer : 1; /* --layer specified */ +}; + +void +store_parsed_free (struct store_parsed *parsed) +{ + if (parsed->names_len > 0) + free (parsed->names); + if (parsed->name_prefix) + free (parsed->name_prefix); + free (parsed); +} + +/* 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) +{ + char buf[40]; + error_t err = 0; + size_t num_names = argz_count (parsed->names, parsed->names_len); + + 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); + } + + if (!err && parsed->type != parsed->default_type) + { + 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; +} + +error_t +store_parsed_name (const struct store_parsed *parsed, char **name) +{ + 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; + + 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 *classes) +{ + while (*classes) + if ((*classes)->name && strcmp (name, (*classes)->name) == 0) + return *classes; + else + classes++; + 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) + +/* 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. */ + { + 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': + arg = "device"; + /* fall through */ + case 'T': + return parse_type (arg, state, parsed); + + case 'I': + if (parsed->layer) + PERR (EINVAL, "--layer and --interleave are exclusive"); + if (parsed->interleave) + /* Actually no reason why we couldn't support this.... */ + PERR (EINVAL, "--interleave specified multiple times"); + + parsed->interleave = atoi (arg); + if (! parsed->interleave) + PERR (EINVAL, "%s: Bad value for --interleave", arg); + break; + + 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"); + parsed->layer = 1; +#endif + break; + + case ARGP_KEY_ARG: + /* A store device to use! */ + if (parsed->type->validate_name) + err = (*parsed->type->validate_name) (arg, parsed->classes); + else + err = 0; + if (! err) + err = argz_add (&parsed->names, &parsed->names_len, arg); + if (err) + argp_failure (state, 1, err, "%s", arg); + return err; + break; + + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + { + 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 ?: store_std_classes; + parsed->default_type = + find_class (params->default_type ?: DEFAULT_STORE_TYPE, + 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. */ + store_parsed_free (parsed); break; + + case ARGP_KEY_SUCCESS: + /* Successfully finished parsing, return a result. */ + if (parsed->names == 0 + && (!parsed->type->validate_name + || (*parsed->type->validate_name) (0, parsed->classes) != 0)) + { + store_parsed_free (parsed); + PERR (EINVAL, "No store specified"); + } + else + ((struct store_argp_params *)state->input)->result = parsed; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return 0; +} + +struct argp +store_argp = { options, parse_opt, args_doc, doc }; |