src/console.c
branchnew-lib-errors
changeset 218 5229a5d098b2
parent 217 7728d6ec3abf
child 219 cefec18b8268
equal deleted inserted replaced
217:7728d6ec3abf 218:5229a5d098b2
     1 #include "console.h"
       
     2 #include "log.h"
       
     3 
       
     4 #include <stdlib.h>
       
     5 #include <unistd.h>
       
     6 #include <stdio.h>
       
     7 #include <readline/readline.h>
       
     8 #include <readline/history.h>
       
     9 
       
    10 #include <signal.h>
       
    11 #include <assert.h>
       
    12 
       
    13 struct console {
       
    14     /** Configuration */
       
    15     struct console_config config;
       
    16 
       
    17     /** Input event */
       
    18     struct event *ev;
       
    19 
       
    20     /** Callback functions */
       
    21     const struct console_callbacks *callbacks;
       
    22 
       
    23     /** Callback context argument */
       
    24     void *cb_arg;
       
    25 
       
    26     /** Old SIGINT handler */
       
    27     struct sigaction old_sigint;
       
    28 
       
    29     /** Already initialized? */
       
    30     bool initialized;
       
    31 
       
    32     /** Set as log output function? */
       
    33     bool log_output;
       
    34 
       
    35     /** Prompt displayed? */
       
    36     bool have_prompt;
       
    37 
       
    38     /** Waiting for line to be processed */
       
    39     bool waiting;
       
    40 };
       
    41 
       
    42 /** The global console state */
       
    43 static struct console _console;
       
    44 
       
    45 /**
       
    46  * Our stdin input handler
       
    47  */
       
    48 static void console_input (int fd, short what, void *arg)
       
    49 {
       
    50     struct console *console = arg;
       
    51 
       
    52     (void) fd;
       
    53     (void) what;
       
    54 
       
    55     if (console->waiting)
       
    56         // can't feed readline input while it's disabled
       
    57         return log_warn("discrding input while waiting");
       
    58 
       
    59     else
       
    60         // tell readline to process it
       
    61         rl_callback_read_char();
       
    62 }
       
    63 
       
    64 /**
       
    65  * Our readline line handler
       
    66  */
       
    67 static void console_line (char *line)
       
    68 {
       
    69     struct console *console = &_console;
       
    70     enum console_line_status status = CONSOLE_CONTINUE;
       
    71     
       
    72     // special-case EOF
       
    73     if (!line) {
       
    74         // prettify
       
    75         rl_crlf();
       
    76         rl_on_new_line();
       
    77 
       
    78         if (console->callbacks->on_eof)
       
    79             console->callbacks->on_eof(console->cb_arg);
       
    80         
       
    81         return;
       
    82     }
       
    83 
       
    84     // update state for console_print during processing
       
    85     console->have_prompt = false;
       
    86 
       
    87     // invoke the console callback
       
    88     if (console->callbacks && console->callbacks->on_line)
       
    89         status = console->callbacks->on_line(line, console->cb_arg);
       
    90     
       
    91     // add to history mechanism
       
    92     add_history(line);
       
    93 
       
    94     // release the line
       
    95     free(line);
       
    96     
       
    97     switch (status) {
       
    98         case CONSOLE_CONTINUE:
       
    99             // the prompt will be displayed again
       
   100             console->have_prompt = true;
       
   101 
       
   102             break;
       
   103 
       
   104         case CONSOLE_WAIT:
       
   105             // deactivate our read event
       
   106             if (event_del(console->ev))
       
   107                 log_warn("unable to deactivate console read event");
       
   108 
       
   109             // remove the readline stuff to stop it from displaying the prompt
       
   110             rl_callback_handler_remove();
       
   111 
       
   112             // ignore input
       
   113             console->waiting = true;
       
   114 
       
   115             break;
       
   116     }
       
   117 }
       
   118 static void on_sigint (int sig)
       
   119 {
       
   120     struct console *console = &_console;
       
   121 
       
   122     (void) sig;
       
   123 
       
   124     if (console->waiting) {
       
   125         // notify user
       
   126         if (console->callbacks->on_interrupt)
       
   127             console->callbacks->on_interrupt(console->cb_arg);
       
   128 
       
   129     } else {
       
   130         // interrupt the input line
       
   131         // XXX: is this the right function to call?
       
   132         rl_free_line_state();
       
   133 
       
   134         // redisplay on new line
       
   135         rl_crlf();
       
   136         rl_callback_handler_install(console->config.prompt, console_line);
       
   137     }
       
   138 }
       
   139 
       
   140 err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
       
   141         const struct console_callbacks *callbacks, void *cb_arg, error_t *err)
       
   142 {
       
   143     struct console *console = &_console;
       
   144 
       
   145     // check it's not already initialized
       
   146     assert(!console->initialized);
       
   147 
       
   148     // store
       
   149     console->config = *config;
       
   150 
       
   151     // store callbacks?
       
   152     if (callbacks)
       
   153         console_set_callbacks(console, callbacks, cb_arg);
       
   154 
       
   155     // setup the input event
       
   156     if ((console->ev = event_new(ev_base, STDIN_FILENO, EV_READ | EV_PERSIST, &console_input, console)) == NULL)
       
   157         JUMP_SET_ERROR(err, ERR_EVENT_NEW);
       
   158     
       
   159     // set our SIGINT handler
       
   160     struct sigaction sigact;
       
   161     
       
   162     memset(&sigact, 0, sizeof(sigact));
       
   163     sigact.sa_handler = on_sigint;
       
   164     sigaction(SIGINT, &sigact, &console->old_sigint);
       
   165 
       
   166     // setup state for initial readline prompt
       
   167     console->have_prompt = true;
       
   168 
       
   169     // setup readline
       
   170     rl_callback_handler_install(config->prompt, console_line);
       
   171     
       
   172     // mark it as initialized
       
   173     console->initialized = true;
       
   174 
       
   175     // enable input
       
   176     if (event_add(console->ev, NULL))
       
   177         JUMP_SET_ERROR(err, ERR_EVENT_ADD);
       
   178 
       
   179     // ok
       
   180     *console_ptr = console;
       
   181 
       
   182     return SUCCESS;
       
   183 
       
   184 error:
       
   185     console_destroy(console);
       
   186 
       
   187     return ERROR_CODE(err);
       
   188 }
       
   189 
       
   190 void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg)
       
   191 {
       
   192     console->callbacks = callbacks;
       
   193     console->cb_arg = cb_arg;
       
   194 }
       
   195 
       
   196 void console_continue (struct console *console)
       
   197 {
       
   198     if (!console->waiting)
       
   199         return;
       
   200 
       
   201     // re-enable input
       
   202     console->waiting = false;
       
   203 
       
   204     if (event_add(console->ev, NULL))
       
   205         log_fatal("unable to re-enable console read event");
       
   206     
       
   207     // the prompt will be displayed again
       
   208     console->have_prompt = true;
       
   209 
       
   210     // re-setup readline with prompt
       
   211     rl_callback_handler_install(console->config.prompt, console_line);
       
   212 }
       
   213 
       
   214 err_t console_print (struct console *console, const char *line)
       
   215 {
       
   216     if (console->have_prompt)
       
   217         // don't interrupt current input line
       
   218         rl_crlf();
       
   219 
       
   220     // output the line
       
   221     if (printf("%s\n", line) < 0)
       
   222         return _ERR_GENERAL;
       
   223     
       
   224     if (console->have_prompt) {
       
   225         // restore input
       
   226         rl_on_new_line();
       
   227         rl_redisplay();
       
   228     }
       
   229 
       
   230     // ok
       
   231     return SUCCESS;
       
   232 }
       
   233 
       
   234 /**
       
   235  * Our log_output_func implementation
       
   236  */
       
   237 static void console_log_output_func (const char *line, void *_console)
       
   238 {
       
   239     struct console *console = _console;
       
   240 
       
   241     console_print(console, line);
       
   242 }
       
   243 
       
   244 void console_set_log_output (struct console *console)
       
   245 {
       
   246     // replace old one
       
   247     log_set_func(console_log_output_func, console);
       
   248 
       
   249     // mark
       
   250     console->log_output = true;
       
   251 }
       
   252 
       
   253 void console_destroy (struct console *console)
       
   254 {
       
   255     if (console->log_output)
       
   256         // unset ourselves as the log handler
       
   257         log_set_func(NULL, NULL);
       
   258 
       
   259     // remove the input event
       
   260     if (console->ev)
       
   261         event_free(console->ev); 
       
   262     
       
   263     console->ev = NULL;
       
   264 
       
   265     // de-init rl?
       
   266     if (console->initialized)
       
   267         rl_callback_handler_remove();
       
   268     
       
   269     console->initialized = false;
       
   270 
       
   271     // restore signal handler
       
   272     sigaction(SIGINT, &console->old_sigint, NULL);
       
   273 
       
   274     // remove stored stuff
       
   275     console->callbacks = console->cb_arg = NULL;
       
   276 }
       
   277