diff -r 7728d6ec3abf -r 5229a5d098b2 src/lib/console.c --- /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 +#include +#include +#include +#include + +#include +#include + +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; +} +