X-Git-Url: http://git.tuebingen.mpg.de/?p=adu.git;a=blobdiff_plain;f=interactive.c;h=61b4f293562bfe8077f9bcc25f528887605bb7ce;hp=686a89f192aecfbc65373b49a7c0cd2486a57b71;hb=cd5cfb4c2dbbf5615c78d1e439f0cbfb08437b43;hpb=b01ff57775258c8df4c486b0962fc67cfdfef439 diff --git a/interactive.c b/interactive.c index 686a89f..61b4f29 100644 --- a/interactive.c +++ b/interactive.c @@ -1,50 +1,113 @@ +/* + * Copyright (C) 2008 Andre Noll + * + * Licensed under the GPL v2. For licencing details see COPYING. + */ + +/** \file interactive.c \brief Commands for interactive mode. */ + +#include /* isspace() */ + #include "adu.h" +#include "format.h" +#include "user.h" #include "string.h" -#include "error.h" #include "cmdline.h" #include "select.cmdline.h" +#include "select.h" +#include "error.h" -struct select_args_info select_conf; - +/** + * Describes one valid command for interactive mode. + * + * When invoked in interactive mode, adu reads commands from stdin. There's a + * static array of all such commands. + */ struct interactive_command { + /** The name of the command. */ const char *name; + /** Pointer to The function that is being executed. */ int (*handler)(char *); + /** Help text. */ + const char *desc; }; +static struct uid_range *admissible_uids; +static struct format_info *fi; + +/** The set of supported interactive commands. */ #define INTERACTIVE_COMMANDS \ - INTERACTIVE_COMMAND(dump) \ - INTERACTIVE_COMMAND(set) \ - INTERACTIVE_COMMAND(def) \ + INTERACTIVE_COMMAND(set, "change the current configuration") \ + INTERACTIVE_COMMAND(reset, "reset configuration to defaults") \ + INTERACTIVE_COMMAND(help, "show list of commands and one-line descriptions") \ + INTERACTIVE_COMMAND(run, "start the query according to the current configuration") \ + INTERACTIVE_COMMAND(source, "read and execute interactive commands from a file") + -#define INTERACTIVE_COMMAND(name) \ +/** \cond doxygen is not smart enough for this */ +#define INTERACTIVE_COMMAND(name, desc) \ static int icom_ ## name (char *line); INTERACTIVE_COMMANDS #undef INTERACTIVE_COMMAND -#define INTERACTIVE_COMMAND(_name) \ +#define INTERACTIVE_COMMAND(_name, _desc) \ { \ .name = #_name, \ - .handler = icom_ ## _name \ + .handler = icom_ ## _name, \ + .desc = _desc \ }, struct interactive_command icmds[] = { INTERACTIVE_COMMANDS {.name = NULL} }; +/** \endcond */ + +/** Iterate over the list of all interactive commands. */ +#define FOR_EACH_COMMAND(c) for (c = icmds; c->name; c++) static int read_input_line(char *line, size_t size) { return fgets(line, size, stdin)? 1 : -1; } -static int icom_def(__a_unused char *line) +static int icom_run(__a_unused char *line) { - select_cmdline_parser_init(&select_conf); + return run_select_query(admissible_uids, fi); +} + +static int icom_help(__a_unused char *line) +{ + struct interactive_command *c; + + FOR_EACH_COMMAND(c) + fprintf(stdout, "%s\t%s\n", c->name, c->desc); return 1; } +/** + * Print the list of commands with short descriptions. + */ +void print_interactive_help(void) +{ + struct interactive_command *c; + FOR_EACH_COMMAND(c) + fprintf(stdout, "\t%s\t%s\n", c->name, c->desc); +} + +static int icom_reset(__a_unused char *line) +{ + NOTICE_LOG("resetting configuration to default\n"); + free_format_info(fi); + fi = NULL; + free(admissible_uids); + admissible_uids = NULL; + select_cmdline_parser_init(&select_conf); + return parse_select_options(NULL, NULL, &admissible_uids, &fi); +} + static int icom_set(char *line) { struct select_cmdline_parser_params params = { @@ -54,54 +117,122 @@ static int icom_set(char *line) .check_ambiguity = 0, .print_errors = 1 }; - return select_cmdline_parser_string_ext(line, &select_conf, "select", - ¶ms)? - -E_SYNTAX : 1; -} + if (!line) { + select_cmdline_parser_dump(stdout, &select_conf); + return 1; + } -static int icom_dump(__a_unused char *line) -{ - ERROR_LOG("dump: %s\n", select_conf.format_arg); - select_cmdline_parser_dump(stdout, &select_conf); - return 1; + free_format_info(fi); + fi = NULL; + free(admissible_uids); + admissible_uids = NULL; + return parse_select_options(line, ¶ms, &admissible_uids, &fi); } +/** + * Wrapper for isspace. + * NetBSD needs this. + */ +/* + * The values should be cast to an unsigned char first, then to int. + * Why? Because the isdigit (as do all other is/to functions/macros) + * expect a number from 0 upto and including 255 as their (int) argument. + * Because char is signed on most systems, casting it to int immediately + * gives the functions an argument between -128 and 127 (inclusive), + * which they will use as an array index, and which will thus fail + * horribly for characters which have their most significant bit set. + */ +#define adu_isspace(c) isspace((int)(unsigned char)(c)) + static int exec_interactive_command(char *line) { - const char const *delim = "\t\n "; + const char const *delim = "\t\n\f\r\v "; int i; - char *cmd = adu_strdup(line + strspn(line, delim)); - char *p = cmd + strcspn(cmd, delim); + char *cmd, *args; int ret = -E_SYNTAX; + size_t len; + + if (!line || !*line) + return 1; + len = strlen(line); - *p = '\0'; - p++; + while (len && adu_isspace(line[len - 1])) { + line[len - 1] = '\0'; + len--; + } + if (!len) + return 1; + line += strspn(line, delim); /* skip initial whitespace */ + if (!*line) + return 1; + /* OK, we have a non-empty line */ + if (*line == '#') + return 1; + cmd = adu_strdup(line); + args = cmd + strcspn(cmd, delim); + if (!*args) + args = NULL; + else { + *args = '\0'; + args++; + /* let args point to the next non-whitespace char */ + args += strspn(args, delim); + if (!*args) + args = NULL; + } + DEBUG_LOG("name: %s, args: %s.\n", cmd, args); for (i = 0; icmds[i].name; i++) { - ERROR_LOG("name: %s, cmd: %s.\n", icmds[i].name, cmd); if (strcmp(icmds[i].name, cmd)) continue; - ERROR_LOG("exec cmd: %s, args: %s\n", cmd, p); - ret = icmds[i].handler(p); + INFO_LOG("exec cmd: %s, args: %s\n", cmd, args); + ret = icmds[i].handler(args); break; } free(cmd); return ret; } +static int icom_source(char *args) +{ + char line[255]; + FILE *src = fopen(args, "r"); + int ret; + + if (!src) + return -ERRNO_TO_ERROR(errno); + while (fgets(line, sizeof(line), src)) { + ret = exec_interactive_command(line); + if (ret < 0) + goto out; + } + ret = 1; +out: + fclose(src); + return ret; +} + +/** + * The main function for interactive mode. + * + * \return Standard. + */ int com_interactive(void) { char line[255]; - int ret = 1; + int ret; + select_cmdline_parser_init(&select_conf); + ret = parse_select_options(NULL, NULL, &admissible_uids, &fi); + if (ret< 0) + return ret; + ret = read_uid_file(); + if (ret < 0) + return ret; while (read_input_line(line, sizeof(line)) >= 0) { - size_t len = strlen(line); - if (!len) - continue; - if (line[len - 1] == '\n') - line[len - 1] = '\0'; ret = exec_interactive_command(line); if (ret < 0) printf("%s\n", adu_strerror(-ret)); + fflush(NULL); } return ret; }