implement console_print and log_set_func
authorTero Marttila <terom@fixme.fi>
Sun, 12 Apr 2009 22:19:54 +0300
changeset 137 c607c357c486
parent 136 81dbeb5bc38e
child 138 a716c621cb90
implement console_print and log_set_func
TODO
src/console.c
src/console.h
src/log.c
src/log.h
src/lua_console.c
src/nexus.c
--- a/TODO	Sun Apr 12 20:37:57 2009 +0300
+++ b/TODO	Sun Apr 12 22:19:54 2009 +0300
@@ -22,6 +22,9 @@
  * user-defined types
  * return values
 
+console:
+ * improve console_print further, to act more like rlwrap
+
 lua_console:
  * some kind of remote console
 
--- a/src/console.c	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/console.c	Sun Apr 12 22:19:54 2009 +0300
@@ -1,10 +1,12 @@
 #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 */
@@ -17,10 +19,12 @@
 {
     struct console *console = arg;
 
-    (void) console;
     (void) fd;
     (void) what;
 
+    // update state
+    console->have_input = true;
+
     // tell readline to process it
     rl_callback_read_char();
 }
@@ -32,7 +36,10 @@
 {
     struct console *console = &_console;
 
-    // XXX: EOF?
+    // XXX: line == NULL -> EOF?
+    
+    // update state
+    console->have_input = false;
 
     // invoke the console callback
     if (console->callbacks && console->callbacks->on_line)
@@ -43,6 +50,9 @@
 
     // 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,
@@ -88,8 +98,51 @@
     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); 
--- a/src/console.h	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/console.h	Sun Apr 12 22:19:54 2009 +0300
@@ -52,6 +52,12 @@
 
     /** Already initialized? */
     bool initialized;
+
+    /** Set as log output function? */
+    bool log_output;
+
+    /** In the middle of input? */
+    bool have_input;
 };
 
 /**
@@ -73,6 +79,16 @@
 void console_set_callbacks (struct console *console, const struct console_callbacks *callbacks, void *cb_arg);
 
 /**
+ * Output a full line (without included newline) on the console, trying not to interfere with the input too much.
+ */
+err_t console_print (struct console *console, const char *line);
+
+/**
+ * Install this console as the log output handler
+ */
+void console_set_log_output (struct console *console);
+
+/**
  * Deinitialize the console, restoring the TTY and releasing resources
  */
 void console_destroy (struct console *console);
--- a/src/log.c	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/log.c	Sun Apr 12 22:19:54 2009 +0300
@@ -1,16 +1,39 @@
 
 #include "log.h"
+#include "str.h"
 
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 
 /**
+ * The default log output func
+ */
+void log_default_func (const char *line, void *arg)
+{
+    (void) arg;
+
+    // display
+    printf("%s\n", line);
+}
+
+/**
  * The global log level
  */
 static enum log_level _log_level = LOG_LEVEL_DEFAULT;
 
 /**
+ * The global log output func
+ */
+static struct log_output_ctx {
+    /** The function itself */
+    log_output_func func;
+
+    /** The arg */
+    void *arg;
+} _log_output_ctx = { log_default_func, NULL };
+
+/**
  * List of log level names
  */
 const char *log_level_names[] = {
@@ -34,33 +57,7 @@
         default: return "???";
     }
 }
-
-/**
- * Output the "[TYPE] FUNC: " header
- */
-void _log_header (enum log_level level, const char *func)
-{
-    printf("[%5s] %20s : ", log_level_name(level), func);
-}
-
-void log_msg (enum log_level level, const char *func, const char *format, ...)
-{
-    va_list vargs;
-
-    // filter out?
-    if (level < _log_level)
-        return;
-    
-    _log_header(level, func);
-    
-    // formatted output
-    va_start(vargs, format);
-    vprintf(format, vargs);
-    va_end(vargs);
-
-    // newline
-    printf("\n");
-}
+#undef _LOG_LEVEL_NAME
 
 void set_log_level (enum log_level level)
 {
@@ -68,63 +65,82 @@
     _log_level = level;
 }
 
-void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
+void log_set_func (log_output_func func, void *arg)
 {
-    va_list vargs;
+    // replace the current one
+    _log_output_ctx.func = func ? func : log_default_func;
+    _log_output_ctx.arg = arg;
+}
+
+/**
+ * Format the full output line, and pass it to the log_output_func. The line will be of the form:
+ *  "[<level>] <func> : <log_fmt> [ <user_fmt> ]"
+ */
+static void log_output (enum log_level level, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
+{
+    char buf[LOG_MSG_MAX], *buf_ptr = buf;
+    size_t buf_size = sizeof(buf);
 
     // filter out?
     if (level < _log_level)
         return;
- 
-    // header
-    _log_header(level, func);
+
+    // output the header
+    buf_ptr += str_advance(NULL, &buf_size, str_append_fmt(buf_ptr, buf_size, "[%5s] %20s : ", log_level_name(level), func));
     
-    // formatted output
+    // output the user data
+    buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, user_fmt, user_fmtargs));
+    
+    // output the suffix
+    if (log_fmt) {
+        va_list vargs;
+
+        va_start(vargs, log_fmt);
+        buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, vargs));
+        va_end(vargs);
+    }
+
+    // send it to the output func
+    _log_output_ctx.func(buf, _log_output_ctx.arg);
+}
+
+void log_msg (enum log_level level, const char *func, const char *format, ...)
+{
+    va_list vargs;
+    
+    // formatted output: no suffix
     va_start(vargs, format);
-    vprintf(format, vargs);
+    log_output(level, func, format, vargs, NULL);
     va_end(vargs);
+}
 
