* 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.
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.
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;
* 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;
}
} else
asprintf(error, "Unrecognised command: %s",w);
+ break;
}
}
}
return -1;
}
c = (struct cmd*)arglist[0];
-
- c->cmd(st, arglist);
+ if (c)
+ c->cmd(st, arglist);
free(arglist);
return 1;
}
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
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;
}
/****** 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
};
/****** 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)
/****** 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)
};
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
};