Show milliseconds in SILC_LOG_DEBUG and family outputs
[runtime.git] / lib / silcutil / silclog.c
index dc74ace1492e06ae4f3e13cb9e552389f60b6873..e857bac29789cf85c96cd5fe7e794b18c5913faa 100644 (file)
@@ -2,14 +2,13 @@
 
   silclog.c
 
-  Author: Johnny Mnemonic <johnny@themnemonic.org>
+  Author: Pekka Riikonen <priikone@silcnet.org>
 
-  Copyright (C) 1997 - 2002 Pekka Riikonen
+  Copyright (C) 1997 - 2008 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
+  the Free Software Foundation; version 2 of the License.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   GNU General Public License for more details.
 
 */
-/* $Id$ */
 
-#include "silcincludes.h"
-
-/* Minimum time delay for log flushing calls (in seconds) */
-#define SILC_LOG_FLUSH_MIN_DELAY 2
-
-/* nice macro for looping through all logs -- makes the code more readable */
-#define SILC_FOREACH_LOG(__x__) for (__x__ = 0; __x__ < SILC_LOG_MAX; __x__++)
-
-/* Our working struct -- at the moment we keep it private, but this could
- * change in the future */
-struct SilcLogStruct {
+#include "silcruntime.h"
+
+/* SilcLogSettings context */
+typedef struct {
+  SilcUInt32 flushdelay;
+
+  char debug_string[128];
+  SilcLogDebugCb debug_cb;
+  void *debug_context;
+  SilcLogHexdumpCb hexdump_cb;
+  void *hexdump_context;
+
+  unsigned int timestamp       : 1;
+  unsigned int quick           : 1;
+  unsigned int debug           : 1;
+  unsigned int debug_hexdump   : 1;
+  unsigned int scheduled       : 1;
+  unsigned int no_init         : 1;
+  unsigned int starting        : 1;
+} *SilcLogSettings, SilcLogSettingsStruct;
+
+/* SilcLog context */
+typedef struct {
   char filename[256];
   FILE *fp;
-  SilcUInt32 maxsize;
+  SilcUInt64 maxsize;
   const char *typename;
   SilcLogType type;
   SilcLogCb cb;
   void *context;
+} *SilcLog, SilcLogStruct;
+
+#ifndef SILC_SYMBIAN
+
+/* Default settings */
+static SilcLogSettingsStruct silclog =
+{
+  300,
+  { 0 },
+  NULL, NULL,
+  NULL, NULL,
+  TRUE,
+  FALSE,
+  FALSE,
+  FALSE,
+  FALSE,
+  FALSE,
+  TRUE,
 };
-typedef struct SilcLogStruct *SilcLog;
 
-/* These are the known logging channels.  We initialize this struct with most
- * of the fields set to NULL, because we'll fill in those values at runtime. */
-static struct SilcLogStruct silclogs[SILC_LOG_MAX] = {
+#endif /* !SILC_SYMBIAN */
+
+/* Default log contexts */
+#ifndef SILC_SYMBIAN
+static SilcLogStruct silclogs[4] =
+#else
+const SilcLogStruct silclogs[4] =
+#endif /* !SILC_SYMBIAN */
+{
   {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
   {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
   {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
   {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
 };
 
-/* If TRUE, log files will be flushed for each log input */
-bool silc_log_quick = FALSE;
-
-/* Set TRUE/FALSE to enable/disable debugging */
-bool silc_debug = FALSE;
-bool silc_debug_hexdump = FALSE;
-
-/* Flush delay (in seconds) */
-long silc_log_flushdelay = 300;
-
-/* Regular pattern matching expression for the debug output */
-char silc_log_debug_string[128];
-
-/* Debug callbacks. If set, these are triggered for each specific output. */
-static SilcLogDebugCb silc_log_debug_cb = NULL;
-static void *silc_log_debug_context = NULL;
-static SilcLogHexdumpCb silc_log_hexdump_cb = NULL;
-static void *silc_log_hexdump_context = NULL;
+/* Return log context by type */
 
-/* Did we register already our functions to the scheduler? */
-static bool silc_log_scheduled = FALSE;
-static bool silc_log_no_init = FALSE;
-
-/* This is only needed during starting up -- don't lose any logging message */
-static bool silc_log_starting = TRUE;
-
-/* The type wrapper utility. Translates a SilcLogType id to the corresponding
- * logfile, or NULL if not found. */
-static SilcLog silc_log_find_by_type(SilcLogType type)
+static SilcLog silc_log_get_context(SilcLogType type)
 {
-  /* this is not really needed, but i think it's more secure */
-  switch (type) {
-    case SILC_LOG_INFO:
-      return &silclogs[SILC_LOG_INFO];
-    case SILC_LOG_WARNING:
-      return &silclogs[SILC_LOG_WARNING];
-    case SILC_LOG_ERROR:
-      return &silclogs[SILC_LOG_ERROR];
-    case SILC_LOG_FATAL:
-      return &silclogs[SILC_LOG_FATAL];
-    default:
-      return NULL;
-  }
-  return NULL;
+  if (type < 1 || type > 4)
+    return NULL;
+  return (SilcLog)&silclogs[(int)type - 1];
 }
 
-/* Given an open log file, checks the size and rotates it if there is a
- * max size set lower then the current size */
+/* Check log file site and cycle log file if it is over max size. */
+
 static void silc_log_checksize(SilcLog log)
 {
-  char newname[127];
-  long size;
+  char newname[256];
+  SilcUInt64 size;
 
   if (!log || !log->fp || !log->maxsize)
-    return; /* we are not interested */
-
-  if ((size = ftell(log->fp)) < 0) {
-    /* OMG, EBADF is here.. we'll try our best.. */
-    FILE *oldfp = log->fp;
-    fclose(oldfp); /* we can discard the error */
-    log->fp = NULL; /* make sure we don't get here recursively */
-    SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%p",
-                   log->filename, oldfp));
     return;
+
+  size = silc_file_size(log->filename);
+  if (!size) {
+    fclose(log->fp);
+    log->fp = NULL;
   }
-  if (size < log->maxsize) return;
 
-  /* It's too big */
-  fprintf(log->fp, "[%s] [%s] Cycling log file, over max "
-         "logsize (%lu kilobytes)\n",
-         silc_get_time(), log->typename, log->maxsize / 1024);
+  if (size < log->maxsize)
+    return;
+
+  /* Cycle log file */
+  fprintf(log->fp,
+         "[%s] [%s] Cycling log file, over max log size (%lu kilobytes)\n",
+         silc_time_string(0), log->typename,
+         (unsigned long)log->maxsize / 1024);
   fflush(log->fp);
   fclose(log->fp);
-  snprintf(newname, sizeof(newname), "%s.old", log->filename);
-  unlink(newname);
 
-  /* I heard the following syscall may cause portability issues, but I don't
-   * have any other solution since SILC library doesn't provide any other
-   * function like this. -Johnny */
+  memset(newname, 0, sizeof(newname));
+  silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
+  unlink(newname);
   rename(log->filename, newname);
-  if (!(log->fp = fopen(log->filename, "w")))
-    SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s",
-                     log->filename, log->typename, strerror(errno)));
-}
-
-/* Reset a logging channel (close and reopen) */
 
-static bool silc_log_reset(SilcLog log)
-{
-  if (!log) return FALSE;
-  if (log->fp) {
-    fflush(log->fp);
-    fclose(log->fp);
-  }
-  if (!log->filename[0]) return FALSE;
-  if (!(log->fp = fopen(log->filename, "a+"))) {
-    SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s",
+  log->fp = fopen(log->filename, "w");
+  if (!log->fp)
+    SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
                      log->filename, log->typename, strerror(errno)));
-    return FALSE;
-  }
-
-  return TRUE;
+#ifdef HAVE_CHMOD
+  chmod(log->filename, 0600);
+#endif /* HAVE_CHMOD */
 }
 
 /* Internal timeout callback to flush log channels and check file sizes */
 
 SILC_TASK_CALLBACK(silc_log_fflush_callback)
 {
-  unsigned int u;
-  if (!silc_log_quick) {
+#ifndef SILC_SYMBIAN
+  SilcLog log;
+
+  if (!silclog.quick) {
     silc_log_flush_all();
-    SILC_FOREACH_LOG(u)
-      silc_log_checksize(&silclogs[u]);
+    log = silc_log_get_context(SILC_LOG_INFO);
+    silc_log_checksize(log);
+    log = silc_log_get_context(SILC_LOG_WARNING);
+    silc_log_checksize(log);
+    log = silc_log_get_context(SILC_LOG_ERROR);
+    silc_log_checksize(log);
+    log = silc_log_get_context(SILC_LOG_FATAL);
+    silc_log_checksize(log);
   }
-  silc_log_starting = FALSE;
-  if (silc_log_flushdelay < SILC_LOG_FLUSH_MIN_DELAY)
-    silc_log_flushdelay = SILC_LOG_FLUSH_MIN_DELAY;
-  silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback,
-                        context, silc_log_flushdelay, 0, SILC_TASK_TIMEOUT,
-                        SILC_TASK_PRI_NORMAL);
+
+  silclog.starting = FALSE;
+
+  if (silclog.flushdelay < 2)
+    silclog.flushdelay = 2;
+  silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
+                                silclog.flushdelay, 0);
+#endif /* !SILC_SYMBIAN */
 }
 