-    // err_code and newline
-    printf(": %s\n", error_name(err));
+void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
+{
+    va_list vargs;
+
+    // formatted output: suffix error_name()
+    va_start(vargs, format);
+    log_output(level, func, format, vargs, ": %s", error_name(err));
+    va_end(vargs);
 }
 
 void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
 {
     va_list vargs;
 
-    // filter out?
-    if (level < _log_level)
-        return;
- 
-    // header
-    _log_header(level, func);
-    
-    // formatted output
+    // formatted output: suffix error_msg()
     va_start(vargs, format);
-    vprintf(format, vargs);
+    log_output(level, func, format, vargs, ": %s", error_msg(err));
     va_end(vargs);
-
-    // err_code and newline
-    printf(": %s\n", error_msg(err));
 }
 
 void _log_perr (enum log_level level, const char *func, const char *format, ...)
 {
     va_list vargs;
 
-    // filter out?
-    if (level < _log_level)
-        return;
- 
-    // header
-    _log_header(level, func);
-    
-    // formatted output
+    // formatted output: suffix strerror()
     va_start(vargs, format);
-    vprintf(format, vargs);
+    log_output(level, func, format, vargs, ": %s", strerror(errno));
     va_end(vargs);
-
-    // err_code and newline
-    printf(": %s\n", strerror(errno));
 }
 
--- a/src/log.h	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/log.h	Sun Apr 12 22:19:54 2009 +0300
@@ -31,10 +31,14 @@
 #define LOG_LEVEL_DEFAULT LOG_INFO
 
 /**
- * Log a message with the given level
+ * Maximum length of a log message, including terminating NUL
  */
-void log_msg (enum log_level level, const char *func, const char *format, ...)
-    __attribute__ ((format (printf, 3, 4)));
+#define LOG_MSG_MAX 1024
+
+/**
+ * Log output function, for displaying a line of text, without any trailing newline.
+ */
+typedef void (*log_output_func) (const char *line, void *arg);
 
 /**
  * Set the current log level to filter out messages below the given level
@@ -42,6 +46,19 @@
 void set_log_level (enum log_level level);
 
 /**
+ * Set the current log output function, replacing any default or previous value.
+ *
+ * Pass in func as NULL to revert to the default handler.
+ */
+void log_set_func (log_output_func func, void *arg);
+
+/**
+ * Log a message with the given level
+ */
+void log_msg (enum log_level level, const char *func, const char *format, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+/**
  * Shorthand for log_msg
  */
 #define log_debug(...) log_msg(LOG_DEBUG, __func__, __VA_ARGS__)
@@ -51,11 +68,14 @@
 #define log_fatal(...) log_msg(LOG_FATAL, __func__, __VA_ARGS__)
 
 /**
- * Log a message with LOG_ERROR, appending the formatted error code
+ * Log a message with the given level, appending the formatted error code name
  */
 void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
     __attribute__ ((format (printf, 4, 5)));
 
+/**
+ * Log a message with the given level, appending the formatted error message
+ */
 void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
     __attribute__ ((format (printf, 4, 5)));
 
@@ -65,8 +85,12 @@
 void _log_perr (enum log_level level, const char *func, const char *format, ...)
     __attribute__ ((format (printf, 3, 4)));
 
+/**
+ * Shorthand for _log_*
+ */
 #define log_err(err, ...) _log_err(LOG_ERROR, err, __func__, __VA_ARGS__)
 #define log_err_info(err_info, ...) _log_err_info(LOG_ERROR, err_info, __func__, __VA_ARGS__)
+#define log_perr(...) _log_perr(LOG_ERROR, __func__, __VA_ARGS__)
 
 /**
  * log_fatal + exit failure
--- a/src/lua_console.c	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/lua_console.c	Sun Apr 12 22:19:54 2009 +0300
@@ -17,7 +17,7 @@
     
     // eval it
     if (nexus_lua_eval(lc->lua, line, &err))
-        log_error("%s", error_msg(&err));
+        console_print(lc->console, error_msg(&err));
 
     // XXX: display return value?
      
--- a/src/nexus.c	Sun Apr 12 20:37:57 2009 +0300
+++ b/src/nexus.c	Sun Apr 12 22:19:54 2009 +0300
@@ -286,6 +286,9 @@
     // init the console
     if (console_init(&console, nexus->ev_base, &config, NULL, NULL, err))
         return ERROR_CODE(err);
+    
+    // set it as the log output handler
+    console_set_log_output(console);
 
     // create the lua console on top of that
     if (lua_console_create(&nexus->lua_console, console, nexus->lua, err))