+/*
+ * Copyright (C) 2008 Andre Noll <maan@tuebingen.mpg.de>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file interactive.c \brief Commands for interactive mode. */
+
+#include <ctype.h> /* isspace() */
+
#include "adu.h"
#include "format.h"
-#include "select.h"
+#include "user.h"
#include "string.h"
-#include "error.h"
#include "cmdline.h"
+#include "select.cmdline.h"
+#include "select.h"
+#include "error.h"
+/**
+ * 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, "dump the current configuration") \
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 options")
+ INTERACTIVE_COMMAND(run, "start the query according to the current configuration") \
+ INTERACTIVE_COMMAND(source, "read and execute interactive commands from a file")
+/** \cond doxygen is not smart enough for this */
#define INTERACTIVE_COMMAND(name, desc) \
static int icom_ ## name (char *line);
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 1;
}
+/**
+ * Print the list of commands with short descriptions.
+ */
void print_interactive_help(void)
{
- icom_help(NULL);
+ 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 1;
+ return parse_select_options(NULL, NULL, &admissible_uids, &fi);
}
static int icom_set(char *line)
.check_ambiguity = 0,
.print_errors = 1
};
+ if (!line) {
+ 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);
}
-static int icom_dump(__a_unused char *line)
-{
- select_cmdline_parser_dump(stdout, &select_conf);
- return 1;
-}
+/**
+ * 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;
}