-/* Outputs the log message to the first available channel. Channels are
- * ordered by importance (see SilcLogType documentation).
- * More important channels can be printed on less important ones, but not
- * vice-versa. */
+/* Output log message to log file */
 
 void silc_log_output(SilcLogType type, char *string)
 {
   const char *typename = NULL;
+  SilcLog log = silc_log_get_context(type);
   FILE *fp;
-  SilcLog log;
 
-  if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type)))
+  if (!log)
     goto end;
 
-  /* Save the original typename, because even if we redirect the message
-   * to another channel we'll keep however the original channel name */
-  typename = log->typename;
-
-  /* If there is a custom callback set, use it and return. */
-  if (log->cb) {
+  /* Forward to callback if set */
+  if (log->cb)
     if ((*log->cb)(type, string, log->context))
       goto end;
-  }
 
-  if (!silc_log_scheduled) {
-    if (silc_log_no_init == FALSE) {
+  typename = log->typename;
+
+#ifndef SILC_SYMBIAN
+  if (!silclog.scheduled) {
+    if (silclog.no_init == FALSE) {
       fprintf(stderr,
-             "Warning, trying to output without log files initialization, "
+             "Warning, log files not initialized, "
              "log output is going to stderr\n");
-      silc_log_no_init = TRUE;
+      silclog.no_init = TRUE;
     }
-    /* redirect output */
+
     fp = stderr;
     log = NULL;
     goto found;
   }
+#endif /* !SILC_SYMBIAN */
 
-  /* ok, now we have to find an open stream */
-  while (TRUE) {
-    if (log && (fp = log->fp)) goto found;
-    if (type == 0) break;
-    log = silc_log_find_by_type(--type);
-  }
+  /* Find open log file */
+  while (log) {
+    if (log->fp) {
+      fp = log->fp;
+      break;
+    }
 
-  /* Couldn't find any open stream.. sorry :( */
-  SILC_LOG_DEBUG(("Warning! couldn't find any available log channel!"));
-  goto end;
+    log = silc_log_get_context(--type);
+  }
+  if (!log || !log->fp)
+    goto end;
 
+#ifndef SILC_SYMBIAN
  found:
-  fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), typename, string);
-  if (silc_log_quick || silc_log_starting) {
+  if (silclog.timestamp)
+    fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string);
+  else
+    fprintf(fp, "[%s] %s\n", typename, string);
+
+  if (silclog.quick || silclog.starting) {
     fflush(fp);
-    if (log) /* we may have been redirected to stderr */
+    if (log)
       silc_log_checksize(log);
   }
