#include "console.h"
#include "log.h"
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <assert.h>
/** 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;
// update state
console->have_input = true;
// tell readline to process it
rl_callback_read_char();
}
/**
* Our readline line handler
*/
static void console_line (char *line)
{
struct console *console = &_console;
// XXX: line == NULL -> EOF?
// update state
console->have_input = false;
// invoke the console callback
if (console->callbacks && console->callbacks->on_line)
console->callbacks->on_line(line, console->cb_arg);
// add to history mechanism
add_history(line);
// release the line
free(line);
// update state, as the prompt will be displayed again
console->have_input = true;
}
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, struct error_info *err)
{
struct console *console = &_console;
// check it's not already initialized
assert(!console->initialized);
// 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);
// 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;
}
err_t console_print (struct console *console, const char *line)
{
if (console->have_input)
// don't interrupt current input line
rl_crlf();
// output the line
if (printf("%s\n", line) < 0)
return _ERR_GENERAL;
if (console->have_input) {
// 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;
// remove stored stuff
console->callbacks = console->cb_arg = NULL;
}