From 81c2cb5e36bbdd58717f7ca69bc28d92dba0e492 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 13 Mar 2011 13:55:03 +1100 Subject: [PATCH] Add 'choice' argument type. A 'choice' must be selected from a set list of options. Signed-off-by: NeilBrown --- tools/lafs.c | 91 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/tools/lafs.c b/tools/lafs.c index af8e00b..b3d8367 100644 --- a/tools/lafs.c +++ b/tools/lafs.c @@ -65,20 +65,21 @@ struct state { * types are: * flag: expect --tagname This is either present or not. * opaque: An uninterpreted string, often a number. + * choice: On of a defined list of strings. * external: An external filename - completion is possible. * internal: An internal filename - completion might be possible. * subcommand: one of a list of subcommands. * Any unique prefix of a tag is allowed to match. */ -enum argtype { flag, opaque, external, internal, subcommand, terminal }; +enum argtype { flag, opaque, choice, external, internal, subcommand, terminal }; static struct args { char *tag; enum argtype type; int pos; - struct cmd *subcmd; + union { struct cmd *subcmd; char **options; }; char *desc; } lafs_args[]; -#define TERMINAL_ARG {NULL, terminal, 0, NULL, NULL} +#define TERMINAL_ARG {NULL, terminal, 0, {NULL}, NULL} /* When a positional parameter is identified as 'subcommand' it is associated * with a list of 'struct cmd' identifying the possible subcommands. @@ -139,6 +140,17 @@ static int find_tag(struct args *args, const char *tag, int len) return best; } +/* Find an option in the list, return position. + * No prefixing is allowed + */ +static int find_option(char **options, char *w) +{ + int i; + for (i = 0; options[i]; i++) + if (strcmp(options[i], w) == 0) + return i; + return -1; +} /* Return the first word on the line, modifying the line in-place and * updating *line to be ready to get the next word. @@ -226,7 +238,7 @@ static void **parse_line(struct args **argsp, char *line, int *offsetp, rv = calloc(i+offset, sizeof(char*)); size = i+offset; - while ((w = take_word(&line)) != NULL) { + while (!*error && (w = take_word(&line)) != NULL) { if (*w == '-') { /* Find the matching tag. */ char *t = w, *e; @@ -271,8 +283,17 @@ static void **parse_line(struct args **argsp, char *line, int *offsetp, * parse the remaining args in the context of the * given subcommand - if it exists. */ - if (args[i].type == subcommand) { - struct cmd *c = find_cmd(args[i].subcmd, w); + switch(args[i].type) { + struct cmd *c; + int o; + default: break; + case choice: + o = find_option(args[i].options, w); + if (o < 0) + asprintf(error, "Value %s for %s is not acceptable", w, args[i].tag); + break; + case subcommand: + c = find_cmd(args[i].subcmd, w); rv[i+offset] = c; if (c) { args = c->args; @@ -289,6 +310,7 @@ static void **parse_line(struct args **argsp, char *line, int *offsetp, } } else asprintf(error, "Unrecognised command: %s",w); + break; } } } @@ -314,8 +336,8 @@ static int execute_line(struct state *st, char *line) return -1; } c = (struct cmd*)arglist[0]; - - c->cmd(st, arglist); + if (c) + c->cmd(st, arglist); free(arglist); return 1; } @@ -466,6 +488,29 @@ static char *tag_gen(const char *prefix, int state) return NULL; } +/* choice_gen is used to generate a list of possible values + * for a 'choice' field. + * 'gen_options' is the options that can go here. + */ +static char **gen_options; +static char *choice_gen(const char *prefix, int state) +{ + static int next; + int len; + + if (state == 0) + next = 0; + + len = strlen(prefix); + for (; gen_options[next] ; next++) { + if (strncmp(prefix, gen_options[next], len) != 0) + continue; + next++; + return strdup(gen_options[next-1]); + } + return NULL; +} + /* * This is the brains of the completion handler. * We parse the line-so-far to determine way options have already @@ -528,6 +573,11 @@ static char **complete_in_context(const char *prefix, int start, int end) matches = rl_completion_matches( prefix, rl_filename_completion_function); break; + case choice: + gen_options = args[p].options; + matches = rl_completion_matches( + prefix, choice_gen); + break; default: break; } @@ -580,8 +630,8 @@ static void c_exit(struct state *st, void **args) /****** HELP ******/ static char help_help[] = "Print help for a command or all commands"; static struct args args_help[] = { - { "COMMAND", subcommand, -1, lafs_cmds, "Command to display help for"}, - { "-all", flag, -1, NULL, "List brief help for all commands"}, + { "COMMAND", subcommand, -1, {lafs_cmds}, "Command to display help for"}, + { "-all", flag, -1, {NULL}, "List brief help for all commands"}, TERMINAL_ARG }; @@ -632,12 +682,15 @@ static void c_help(struct state *st, void **args) /****** NEWFS ******/ static char help_newfs[] = "Create a new LaFS filesystem, which can then be stored on one or more devices."; +static char *block_sizes[] = { "512", "1024", "2048", "4096", NULL }; static struct args args_newfs[] = { - { "BLOCK-SIZE", opaque, -1, NULL, "Block size, 512..4096, defaults to 1024"}, - { "-block-size", opaque, 0, NULL, "Block size, 512..4096, defaults to 1024"}, - { "-state-size", opaque, -1, NULL, "Size of state block, defaults to 1024"}, - { "-uuid", opaque, -1, NULL, "UUID - normally randomly generated"}, - { "-black", opaque, -1, NULL, "nothing (just testing)"}, + { "BLOCK-SIZE", choice, -1, {.options=block_sizes}, + "Block size, 512..4096, defaults to 1024"}, + { "-block-size", choice, 0, {.options=block_sizes}, + "Block size, 512..4096, defaults to 1024"}, + { "-state-size", opaque, -1, {NULL}, "Size of state block, defaults to 1024"}, + { "-uuid", opaque, -1, {NULL}, "UUID - normally randomly generated"}, + { "-black", opaque, -1, {NULL}, "nothing (just testing)"}, TERMINAL_ARG }; static void c_newfs(struct state *st, void **args) @@ -649,9 +702,9 @@ static void c_newfs(struct state *st, void **args) /****** STORE ******/ static char help_store[] = "Create a file in the LaFS from an external file"; static struct args args_store[] = { - { "FROM", external, -1, NULL, "File to copy into LaFS"}, - { "TO", internal, -1, NULL, "Where to store file in LaFS"}, - { "-from", external, 0, NULL, "File to copy into LaFS"}, + { "FROM", external, -1, {NULL}, "File to copy into LaFS"}, + { "TO", internal, -1, {NULL}, "Where to store file in LaFS"}, + { "-from", external, 0, {NULL}, "File to copy into LaFS"}, TERMINAL_ARG }; static void c_store(struct state *st, void **args) @@ -679,6 +732,6 @@ static struct cmd lafs_cmds[] = { }; static struct args lafs_args[] = { - { "COMMAND", subcommand, -1, lafs_cmds, "Command for lafs to execute"}, + { "COMMAND", subcommand, -1, {lafs_cmds}, "Command for lafs to execute"}, TERMINAL_ARG }; -- 2.39.5