+#endif /* !SILC_SYMBIAN */
 
  end:
-  /* If debugging, also output the logging message to the console */
-  if (typename && silc_debug) {
+#ifndef SILC_SYMBIAN
+  /* Output log to stderr if debugging */
+  if (typename && silclog.debug) {
     fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
     fflush(stderr);
   }
+#else
+  fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
+#endif /* !SILC_SYMBIAN */
+
   silc_free(string);
 }
 
-/* returns an internally allocated pointer to a logging channel file */
-char *silc_log_get_file(SilcLogType type)
-{
-  SilcLog log;
-
-  if (!(log = silc_log_find_by_type(type)))
-    return NULL;
-  if (log->fp)
-    return log->filename;
-  return NULL;
-}
+/* Set and initialize the specified log file. */
 
-/* Set and initialize the specified logging channel. See the API reference */
-bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
-                      SilcSchedule scheduler)
+SilcBool silc_log_set_file(SilcLogType type, char *filename,
+                          SilcUInt32 maxsize, SilcSchedule scheduler)
 {
+#ifndef SILC_SYMBIAN
   FILE *fp = NULL;
   SilcLog log;
 
-  log = silc_log_find_by_type(type);
+  if (!scheduler)
+    scheduler = silc_schedule_get_global();
+
+  log = silc_log_get_context(type);
   if (!log)
     return FALSE;
 
-  SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)",
+  SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
                  log->typename, filename, maxsize));
 
