--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/console.c Thu May 28 00:35:02 2009 +0300
@@ -0,0 +1,277 @@
+#include "console.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <signal.h>
+#include <assert.h>
+
+struct console {
+ /** Configuration */
+ struct console_config config;
+
+ /** Input event */
+ struct event *ev;
+
+ /** Callback functions */
+ const struct console_callbacks *callbacks;
+
+ /** Callback context argument */
+ void *cb_arg;
+
+ /** Old SIGINT handler */
+ struct sigaction old_sigint;
+
+ /** Already initialized? */
+ bool initialized;
+
+ /** Set as log output function? */
+ bool log_output;
+
+ /** Prompt displayed? */
+ bool have_prompt;
+
+ /** Waiting for line to be processed */
+ bool waiting;
+};
+
+/** The global console state */
+static struct console _console;
+
+/**
+ * Our stdin input handler
+ */
+static void console_input (int fd, short what, void *arg)
+{
+ struct console *console = arg;
+
+ (void) fd;
+ (void) what;
+
+ if (console->waiting)
+ // can't feed readline input while it's disabled
+ return log_warn("discrding input while waiting");
+
+ else
+ // tell readline to process it
+ rl_callback_read_char();
+}
+
+/**
+ * Our readline line handler
+ */
+static void console_line (char *line)
+{
+ struct console *console = &_console;
+ enum console_line_status status = CONSOLE_CONTINUE;
+
+ // special-case EOF
+ if (!line) {
+ // prettify
+ rl_crlf();
+ rl_on_new_line();
+
+ if (console->callbacks->on_eof)
+ console->callbacks->on_eof(console->cb_arg);
+
+ return;
+ }
+
+ // update state for console_print during processing
+ console->have_prompt = false;
+
+ // invoke the console callback
+ if (console->callbacks && console->callbacks->on_line)
+ status = console->callbacks->on_line(line, console->cb_arg);
+
+ // add to history mechanism
+ add_history(line);
+
+ // release the line
+ free(line);
+
+ switch (status) {
+ case CONSOLE_CONTINUE:
+ // the prompt will be displayed again
+ console->have_prompt = true;
+
+ break;
+
+ case CONSOLE_WAIT:
+ // deactivate our read event
+ if (event_del(console->ev))
+ log_warn("unable to deactivate console read event");
+
+ // remove the readline stuff to stop it from displaying the prompt
+ rl_callback_handler_remove();
+
+ // ignore input
+ console->waiting = true;
+
+ break;
+ }
+}
+static void on_sigint (int sig)
+{
+ struct console *console = &_console;
+
+ (void) sig;
+
+ if (console->waiting) {
+ // notify user
+ if (console->callbacks->on_interrupt)
+ console->callbacks->on_interrupt(console->cb_arg);
+
+ } else {
+ // interrupt the input line
+ // XXX: is this the right function to call?
+ rl_free_line_state();
+
+ // redisplay on new line
+ rl_crlf();
+ rl_callback_handler_install(console->config.prompt, console_line);
+ }
+}
+
+err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
+ const struct console_callbacks *callbacks, void *cb_arg, error_t *err)
+{
+ struct console *console = &_console;
+
+ // check it's not already initialized
+ assert(!console->initialized);
+
+ // store
+ console->config = *config;
+
+ // store callbacks?
+ if (callbacks)
+ console_set_callbacks(console, callbacks, cb_arg);
+
+ // setup the input event
+ if ((console->ev = event_new(ev_base, STDIN_FILENO, EV_READ | EV_PERSIST, &console_input, console)) == NULL)
+ JUMP_SET_ERROR(err, ERR_EVENT_NEW);
+
+ // set our SIGINT handler
+ struct sigaction sigact;
+
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_handler = on_sigint;
+ sigaction(SIGINT, &sigact, &console->old_sigint);
+
+ // setup state for initial readline prompt
+ console->have_prompt = true;
+
+ // setup readline
+ rl_callback_handler_install(config->prompt, console_line);
+
+ // mark it as initialized
+ console->initialized = true;
+
+ // enable input
+ if (event_add(console->ev, NULL))
+ JUMP_SET_ERROR(err, ERR_EVENT_ADD);
+
+ // ok
+ *console_ptr = console;
+
+ return SUCCESS;
+
+error:
+ console_destroy(console);
+
+ return ERROR_CODE(err);
+}
+
+void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg)
+{
+ console->callbacks = callbacks;
+ console->cb_arg = cb_arg;
+}
+
+void console_continue (struct console *console)
+{
+ if (!console->waiting)
+ return;
+
+ // re-enable input
+ console->waiting = false;
+
+ if (event_add(console->ev, NULL))
+ log_fatal("unable to re-enable console read event");
+
+ // the prompt will be displayed again
+ console->have_prompt = true;
+
+ // re-setup readline with prompt
+ rl_callback_handler_install(console->config.prompt, console_line);
+}
+
+err_t console_print (struct console *console, const char *line)
+{
+ if (console->have_prompt)
+ // don't interrupt current input line
+ rl_crlf();
+
+ // output the line
+ if (printf("%s\n", line) < 0)
+ return _ERR_GENERAL;
+
+ if (console->have_prompt) {
+ // restore input
+ rl_on_new_line();
+ rl_redisplay();
+ }
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Our log_output_func implementation
+ */
+static void console_log_output_func (const char *line, void *_console)
+{
+ struct console *console = _console;
+
+ console_print(console, line);
+}
+
+void console_set_log_output (struct console *console)
+{
+ // replace old one
+ log_set_func(console_log_output_func, console);
+
+ // mark
+ console->log_output = true;
+}
+
+void console_destroy (struct console *console)
+{
+ if (console->log_output)
+ // unset ourselves as the log handler
+ log_set_func(NULL, NULL);
+
+ // remove the input event
+ if (console->ev)
+ event_free(console->ev);
+
+ console->ev = NULL;
+
+ // de-init rl?
+ if (console->initialized)
+ rl_callback_handler_remove();
+
+ console->initialized = false;
+
+ // restore signal handler
+ sigaction(SIGINT, &console->old_sigint, NULL);
+
+ // remove stored stuff
+ console->callbacks = console->cb_arg = NULL;
+}
+