-  /* before assuming the new file, make sure we can open it */
+  /* Open log file */
   if (filename) {
-    if (!(fp = fopen(filename, "a+"))) {
-      fprintf(stderr, "warning: couldn't open log file %s: %s\n",
+    fp = fopen(filename, "a+");
+    if (!fp) {
+      fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
              filename, strerror(errno));
       return FALSE;
     }
+#ifdef HAVE_CHMOD
+    chmod(filename, 0600);
+#endif /* HAVE_CHMOD */
   }
 
-  /* clean the logging channel */
+  /* Close previous log file if it exists */
   if (strlen(log->filename)) {
     if (log->fp)
       fclose(log->fp);
@@ -279,220 +273,283 @@ bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
     log->fp = NULL;
   }
 
+  /* Set new log file */
   if (fp) {
-    memset(log->filename, 0, sizeof(log->filename));
-    strncpy(log->filename, filename,
-           strlen(filename) < sizeof(log->filename) ? strlen(filename) :
-           sizeof(log->filename) - 1);
     log->fp = fp;
     log->maxsize = maxsize;
+
+    memset(log->filename, 0, sizeof(log->filename));
+    silc_strncat(log->filename, sizeof(log->filename), filename,
+                strlen(filename));
   }
 
+  /* Add flush timeout */
   if (scheduler) {
-    if (silc_log_scheduled)
-      return TRUE;
-
-    /* Add schedule hook with a short delay to make sure we'll use 
-       right delay */
-    silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
-                          (void *) scheduler, 10, 0,
-                          SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
-    silc_log_scheduled = TRUE;
+    silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
+    silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
+                                  scheduler, 10, 0);
+    silclog.scheduled = TRUE;
   }
 
+#endif /* !SILC_SYMBIAN */
   return TRUE;
 }
 
+/* Return log filename */
+
+char *silc_log_get_file(SilcLogType type)
+{
+  SilcLog log = silc_log_get_context(type);
+  return log && log->fp ? log->filename : NULL;
+}
+
 /* Sets a log callback, set callback to NULL to return to default behaviour */
 
 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
 {
-  SilcLog log;
+#ifndef SILC_SYMBIAN
+  SilcLog log = silc_log_get_context(type);
+  if (log) {
+    log->cb = cb;
+    log->context = context;
+  }
+#endif /* !SILC_SYMBIAN */
+}
 
-  if (!(log = silc_log_find_by_type(type)))
-    return;
+/* Reset log callbacks */
 
-  log->cb = cb;
-  log->context = context;
+void silc_log_reset_callbacks(void)
+{
+#ifndef SILC_SYMBIAN
+  SilcLog log;
+  log = silc_log_get_context(SILC_LOG_INFO);
+  log->cb = log->context = NULL;
+  log = silc_log_get_context(SILC_LOG_WARNING);
+  log->cb = log->context = NULL;
+  log = silc_log_get_context(SILC_LOG_ERROR);
+  log->cb = log->context = NULL;
+  log = silc_log_get_context(SILC_LOG_FATAL);
+  log->cb = log->context = NULL;
+#endif /* !SILC_SYMBIAN */
 }
 
-/* Resets log callbacks */
+/* Flush all log files */
 
-void silc_log_reset_callbacks()
+void silc_log_flush_all(void)
 {
-  unsigned int u;
-  SILC_FOREACH_LOG(u) {
-    silclogs[u].cb = NULL;
-    silclogs[u].context = NULL;
-  }
+  SilcLog log;
+  log = silc_log_get_context(SILC_LOG_INFO);
+  if (log->fp)
+    fflush(log->fp);
+  log = silc_log_get_context(SILC_LOG_WARNING);
+  if (log->fp)
+    fflush(log->fp);
+  log = silc_log_get_context(SILC_LOG_ERROR);
+  if (log->fp)
+    fflush(log->fp);
+  log = silc_log_get_context(SILC_LOG_FATAL);
+  if (log->fp)
+    fflush(log->fp);
 }
 
-/* Flushes all opened logging channels */
+/* Reset a log file */
 
-void silc_log_flush_all() {
-  unsigned int u;
-  SILC_LOG_DEBUG(("Flushing all logs"));
-  SILC_FOREACH_LOG(u) {
-    if (silclogs[u].fp)
-      fflush(silclogs[u].fp);
+static void silc_log_reset(SilcLog log)
+{
+  if (log->fp) {
+    fflush(log->fp);
+    fclose(log->fp);
   }
+
+  if (!strlen(log->filename))
+    return;
+
+  log->fp = fopen(log->filename, "a+");
+  if (!log->fp)
+    SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
+                     log->filename, log->typename, strerror(errno)));
 }
 
-/* Resets all known logging channels */
+/* Reset all log files */
 
-void silc_log_reset_all() {
-  unsigned int u;
-  SILC_LOG_DEBUG(("Resetting all logs"));
-  SILC_FOREACH_LOG(u)
-    silc_log_reset(&silclogs[u]);
-  /* Immediately flush any possible warning message */
+void silc_log_reset_all(void)
+{
+  SilcLog log;
+  log = silc_log_get_context(SILC_LOG_INFO);
+  if (log->fp)
+    silc_log_reset(log);
+  log = silc_log_get_context(SILC_LOG_WARNING);
+  if (log->fp)
+    silc_log_reset(log);
+  log = silc_log_get_context(SILC_LOG_ERROR);
+  if (log->fp)
+    silc_log_reset(log);
+  log = silc_log_get_context(SILC_LOG_FATAL);
+  if (log->fp)
+    silc_log_reset(log);
   silc_log_flush_all();
 }
 
-/* Outputs the debug message to stderr. */
+/* Sets debug callbacks */
 
-void silc_log_output_debug(char *file, char *function,
-                          int line, char *string)
+void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
+                                 void *debug_context,
+                                 SilcLogHexdumpCb hexdump_cb,
+                                 void *hexdump_context)
 {
-  if (!silc_debug)
-    goto end;
+#ifndef SILC_SYMBIAN
+  silclog.debug_cb = debug_cb;
+  silclog.debug_context = debug_context;
+  silclog.hexdump_cb = hexdump_cb;
+  silclog.hexdump_context = hexdump_context;
+#endif /* !SILC_SYMBIAN */
+}
 
-  if (silc_log_debug_string &&
-      !silc_string_regex_match(silc_log_debug_string, file) &&
-      !silc_string_regex_match(silc_log_debug_string, function))
-    goto end;
+/* Resets debug callbacks */
 
-  if (silc_log_debug_cb) {
-    if ((*silc_log_debug_cb)(file, function, line, string,
-                            silc_log_debug_context))
-      goto end;
-  }
+void silc_log_reset_debug_callbacks()
+{
+#ifndef SILC_SYMBIAN
+  silclog.debug_cb = NULL;
+  silclog.debug_context = NULL;
+  silclog.hexdump_cb = NULL;
+  silclog.hexdump_context = NULL;
+#endif /* !SILC_SYMBIAN */
+}
 
-  fprintf(stderr, "%s:%d: %s\n", function, line, string);
-  fflush(stderr);
+/* Set current debug string */
 
- end:
+void silc_log_set_debug_string(const char *debug_string)
+{
+#ifndef SILC_SYMBIAN
+  char *string;
+  int len;
+  if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
+      strchr(debug_string, '$'))
+    string = silc_strdup(debug_string);
+  else
+    string = silc_string_regexify(debug_string);
+  len = strlen(string);
+  if (len >= sizeof(silclog.debug_string))
+    len = sizeof(silclog.debug_string) - 1;
+  memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
+  strncpy(silclog.debug_string, string, len);
   silc_free(string);
+#endif /* !SILC_SYMBIAN */
 }
 
-/* Hexdumps a message */
+/* Set timestamp */
 
-void silc_log_output_hexdump(char *file, char *function,
-                            int line, void *data_in,
-                            SilcUInt32 len, char *string)
+void silc_log_timestamp(SilcBool enable)
 {
-  int i, k;
-  int off, pos, count;
-  unsigned char *data = (unsigned char *)data_in;
-
-  if (!silc_debug_hexdump)
-    goto end;
+#ifndef SILC_SYMBIAN
+  silclog.timestamp = enable;
+#endif /* !SILC_SYMBIAN */
+}
 
-  if (silc_log_debug_string &&
-      !silc_string_regex_match(silc_log_debug_string, file) &&
-      !silc_string_regex_match(silc_log_debug_string, function))
-    goto end;
+/* Set flushdelay */
 
-  if (silc_log_hexdump_cb) {
-    if ((*silc_log_hexdump_cb)(file, function, line, data_in, len, string,
-                              silc_log_hexdump_context))
-      goto end;
-  }
+void silc_log_flushdelay(SilcUInt32 flushdelay)
+{
+#ifndef SILC_SYMBIAN
+  silclog.flushdelay = flushdelay;
+#endif /* !SILC_SYMBIAN */
+}
 
-  fprintf(stderr, "%s:%d: %s\n", function, line, string);
+/* Set quick logging */
 
-  k = 0;
-  pos = 0;
-  count = 16;
-  off = len % 16;
-  while (1) {
-    if (off) {
-      if ((len - pos) < 16 && (len - pos <= len - off))
-       count = off;
-    } else {
-      if (pos == len)
-       count = 0;
-    }
-    if (off == len)
-      count = len;
+void silc_log_quick(SilcBool enable)
+{
+#ifndef SILC_SYMBIAN
+  silclog.quick = enable;
+#endif /* !SILC_SYMBIAN */
+}
 
-    if (count)
-      fprintf(stderr, "%08X  ", k++ * 16);
+/* Set debugging */
 
-    for (i = 0; i < count; i++) {
-      fprintf(stderr, "%02X ", data[pos + i]);
+void silc_log_debug(SilcBool enable)
+{
+#ifndef SILC_SYMBIAN
+  silclog.debug = enable;
+#endif /* !SILC_SYMBIAN */
+}
 
-      if ((i + 1) % 4 == 0)
-       fprintf(stderr, " ");
-    }
+/* Set debug hexdump */
 
-    if (count && count < 16) {
-      int j;
+void silc_log_debug_hexdump(SilcBool enable)
+{
+#ifndef SILC_SYMBIAN
+  silclog.debug_hexdump = enable;
+#endif /* !SILC_SYMBIAN */
+}
 
-      for (j = 0; j < 16 - count; j++) {
-       fprintf(stderr, "   ");
+/* Outputs the debug message to stderr. */
 
-       if ((j + count + 1) % 4 == 0)
-         fprintf(stderr, " ");
-      }
-    }
+void silc_log_output_debug(char *file, const char *function,
+                          int line, char *string)
+{
+  SilcTimeStruct curtime;
 
-    for (i = 0; i < count; i++) {
-      char ch;
+#ifndef SILC_SYMBIAN
+  if (!silclog.debug)
+    goto end;
 
-      if (data[pos] < 32 || data[pos] >= 127)
-       ch = '.';
-      else
-       ch = data[pos];
+  if (!silc_string_regex_match(silclog.debug_string, file) &&
+      !silc_string_regex_match(silclog.debug_string, function))
+    goto end;
 
-      fprintf(stderr, "%c", ch);
-      pos++;
-    }
+  if (silclog.debug_cb) {
+    if ((*silclog.debug_cb)(file, (char *)function, line, string,
+                           silclog.debug_context))
+      goto end;
+  }
+#endif /* !SILC_SYMBIAN */
 
-    if (count)
-      fprintf(stderr, "\n");
+  silc_time_value(0, &curtime);
 
-    if (count < 16)
-      break;
-  }
+#ifdef SILC_WIN32
+  if (strrchr(function, '\\'))
+    fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
+  else
+#endif /* SILC_WIN32 */
+#ifdef SILC_SYMBIAN
+  silc_symbian_debug(function, line, string);
+#else
+  fprintf(stderr, "%02d:%02d:%02d.%03d %s:%d: %s\n", curtime.hour,
+         curtime.minute, curtime.second, curtime.msecond, function, line,
+         string);
+  fflush(stderr);
+#endif /* SILC_SYMBIAN */
 
  end:
   silc_free(string);
 }
 
-/* Sets debug callbacks */
+/* Hexdumps a message */
 
-void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
-                                 void *debug_context,
-                                 SilcLogHexdumpCb hexdump_cb,
-                                 void *hexdump_context)
+void silc_log_output_hexdump(char *file, const char *function,
+                            int line, void *data_in,
+                            SilcUInt32 len, char *string)
 {
-  silc_log_debug_cb = debug_cb;
-  silc_log_debug_context = debug_context;
-  silc_log_hexdump_cb = hexdump_cb;
-  silc_log_hexdump_context = hexdump_context;
-}
+#ifndef SILC_SYMBIAN
+  if (!silclog.debug_hexdump)
+    goto end;
 
-/* Resets debug callbacks */
+  if (!silc_string_regex_match(silclog.debug_string, file) &&
+      !silc_string_regex_match(silclog.debug_string, function))
+    goto end;
 
-void silc_log_reset_debug_callbacks()
-{
-  silc_log_debug_cb = NULL;
-  silc_log_debug_context = NULL;
-  silc_log_hexdump_cb = NULL;
-  silc_log_hexdump_context = NULL;
-}
+  if (silclog.hexdump_cb) {
+    if ((*silclog.hexdump_cb)(file, (char *)function, line,
+                             data_in, len, string, silclog.hexdump_context))
+      goto end;
+  }
+#endif /* !SILC_SYMBIAN */
 
-/* Set current debug string */
+  fprintf(stderr, "%s:%d: %s\n", function, line, string);
 
-void silc_log_set_debug_string(const char *debug_string)
-{
-  char *string;
-  if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
-      strchr(debug_string, '$'))
-    string = strdup(debug_string);
-  else
-    string = silc_string_regexify(debug_string);
-  strncpy(silc_log_debug_string, string, strlen(string));
+  silc_hexdump(data_in, len, stderr);
+
+ end:
   silc_free(string);
 }