]> granicus.if.org Git - neomutt/commitdiff
redirect all messages through a log dispatcher
authorRichard Russon <rich@flatcap.org>
Tue, 30 Jan 2018 00:41:41 +0000 (00:41 +0000)
committerRichard Russon <rich@flatcap.org>
Thu, 15 Mar 2018 18:38:04 +0000 (18:38 +0000)
34 files changed:
Makefile.autosetup
conn/getdomain.c
conn/sasl.c
conn/ssl.c
copy.c
curs_lib.c
globals.h
imap/auth_gss.c
imap/auth_login.c
imap/imap.c
init.c
init.h
main.c
mutt.h
mutt/date.c
mutt/debug.c [deleted file]
mutt/debug.h [deleted file]
mutt/file.c
mutt/idna.c
mutt/logging.c [new file with mode: 0644]
mutt/logging.h [new file with mode: 0644]
mutt/memory.c
mutt/message.c [deleted file]
mutt/message.h
mutt/mutt.h
mutt/regex.c
mutt/string.c
mutt_logging.c
mutt_logging.h
muttlib.c
nntp.c
pager.c
pop_auth.c
protos.h

index 3525ae40b75acdd7e1a54f21447ae387af8c7596..fb6b83dfa5e392808666fd5dde262f42c54a5392 100644 (file)
@@ -94,10 +94,10 @@ ALLOBJS+=   $(NEOMUTTOBJS)
 # libmutt
 LIBMUTT=       libmutt.a
 LIBMUTTOBJS=   mutt/address.o mutt/base64.o mutt/buffer.o mutt/charset.o \
-               mutt/date.o mutt/debug.o mutt/exit.o \
-               mutt/file.o mutt/hash.o mutt/idna.o mutt/list.o \
+               mutt/date.o mutt/exit.o \
+               mutt/file.o mutt/hash.o mutt/idna.o mutt/list.o mutt/logging.o \
                mutt/mapping.o mutt/mbyte.o mutt/md5.o \
-               mutt/memory.o mutt/message.o mutt/mime.o mutt/parameter.o \
+               mutt/memory.o mutt/mime.o mutt/parameter.o \
                mutt/regex.o mutt/sha1.o mutt/signal.o mutt/string.o \
                mutt/rfc2047.o
 CLEANFILES+=   $(LIBMUTT) $(LIBMUTTOBJS)
index 0ed11f63ef46b2af59a39d6522ff16499e54f2c2..ddde39ec1c4efc68f8f7ff8d66b79a1b582bbf91 100644 (file)
@@ -36,7 +36,7 @@
 #include <sys/socket.h>
 #include <time.h>
 #include <unistd.h>
-#include "mutt/debug.h"
+#include "mutt/logging.h"
 #include "mutt/memory.h"
 #include "mutt/string2.h"
 
@@ -109,7 +109,7 @@ int getdnsdomainname(char *d, size_t len)
   {
     mutt_str_strfcpy(d, ++p, len);
     rc = 0;
-    mutt_debug(1, "%s\n", d);
+    mutt_debug(1, "Hostname: %s\n", d);
     freeaddrinfo(h);
   }
 
index 77b25e2c5e1163312c5bf527a253d31985a81e43..214167aa862c79060e0563f599d016eeef9155ab 100644 (file)
@@ -53,7 +53,7 @@
 #include <string.h>
 #include <sys/socket.h>
 #include <time.h>
-#include "mutt/debug.h"
+#include "mutt/logging.h"
 #include "mutt/memory.h"
 #include "mutt/message.h"
 #include "mutt/string2.h"
index c2d6b24a4680e2de48b456f9fc9aaf04b22733ca..ee749ac12cf151f99e5b5d069cdfe3ee92296257 100644 (file)
@@ -52,7 +52,6 @@
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include "mutt/debug.h"
 #include "mutt/file.h"
 #include "mutt/memory.h"
 #include "mutt/message.h"
diff --git a/copy.c b/copy.c
index 05d7548895f3807dc0379267f7892798a32e17f0..229c69e793e12a75db48e744d27e359c3f360f7c 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -256,7 +256,7 @@ int mutt_copy_hdr(FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end,
           ++x;
           if (mutt_str_strncasecmp(buf, np->data, mutt_str_strlen(np->data)) == 0)
           {
-            mutt_debug(2, "Reorder: %s matches %s\n", np->data, buf);
+            mutt_debug(2, "Reorder: %s matches %s", np->data, buf);
             break;
           }
         }
index f5b9dd96a38106f723fd8ee5f6b08ff0b2b661d4..3ad927fa297399d8cfac082bd9bf17155fc8c6a2 100644 (file)
@@ -45,7 +45,6 @@
 #include "globals.h"
 #include "header.h"
 #include "mutt_curses.h"
-#include "mutt_logging.h"
 #include "mutt_menu.h"
 #include "opcodes.h"
 #include "options.h"
@@ -382,62 +381,6 @@ void mutt_query_exit(void)
   SigInt = 0;
 }
 
-static void curses_message(int error, const char *fmt, va_list ap)
-{
-  char scratch[LONG_STRING];
-
-  vsnprintf(scratch, sizeof(scratch), fmt, ap);
-
-  /* Only pause if this is a message following an error */
-  if (!error && OPT_MSG_ERR)
-    error_pause();
-
-  mutt_debug(1, "%s\n", scratch);
-  mutt_simple_format(ErrorBuf, sizeof(ErrorBuf), 0, MuttMessageWindow->cols,
-                     FMT_LEFT, 0, scratch, sizeof(scratch), 0);
-
-  if (!OPT_KEEP_QUIET)
-  {
-    if (error)
-      BEEP();
-    SETCOLOR(error ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
-    mutt_window_mvaddstr(MuttMessageWindow, 0, 0, ErrorBuf);
-    NORMAL_COLOR;
-    mutt_window_clrtoeol(MuttMessageWindow);
-    mutt_refresh();
-  }
-
-  if (error)
-  {
-    OPT_MSG_ERR = true;
-    if (gettimeofday(&LastError, NULL) < 0)
-      mutt_debug(1, "gettimeofday failed: %d\n", errno);
-  }
-  else
-  {
-    OPT_MSG_ERR = false;
-    LastError.tv_sec = 0;
-  }
-}
-
-void mutt_curses_error(const char *fmt, ...)
-{
-  va_list ap;
-
-  va_start(ap, fmt);
-  curses_message(1, fmt, ap);
-  va_end(ap);
-}
-
-void mutt_curses_message(const char *fmt, ...)
-{
-  va_list ap;
-
-  va_start(ap, fmt);
-  curses_message(0, fmt, ap);
-  va_end(ap);
-}
-
 void mutt_progress_init(struct Progress *progress, const char *msg,
                         unsigned short flags, unsigned short inc, size_t size)
 {
index ede907e0969b77581643e68ab373db7b51120c70..e8e2cdf01434e29d0ab604f36b0ded9518b3cd28 100644 (file)
--- a/globals.h
+++ b/globals.h
@@ -234,9 +234,6 @@ WHERE short NntpPoll;
 WHERE short NntpContext;
 #endif
 
-WHERE short DebugLevel;
-WHERE char *DebugFile;
-
 WHERE short MenuContext;
 WHERE short PagerContext;
 WHERE short PagerIndexLines;
index 542bf9b09d2a77eb92e071398ec2a64a3a6e3c6c..0c4c1ae6efd2b8ab2c4f9d67e38d1b3e691b0707 100644 (file)
@@ -41,6 +41,7 @@
 #include "auth.h"
 #include "globals.h"
 #include "mutt_account.h"
+#include "mutt_logging.h"
 #include "mutt_socket.h"
 #include "options.h"
 #include "protos.h"
@@ -129,7 +130,7 @@ enum ImapAuthRes imap_auth_gss(struct ImapData *idata, const char *method)
     mutt_debug(2, "Couldn't get service name for [%s]\n", buf1);
     return IMAP_AUTH_UNAVAIL;
   }
-  else if (debuglevel >= 2)
+  else if (DebugLevel >= 2)
   {
     gss_display_name(&min_stat, target_name, &request_buf, &mech_name);
     mutt_debug(2, "Using service name [%s]\n", (char *) request_buf.value);
index f9769632eee5bcf0d7646a3b68305c2508d465fc..6f3f89d009165f8b4eec9584b880d27061334ce2 100644 (file)
@@ -39,6 +39,7 @@
 #include "auth.h"
 #include "globals.h"
 #include "mutt_account.h"
+#include "mutt_logging.h"
 #include "mutt_socket.h"
 #include "options.h"
 #include "protos.h"
@@ -74,7 +75,7 @@ enum ImapAuthRes imap_auth_login(struct ImapData *idata, const char *method)
   /* don't print the password unless we're at the ungodly debugging level
    * of 5 or higher */
 
-  if (debuglevel < IMAP_LOG_PASS)
+  if (DebugLevel < IMAP_LOG_PASS)
     mutt_debug(2, "Sending LOGIN command for %s...\n", idata->conn->account.user);
 
   snprintf(buf, sizeof(buf), "LOGIN %s %s", q_user, q_pass);
index 30a5b62564257d32c6a08dd4bbf24bbff7f4c52d..980b7edbc061deaab9d85f9fb85016ccca51328a 100644 (file)
@@ -96,6 +96,7 @@
 #include "message.h"
 #include "mutt_account.h"
 #include "mutt_curses.h"
+#include "mutt_logging.h"
 #include "mutt_socket.h"
 #include "mx.h"
 #include "options.h"
@@ -815,6 +816,10 @@ int imap_read_literal(FILE *fp, struct ImapData *idata, unsigned long bytes,
 {
   char c;
   bool r = false;
+  struct Buffer *buf = NULL;
+
+  if (DebugLevel >= IMAP_LOG_LTRL)
+    buf = mutt_buffer_alloc(bytes + 10);
 
   mutt_debug(2, "reading %ld bytes\n", bytes);
 
@@ -825,6 +830,7 @@ int imap_read_literal(FILE *fp, struct ImapData *idata, unsigned long bytes,
       mutt_debug(1, "error during read, %ld bytes read\n", pos);
       idata->status = IMAP_FATAL;
 
+      mutt_buffer_free(&buf);
       return -1;
     }
 
@@ -843,10 +849,15 @@ int imap_read_literal(FILE *fp, struct ImapData *idata, unsigned long bytes,
 
     if (pbar && !(pos % 1024))
       mutt_progress_update(pbar, pos, -1);
-    if (debuglevel >= IMAP_LOG_LTRL)
-      fputc(c, debugfile);
+    if (DebugLevel >= IMAP_LOG_LTRL)
+      mutt_buffer_addch(buf, c);
   }
 
+  if (DebugLevel >= IMAP_LOG_LTRL)
+  {
+    mutt_debug(IMAP_LOG_LTRL, "\n%s", buf->data);
+    mutt_buffer_free(&buf);
+  }
   return 0;
 }
 
@@ -2180,7 +2191,7 @@ static int imap_open_mailbox(struct Context *ctx)
   }
 
   /* dump the mailbox flags we've found */
-  if (debuglevel > 2)
+  if (DebugLevel > 2)
   {
     if (STAILQ_EMPTY(&idata->flags))
       mutt_debug(3, "No folder flags found\n");
diff --git a/init.c b/init.c
index 6697c02e1f7ba2a8b27306c91a3356deff88e9ed..f59ec8c0cd3a123f1fa08c83e342c962ffc55e87 100644 (file)
--- a/init.c
+++ b/init.c
@@ -1758,7 +1758,7 @@ static int parse_alias(struct Buffer *buf, struct Buffer *s, unsigned long data,
   mutt_group_context_add_addrlist(gc, tmp->addr);
   mutt_alias_add_reverse(tmp);
 
-  if (debuglevel >= 2)
+  if (DebugLevel > 2)
   {
     /* A group is terminated with an empty address, so check a->mailbox */
     for (struct Address *a = tmp->addr; a && a->mailbox; a = a->next)
@@ -1898,16 +1898,25 @@ static void restore_default(struct Option *p)
       *((struct MbTable **) p->var) = parse_mbtable((char *) p->initial);
       break;
     case DT_PATH:
-      FREE((char **) p->var);
-      char *init = (char *) p->initial;
-      if (init)
       {
-        char path[_POSIX_PATH_MAX];
-        mutt_str_strfcpy(path, init, sizeof(path));
-        mutt_expand_path(path, sizeof(path));
-        *((char **) p->var) = mutt_str_strdup(path);
+        char *init = (char *) p->initial;
+        if (mutt_str_strcmp(p->name, "debug_file") == 0)
+        {
+          mutt_log_set_file(init, true);
+        }
+        else
+        {
+          FREE((char **) p->var);
+          if (init)
+          {
+            char path[_POSIX_PATH_MAX];
+            mutt_str_strfcpy(path, init, sizeof(path));
+            mutt_expand_path(path, sizeof(path));
+            *((char **) p->var) = mutt_str_strdup(path);
+          }
+        }
+        break;
       }
-      break;
     case DT_ADDRESS:
       mutt_addr_free((struct Address **) p->var);
       if (p->initial)
@@ -1925,7 +1934,10 @@ static void restore_default(struct Option *p)
     case DT_NUMBER:
     case DT_SORT:
     case DT_MAGIC:
-      *((short *) p->var) = p->initial;
+      if (mutt_str_strcmp(p->name, "debug_level") == 0)
+        mutt_log_set_level(p->initial, true);
+      else
+        *((short *) p->var) = p->initial;
       break;
     case DT_REGEX:
     {
@@ -2064,67 +2076,7 @@ char **mutt_envlist(void)
 }
 
 /**
- * start_debug - prepare the debugging file
- *
- * This method prepares and opens a new debug file for mutt_debug.
- */
-void start_debug(void)
-{
-  if (!DebugFile)
-    return;
-
-  char buf[_POSIX_PATH_MAX];
-
-  /* rotate the old debug logs */
-  for (int i = 3; i >= 0; i--)
-  {
-    snprintf(debugfilename, sizeof(debugfilename), "%s%d", DebugFile, i);
-    snprintf(buf, sizeof(buf), "%s%d", DebugFile, i + 1);
-
-    mutt_expand_path(debugfilename, sizeof(debugfilename));
-    mutt_expand_path(buf, sizeof(buf));
-    rename(debugfilename, buf);
-  }
-
-  debugfile = mutt_file_fopen(debugfilename, "w");
-  if (debugfile)
-  {
-    setbuf(debugfile, NULL); /* don't buffer the debugging output! */
-    mutt_debug(1, "NeoMutt/%s debugging at level %d\n", PACKAGE_VERSION, debuglevel);
-  }
-}
-
-/**
- * restart_debug - reload the debugging configuration
- *
- * This method closes the old debug file is debug was enabled,
- * then reconfigure the debugging system from the configuration options
- * and start a new debug file if debug is enabled
- */
-static void restart_debug(void)
-{
-  bool disable_debug = (debuglevel > 0 && DebugLevel == 0);
-  bool enable_debug = (debuglevel == 0 && DebugLevel > 0);
-  bool file_changed =
-      ((mutt_str_strlen(debugfilename) - 1) != mutt_str_strlen(DebugFile) ||
-       mutt_str_strncmp(debugfilename, DebugFile, mutt_str_strlen(debugfilename) - 1));
-
-  if (disable_debug || file_changed)
-  {
-    mutt_debug(1, "NeoMutt/%s stop debugging\n", PACKAGE_VERSION);
-    mutt_file_fclose(&debugfile);
-  }
-
-  if (!enable_debug && !disable_debug && debuglevel != DebugLevel)
-    mutt_debug(1, "NeoMutt/%s debugging at level %d\n", PACKAGE_VERSION, DebugLevel);
-
-  debuglevel = DebugLevel;
-
-  if (enable_debug || (file_changed && debuglevel > 0))
-    start_debug();
-}
-
-/* mutt_envlist_set - Helper function for parse_setenv()
+ * mutt_envlist_set - Helper function for parse_setenv()
  * @param name      Name of the environment variable
  * @param value     Value the envionment variable should have
  * @param overwrite Whether the environment variable should be overwritten
@@ -2200,6 +2152,7 @@ static int parse_setenv(struct Buffer *tmp, struct Buffer *s,
     int found = 0;
     while (envp && *envp)
     {
+      /* This will display all matches for "^QUERY" */
       if (mutt_str_strncmp(tmp->data, *envp, len) == 0)
       {
         if (!found)
@@ -2481,22 +2434,20 @@ static int parse_set(struct Buffer *tmp, struct Buffer *s, unsigned long data,
         }
         else if ((idx >= 0) && (DTYPE(MuttVars[idx].type) == DT_PATH))
         {
-          if (mutt_str_strcmp(MuttVars[idx].name, "debug_file") == 0 && debugfile_cmdline)
-          {
-            mutt_message(_("set debug_file ignored, it has been overridden by "
-                           "the cmdline"));
-            break;
-          }
-          /* MuttVars[idx].var is already 'char**' (or some 'void**') or...
-           * so cast to 'void*' is okay */
-          FREE((void *) MuttVars[idx].var);
-
           char scratch[_POSIX_PATH_MAX];
           mutt_str_strfcpy(scratch, tmp->data, sizeof(scratch));
           mutt_expand_path(scratch, sizeof(scratch));
-          *((char **) MuttVars[idx].var) = mutt_str_strdup(scratch);
           if (mutt_str_strcmp(MuttVars[idx].name, "debug_file") == 0)
-            restart_debug();
+          {
+            mutt_log_set_file(scratch, true);
+          }
+          else
+          {
+            /* MuttVars[idx].var is already 'char**' (or some 'void**') or...
+             * so cast to 'void*' is okay */
+            FREE((void *) MuttVars[idx].var);
+            *((char **) MuttVars[idx].var) = mutt_str_strdup(scratch);
+          }
         }
         else if ((idx >= 0) && (DTYPE(MuttVars[idx].type) == DT_STRING))
         {
@@ -2653,12 +2604,9 @@ static int parse_set(struct Buffer *tmp, struct Buffer *s, unsigned long data,
         r = -1;
         break;
       }
-      else if (mutt_str_strcmp(MuttVars[idx].name, "debug_level") == 0 && debuglevel_cmdline)
-      {
-        mutt_message(_(
-            "set debug_level ignored, it has been overridden by the cmdline"));
-        break;
-      }
+
+      if (mutt_str_strcmp(MuttVars[idx].name, "debug_level") == 0)
+        mutt_log_set_level(val, true);
       else
         *ptr = val;
 
@@ -2669,12 +2617,6 @@ static int parse_set(struct Buffer *tmp, struct Buffer *s, unsigned long data,
           *ptr = 0;
         mutt_hist_init();
       }
-      else if (mutt_str_strcmp(MuttVars[idx].name, "debug_level") == 0)
-      {
-        if (*ptr < 0)
-          *ptr = 0;
-        restart_debug();
-      }
       else if (mutt_str_strcmp(MuttVars[idx].name, "pager_index_lines") == 0)
       {
         if (*ptr < 0)
@@ -4035,8 +3977,7 @@ int mutt_init(int skip_sys_rc, struct ListHead *commands)
     {
       if (source_rc(buffer, &err) != 0)
       {
-        fputs(err.data, stderr);
-        fputc('\n', stderr);
+        mutt_error("%s", err.data);
         need_pause = 1;
       }
     }
@@ -4050,8 +3991,7 @@ int mutt_init(int skip_sys_rc, struct ListHead *commands)
     {
       if (source_rc(np->data, &err) != 0)
       {
-        fputs(err.data, stderr);
-        fputc('\n', stderr);
+        mutt_error("%s", err.data);
         need_pause = 1;
       }
     }
@@ -4062,6 +4002,7 @@ int mutt_init(int skip_sys_rc, struct ListHead *commands)
 
   if (need_pause && !OPT_NO_CURSES)
   {
+    log_queue_flush(log_disp_terminal);
     if (mutt_any_key_to_continue(NULL) == 'q')
       return 1;
   }
diff --git a/init.h b/init.h
index 8d5050116a971b526a9141686672ec53da8647fc..e1528655bf110de4f7d130ede12cc43f741e1d90 100644 (file)
--- a/init.h
+++ b/init.h
@@ -34,6 +34,7 @@
 #include "globals.h"
 #include "group.h"
 #include "history.h"
+#include "mutt_logging.h"
 #include "mutt_commands.h"
 #include "mutt_options.h"
 #include "mutt/mutt.h"
diff --git a/main.c b/main.c
index 4df8bd5948dd76b9f1b43c5eeaf8770b9602c303..69c81bffcbc4becb0861fa1257767dc4dbf38f63 100644 (file)
--- a/main.c
+++ b/main.c
@@ -47,7 +47,9 @@
 #include "keymap.h"
 #include "mailbox.h"
 #include "mutt_curses.h"
+#include "mutt_logging.h"
 #include "mutt_menu.h"
+#include "myvar.h"
 #include "ncrypt/ncrypt.h"
 #include "options.h"
 #include "protos.h"
@@ -67,7 +69,6 @@
 #endif
 
 char **envlist = NULL;
-void start_debug(void);
 
 void mutt_exit(int code)
 {
@@ -263,12 +264,14 @@ int main(int argc, char **argv, char **env)
   extern int optind;
   int double_dash = argc, nargc = 1;
   int rc = 1;
+  bool repeat_error = false;
 
-  /* sanity check against stupid administrators */
+  MuttLogger = log_disp_terminal;
 
+  /* sanity check against stupid administrators */
   if (getegid() != getgid())
   {
-    fprintf(stderr, "%s: I don't want to run with privileges!\n", argv[0]);
+    mutt_error("%s: I don't want to run with privileges!", argv[0]);
     goto main_exit;
   }
 
@@ -286,9 +289,6 @@ int main(int argc, char **argv, char **env)
   }
 #endif
 
-  mutt_message = mutt_error; /* send messages to stderr, too */
-  mutt_perror = mutt_perror_debug;
-
   int out = 0;
   if (mutt_randbuf(&out, sizeof(out)) < 0)
     goto main_exit;
@@ -437,6 +437,7 @@ int main(int argc, char **argv, char **env)
 
   if (version > 0)
   {
+    log_queue_flush(log_disp_terminal);
     if (version == 1)
       print_version();
     else
@@ -452,20 +453,27 @@ int main(int argc, char **argv, char **env)
   if (dfile)
   {
     set_default_value("debug_file", (intptr_t) mutt_str_strdup(dfile));
+    mutt_str_replace(&DebugFile, dfile);
   }
-  reset_value("debug_file");
 
   if (dlevel)
   {
     short num = 0;
-    if ((mutt_str_atos(dlevel, &num) < 0) || (num < 0) || (num > 5))
+    if ((mutt_str_atos(dlevel, &num) < 0) || (num < LL_MESSAGE) || (num > LL_DEBUG5))
     {
       mutt_error(_("Error: value '%s' is invalid for -d."), dlevel);
       goto main_exit;
     }
     set_default_value("debug_level", (intptr_t) num);
+    DebugLevel = num;
   }
-  reset_value("debug_level");
+
+  if (dlevel)
+    mutt_log_start();
+  else
+    LogAllowDebugSet = true;
+
+  MuttLogger = log_disp_queue;
 
   if (!STAILQ_EMPTY(&cc_list) || !STAILQ_EMPTY(&bcc_list))
   {
@@ -503,8 +511,13 @@ int main(int argc, char **argv, char **env)
    * before calling the init_pair() function to set the color scheme.  */
   if (!OPT_NO_CURSES)
   {
-    rc = start_curses();
-    if (rc != 0)
+    int crc = start_curses();
+    /* Now that curses is set up, we drop back to normal screen mode.
+     * This simplifies displaying error messages to the user.
+     * The first call to refresh() will swap us back to curses screen mode. */
+    endwin();
+
+    if (crc != 0)
       goto main_curses;
 
     /* check whether terminal status is supported (must follow curses init) */
@@ -521,7 +534,14 @@ int main(int argc, char **argv, char **env)
     reset_value("debug_level");
   if (dfile)
     reset_value("debug_file");
-  start_debug();
+
+  if (mutt_log_start() < 0)
+  {
+    mutt_perror("log file");
+    goto main_exit;
+  }
+
+  LogAllowDebugSet = true;
 
   mutt_list_free(&commands);
 
@@ -567,7 +587,7 @@ int main(int argc, char **argv, char **env)
       else
       {
         rc = 1;
-        mutt_message("%s", np->data);
+        printf("%s\n", np->data);
       }
     }
     mutt_list_free(&alias_queries);
@@ -578,8 +598,9 @@ int main(int argc, char **argv, char **env)
   {
     NORMAL_COLOR;
     clear();
-    mutt_error = mutt_curses_error;
-    mutt_message = mutt_curses_message;
+    MuttLogger = log_disp_curses;
+    log_queue_flush(log_disp_curses);
+    log_queue_set_max_size(100);
   }
 
   /* Create the Folder directory if it doesn't exist. */
@@ -619,9 +640,10 @@ int main(int argc, char **argv, char **env)
   {
     if (!OPT_NO_CURSES)
       mutt_flushinp();
-    ci_send_message(SENDPOSTPONED, NULL, NULL, NULL, NULL);
-    mutt_free_windows();
-    mutt_endwin();
+    if (ci_send_message(SENDPOSTPONED, NULL, NULL, NULL, NULL) == 0)
+      rc = 0;
+    log_queue_empty();
+    repeat_error = true;
   }
   else if (subject || msg || sendflags || draft_file || include_file ||
            !STAILQ_EMPTY(&attach) || optind < argc)
@@ -838,6 +860,10 @@ int main(int argc, char **argv, char **env)
     }
 
     rv = ci_send_message(sendflags, msg, bodyfile, NULL, NULL);
+    /* We WANT the "Mail sent." and any possible, later error */
+    log_queue_empty();
+    if (ErrorBuf[0])
+      mutt_message("%s", ErrorBuf);
 
     if (edit_infile)
     {
@@ -972,6 +998,7 @@ int main(int argc, char **argv, char **env)
     mutt_folder_hook(folder);
     mutt_startup_shutdown_hook(MUTT_STARTUPHOOK);
 
+    repeat_error = true;
     Context = mx_open_mailbox(
         folder, ((flags & MUTT_RO) || ReadOnly) ? MUTT_READONLY : 0, NULL);
     if (Context || !explicit_folder)
@@ -989,18 +1016,20 @@ int main(int argc, char **argv, char **env)
 #ifdef USE_SASL
     mutt_sasl_done();
 #endif
+    log_queue_empty();
+    mutt_log_stop();
     mutt_free_opts();
     mutt_free_windows();
-    mutt_endwin();
-    puts(ErrorBuf);
   }
 
 main_ok:
   rc = 0;
 main_curses:
   mutt_endwin();
+  log_queue_flush(log_disp_terminal);
+  mutt_log_stop();
   /* Repeat the last message to the user */
-  if (ErrorBuf[0])
+  if (repeat_error && ErrorBuf[0])
     puts(ErrorBuf);
 main_exit:
   return rc;
diff --git a/mutt.h b/mutt.h
index db911b678d018cd7d40b90a5d065993d0946cf41..04ad119ced3b25bc5b8d26d46134719317179a0f 100644 (file)
--- a/mutt.h
+++ b/mutt.h
@@ -294,12 +294,6 @@ const char *mutt_str_sysexit(int e);
 
 char *mutt_compile_help(char *buf, size_t buflen, int menu, const struct Mapping *items);
 
-extern char debugfilename[_POSIX_PATH_MAX];
-extern FILE *debugfile;
-extern int debuglevel;
-extern char *debugfile_cmdline;
-extern int debuglevel_cmdline;
-
 /* All the variables below are backing for config items */
 
 /* Quad-options */
index 2a797e0f7cead4ce37b50d3973ff9af948a3e2b5..d57264eca997a502a37c197d2f97c1652d15c8bc 100644 (file)
@@ -33,7 +33,7 @@
 #include <string.h>
 #include <time.h>
 #include "date.h"
-#include "debug.h"
+#include "logging.h"
 #include "memory.h"
 #include "string2.h"
 
diff --git a/mutt/debug.c b/mutt/debug.c
deleted file mode 100644 (file)
index 0f059d6..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @file
- * Debug messages
- *
- * @authors
- * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
- *
- * @copyright
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @page debug Debug messages
- *
- * Output debugging messages, suitable for a developer.
- */
-
-#include "config.h"
-#include <stdarg.h>
-#include <stdio.h>
-
-/**
- * mutt_debug_real - Output some debugging information
- * @param function Function
- * @param file     File
- * @param line     Line number
- * @param level    Debug level
- * @param ...      Arguments to be formatted
- *
- * This stub function ignores the logging level and outputs all information to
- * stderr.
- */
-int mutt_debug_real(const char *function, const char *file, int line, int level, ...)
-{
-  va_list ap;
-  va_start(ap, level);
-  const char *fmt = va_arg(ap, const char *);
-  int ret = vfprintf(stderr, fmt, ap);
-  va_end(ap);
-  return ret;
-}
diff --git a/mutt/debug.h b/mutt/debug.h
deleted file mode 100644 (file)
index 19646e3..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/**
- * @file
- * Debug messages
- *
- * @authors
- * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
- *
- * @copyright
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef _MUTT_DEBUG_H
-#define _MUTT_DEBUG_H
-
-int mutt_debug_real(const char *function, const char *file, int line, int level, ...);
-#define mutt_debug(LEVEL, ...) mutt_debug_real(__func__, __FILE__, __LINE__, LEVEL, __VA_ARGS__)
-
-#endif /* _MUTT_DEBUG_H */
index abc568a1c9d831218f0a2b708dac8e36350e93a4..f543f23a49730720d227a945c6adbc258f545cd9 100644 (file)
@@ -42,7 +42,7 @@
 #include <unistd.h>
 #include <utime.h>
 #include "file.h"
-#include "debug.h"
+#include "logging.h"
 #include "memory.h"
 #include "message.h"
 #include "string2.h"
index 459ecaa132d3ec67358b41e2f33a0070dfb854a8..5f200e73b41b73cfeafd275caace4042c63de826 100644 (file)
@@ -31,8 +31,8 @@
 #include <stdio.h>
 #include <string.h>
 #include "charset.h"
-#include "debug.h"
 #include "idna2.h"
+#include "logging.h"
 #include "memory.h"
 #include "string2.h"
 #ifdef HAVE_IDNA_H
diff --git a/mutt/logging.c b/mutt/logging.c
new file mode 100644 (file)
index 0000000..e170556
--- /dev/null
@@ -0,0 +1,472 @@
+/**
+ * @file
+ * Logging Dispatcher
+ *
+ * @authors
+ * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page logging Logging Dispatcher
+ *
+ * Logging Dispatcher
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include "logging.h"
+#include "file.h"
+#include "memory.h"
+#include "message.h"
+#include "queue.h"
+#include "string2.h"
+
+const char *LevelAbbr = "PEWM12345"; /**< Abbreviations of logging level names */
+
+/**
+ * MuttLogger - The log dispatcher
+ *
+ * This function pointer controls where log messages are redirected.
+ */
+log_dispatcher_t MuttLogger = log_disp_terminal;
+
+FILE *LogFileFP = NULL;      /**< Log file handle */
+char *LogFileName = NULL;    /**< Log file name */
+int LogFileLevel = 0;        /**< Log file level */
+char *LogFileVersion = NULL; /**< Program version */
+
+/**
+ * LogQueue - In-memory list of log lines
+ */
+struct LogList LogQueue = STAILQ_HEAD_INITIALIZER(LogQueue);
+int LogQueueCount = 0; /**< Number of entries currently in the log queue */
+int LogQueueMax = 0;   /**< Maximum number of entries in the log queue */
+
+/**
+ * timestamp - Create a YYYY-MM-DD HH:MM:SS timestamp
+ * @param stamp Unix time
+ * @retval ptr Timestamp string
+ *
+ * If stamp is 0, then the current time will be used.
+ *
+ * @note This function returns a pointer to a static buffer.
+ *       Do not free it.
+ */
+static const char *timestamp(time_t stamp)
+{
+  static char buf[23] = "";
+  static time_t last = 0;
+
+  if (stamp == 0)
+    stamp = time(NULL);
+
+  if (stamp != last)
+  {
+    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&stamp));
+    last = stamp;
+  }
+
+  return buf;
+}
+
+/**
+ * log_file_close - Close the log file
+ * @param verbose If true, then log the event
+ */
+void log_file_close(bool verbose)
+{
+  if (!LogFileFP)
+    return;
+
+  fprintf(LogFileFP, "[%s] Closing log.\n", timestamp(0));
+  mutt_file_fclose(&LogFileFP);
+  if (verbose)
+    mutt_message(_("Closed log file: %s"), LogFileName);
+}
+
+/**
+ * log_file_open - Start logging to a file
+ * @param verbose If true, then log the event
+ * @retval  0 Success
+ * @retval -1 Error, see errno
+ *
+ * Before opening a log file, call log_file_set_version(), log_file_set_level()
+ * and log_file_set_filename().
+ */
+int log_file_open(bool verbose)
+{
+  if (!LogFileName)
+    return -1;
+
+  if (LogFileFP)
+    log_file_close(false);
+
+  if (LogFileLevel < LL_DEBUG1)
+    return -1;
+
+  LogFileFP = mutt_file_fopen(LogFileName, "a+");
+  if (!LogFileFP)
+    return -1;
+
+  fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
+          NONULL(LogFileVersion), LogFileLevel);
+  if (verbose)
+    mutt_message(_("Debugging at level %d to file '%s'"), LogFileLevel, LogFileName);
+  return 0;
+}
+
+/**
+ * log_file_set_filename - Set the filename for the log
+ * @param file Name to use
+ * @param verbose If true, then log the event
+ * @retval  0 Success, file opened
+ * @retval -1 Error, see errno
+ */
+int log_file_set_filename(const char *file, bool verbose)
+{
+  /* also handles both being NULL */
+  if (mutt_str_strcmp(LogFileName, file) == 0)
+    return 0;
+
+  mutt_str_replace(&LogFileName, file);
+
+  if (!LogFileName)
+    log_file_close(verbose);
+
+  return log_file_open(verbose);
+}
+
+/**
+ * log_file_set_level - Set the logging level
+ * @param level Logging level
+ * @param verbose If true, then log the event
+ * @retval  0 Success
+ * @retval -1 Error, level is out of range
+ *
+ * The level can be between 0 and LL_DEBUG5.
+ */
+int log_file_set_level(int level, bool verbose)
+{
+  if ((level < 0) || (level > 5))
+    return -1;
+
+  if (level == LogFileLevel)
+    return 0;
+
+  LogFileLevel = level;
+
+  if (level == 0)
+  {
+    log_file_close(verbose);
+  }
+  else if (LogFileFP)
+  {
+    if (verbose)
+      mutt_message(_("Logging at level %d to file '%s'"), LogFileLevel, LogFileName);
+    fprintf(LogFileFP, "[%s] NeoMutt%s debugging at level %d\n", timestamp(0),
+            NONULL(LogFileVersion), LogFileLevel);
+  }
+  else
+  {
+    log_file_open(verbose);
+  }
+
+  return 0;
+}
+
+/**
+ * log_file_set_version - Set the program's version number
+ * @param version Version number
+ *
+ * The string will be appended directly to 'NeoMutt', so it should begin with a
+ * hyphen.
+ */
+void log_file_set_version(const char *version)
+{
+  mutt_str_replace(&LogFileVersion, version);
+}
+
+/**
+ * log_file_running - XXX
+ */
+bool log_file_running(void)
+{
+  return (LogFileFP != NULL);
+}
+
+/**
+ * log_disp_file - Save a log line to a file
+ * @param stamp    Unix time (optional)
+ * @param file     Source file (UNUSED)
+ * @param line     Source line (UNUSED)
+ * @param function Source function
+ * @param level    Logging level, e.g. #LL_WARNING
+ * @param ...      Format string and parameters, like printf()
+ * @retval -1 Error
+ * @retval  0 Success, filtered
+ * @retval >0 Success, number of characters written
+ *
+ * This log dispatcher saves a line of text to a file.  The format is:
+ * * `[TIMESTAMP]<LEVEL> FUNCTION() FORMATTED-MESSAGE`
+ *
+ * The caller must first set #LogFileName and #LogFileLevel, then call
+ * log_file_open().  Any logging above #LogFileLevel will be ignored.
+ *
+ * If stamp is 0, then the current time will be used.
+ */
+int log_disp_file(time_t stamp, const char *file, int line,
+                  const char *function, int level, ...)
+{
+  if (!LogFileFP || (level < LL_PERROR) || (level > LogFileLevel))
+    return 0;
+
+  int ret = 0;
+  int err = errno;
+
+  if (!function)
+    function = "UNKNOWN";
+
+  ret += fprintf(LogFileFP, "[%s]<%c> %s() ", timestamp(stamp),
+                 LevelAbbr[level + 3], function);
+
+  va_list ap;
+  va_start(ap, level);
+  const char *fmt = va_arg(ap, const char *);
+  ret = vfprintf(LogFileFP, fmt, ap);
+  va_end(ap);
+
+  if (level == LL_PERROR)
+  {
+    fprintf(LogFileFP, ": %s\n", strerror(err));
+  }
+  else if (level <= 0)
+  {
+    fputs("\n", LogFileFP);
+    ret++;
+  }
+
+  return ret;
+}
+
+/**
+ * log_queue_add - Add a LogLine to the queue
+ * @param ll LogLine to add
+ * @retval num Number of entries in the queue
+ *
+ * If #LogQueueMax is non-zero, the queue will be limited to this many items.
+ */
+int log_queue_add(struct LogLine *ll)
+{
+  STAILQ_INSERT_TAIL(&LogQueue, ll, entries);
+
+  if ((LogQueueMax > 0) && (LogQueueCount >= LogQueueMax))
+  {
+    STAILQ_REMOVE_HEAD(&LogQueue, entries);
+  }
+  else
+  {
+    LogQueueCount++;
+  }
+  return LogQueueCount;
+}
+
+/**
+ * log_queue_set_max_size - Set a upper limit for the queue length
+ * @param size New maximum queue length
+ *
+ * @note size of 0 means unlimited
+ */
+void log_queue_set_max_size(int size)
+{
+  if (size < 0)
+    size = 0;
+  LogQueueMax = size;
+}
+
+/**
+ * log_queue_empty - Free the contents of the queue
+ *
+ * Free any log lines in the queue.
+ */
+void log_queue_empty(void)
+{
+  struct LogLine *ll = NULL;
+  struct LogLine *tmp = NULL;
+
+  STAILQ_FOREACH_SAFE(ll, &LogQueue, entries, tmp)
+  {
+    STAILQ_REMOVE(&LogQueue, ll, LogLine, entries);
+    FREE(&ll->message);
+    FREE(&ll);
+  }
+
+  LogQueueCount = 0;
+}
+
+/**
+ * log_queue_flush - Replay the log queue
+ * @param disp Log dispatcher
+ *
+ * Pass all of the log entries in the queue to the log dispatcher provided.
+ * The queue will be emptied afterwards.
+ */
+void log_queue_flush(log_dispatcher_t disp)
+{
+  struct LogLine *ll = NULL;
+  STAILQ_FOREACH(ll, &LogQueue, entries)
+  {
+    disp(ll->time, ll->file, ll->line, ll->function, ll->level, "%s", ll->message);
+  }
+
+  log_queue_empty();
+}
+
+/**
+ * log_disp_queue - Save a log line to an internal queue
+ * @param stamp    Unix time
+ * @param file     Source file
+ * @param line     Source line
+ * @param function Source function
+ * @param level    Logging level, e.g. #LL_WARNING
+ * @param ...      Format string and parameters, like printf()
+ * @retval >0 Success, number of characters written
+ *
+ * This log dispatcher saves a line of text to a queue.
+ * The format string and parameters are expanded and the other parameters are
+ * stored as they are.
+ *
+ * @sa log_queue_set_max_size(), log_queue_flush(), log_queue_empty()
+ *
+ * @warning Log lines are limited to #LONG_STRING bytes.
+ */
+int log_disp_queue(time_t stamp, const char *file, int line,
+                   const char *function, int level, ...)
+{
+  char buf[LONG_STRING] = "";
+  int err = errno;
+
+  va_list ap;
+  va_start(ap, level);
+  const char *fmt = va_arg(ap, const char *);
+  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  if (level == LL_PERROR)
+  {
+    ret += snprintf(buf + ret, sizeof(buf) - ret, ": %s", strerror(err));
+    level = LL_ERROR;
+  }
+
+  struct LogLine *ll = mutt_mem_calloc(1, sizeof(*ll));
+  ll->time = stamp ? stamp : time(NULL);
+  ll->file = file;
+  ll->line = line;
+  ll->function = function;
+  ll->level = level;
+  ll->message = mutt_str_strdup(buf);
+
+  log_queue_add(ll);
+
+  return ret;
+}
+
+/**
+ * log_disp_terminal - Save a log line to the terminal
+ * @param stamp    Unix time (optional)
+ * @param file     Source file (UNUSED)
+ * @param line     Source line (UNUSED)
+ * @param function Source function
+ * @param level    Logging level, e.g. #LL_WARNING
+ * @param ...      Format string and parameters, like printf()
+ * @retval -1 Error
+ * @retval  0 Success, filtered
+ * @retval >0 Success, number of characters written
+ *
+ * This log dispatcher saves a line of text to the terminal.
+ * The format is:
+ * * `[TIMESTAMP]<LEVEL> FUNCTION() FORMATTED-MESSAGE`
+ *
+ * @note The output will be coloured using ANSI escape sequences,
+ *       unless the output is redirected.
+ */
+int log_disp_terminal(time_t stamp, const char *file, int line,
+                      const char *function, int level, ...)
+{
+  if ((level < LL_PERROR) || (level > LL_MESSAGE))
+    return 0;
+
+  char buf[LONG_STRING];
+
+  va_list ap;
+  va_start(ap, level);
+  const char *fmt = va_arg(ap, const char *);
+  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  log_disp_file(stamp, file, line, function, level, "%s", buf);
+
+  FILE *fp = (level < LL_MESSAGE) ? stderr : stdout;
+  int err = errno;
+  int colour = 0;
+  bool tty = (isatty(fileno(fp)) == 1);
+
+  if (tty)
+  {
+    switch (level)
+    {
+      case LL_PERROR:
+      case LL_ERROR:
+        colour = 31;
+        break;
+      case LL_WARNING:
+        colour = 33;
+        break;
+      case LL_MESSAGE:
+        // colour = 36;
+        break;
+      case LL_DEBUG1:
+      case LL_DEBUG2:
+      case LL_DEBUG3:
+      case LL_DEBUG4:
+      case LL_DEBUG5:
+        break;
+    }
+  }
+
+  if (colour > 0)
+    ret += fprintf(fp, "\033[1;%dm", colour);
+
+  fputs(buf, fp);
+
+  if (level == LL_PERROR)
+    ret += fprintf(fp, ": %s", strerror(err));
+
+  if (colour > 0)
+    ret += fprintf(fp, "\033[0m");
+
+  if (level < 1)
+    ret += fprintf(fp, "\n");
+
+  return ret;
+}
diff --git a/mutt/logging.h b/mutt/logging.h
new file mode 100644 (file)
index 0000000..cf99d07
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * @file
+ * Message logging
+ *
+ * @authors
+ * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _LOGGING_H
+#define _LOGGING_H
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <time.h>
+#include "queue.h"
+
+typedef int (*log_dispatcher_t)(time_t stamp, const char *file, int line, const char *function, int level, ...);
+
+extern log_dispatcher_t MuttLogger;
+
+/**
+ * enum LogLevel - Names for the Logging Levels
+ */
+enum LogLevel
+{
+  LL_PERROR  = -3,
+  LL_ERROR   = -2,
+  LL_WARNING = -1,
+  LL_MESSAGE =  0,
+  LL_DEBUG1  =  1,
+  LL_DEBUG2  =  2,
+  LL_DEBUG3  =  3,
+  LL_DEBUG4  =  4,
+  LL_DEBUG5  =  5,
+};
+
+/**
+ * struct LogLine - A Log line
+ */
+struct LogLine
+{
+  time_t time;
+  const char *file;
+  int line;
+  const char *function;
+  int level;
+  char *message;
+  STAILQ_ENTRY(LogLine) entries;
+};
+
+/**
+ * struct LogList - A list of log lines
+ *
+ * The Log is stored as a STAILQ.
+ * This means that insertions are quick at the head and tail of the list.
+ */
+STAILQ_HEAD(LogList, LogLine);
+
+#define mutt_debug(LEVEL, ...) MuttLogger(0, __FILE__, __LINE__, __func__, LEVEL,      __VA_ARGS__)
+#define mutt_warning(...)      MuttLogger(0, __FILE__, __LINE__, __func__, LL_WARNING, __VA_ARGS__)
+#define mutt_message(...)      MuttLogger(0, __FILE__, __LINE__, __func__, LL_MESSAGE, __VA_ARGS__)
+#define mutt_error(...)        MuttLogger(0, __FILE__, __LINE__, __func__, LL_ERROR,   __VA_ARGS__)
+#define mutt_perror(...)       MuttLogger(0, __FILE__, __LINE__, __func__, LL_PERROR,  __VA_ARGS__)
+
+int log_disp_file    (time_t stamp, const char *file, int line, const char *function, int level, ...);
+int log_disp_queue   (time_t stamp, const char *file, int line, const char *function, int level, ...);
+int log_disp_terminal(time_t stamp, const char *file, int line, const char *function, int level, ...);
+
+int  log_queue_add(struct LogLine *ll);
+void log_queue_empty(void);
+void log_queue_flush(log_dispatcher_t disp);
+void log_queue_set_max_size(int size);
+
+void log_file_close(bool verbose);
+int  log_file_open(bool verbose);
+bool log_file_running(void);
+int  log_file_set_filename(const char *file, bool verbose);
+int  log_file_set_level(int level, bool verbose);
+void log_file_set_version(const char *version);
+
+#endif /* _LOGGING_H */
+
index 065d124c171ebf80972a42c87e09b3823957d680..f2333fcf34ec5869979aafbe620f725073c07893 100644 (file)
@@ -35,6 +35,7 @@
 #include <unistd.h>
 #include "memory.h"
 #include "exit.h"
+#include "logging.h"
 #include "message.h"
 
 /**
diff --git a/mutt/message.c b/mutt/message.c
deleted file mode 100644 (file)
index 78b1dfe..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * @file
- * Message logging
- *
- * @authors
- * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
- *
- * @copyright
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @page message Message logging
- *
- * Display informational messages for the user.
- *
- * These library stubs print the messages to stdout/stderr.
- */
-
-#include "config.h"
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include "message.h"
-
-/**
- * default_error - Display an error message
- * @param format printf-like formatting string
- * @param ...    Arguments to format
- *
- * This stub function writes to stderr.
- */
-static void default_error(const char *format, ...)
-{
-  va_list ap;
-  va_start(ap, format);
-  vfprintf(stderr, format, ap);
-  va_end(ap);
-  fputc('\n', stderr);
-}
-
-/**
- * default_message - Display an informative message
- * @param format printf-like formatting string
- * @param ...    Arguments to format
- *
- * This stub function writes to stdout.
- */
-static void default_message(const char *format, ...)
-{
-  va_list ap;
-  va_start(ap, format);
-  vfprintf(stdout, format, ap);
-  va_end(ap);
-  fputc('\n', stdout);
-}
-
-/**
- * default_perror - Lookup a standard error message (using errno)
- * @param message Prefix message to display
- *
- * This stub function writes to stderr.
- */
-static void default_perror(const char *message)
-{
-  char *p = strerror(errno);
-
-  mutt_error("%s: %s (errno = %d)", message, p ? p : _("unknown error"), errno);
-}
-
-void (*mutt_error)(const char *, ...) = default_error;
-void (*mutt_message)(const char *, ...) = default_message;
-void (*mutt_perror)(const char *) = default_perror;
index 7964fa049d5be8af4a9368af441e0d09ad47750d..ea3351cde2b05674679957110d7cf6fb715165cf 100644 (file)
@@ -36,8 +36,4 @@
 #define N_(a) a
 #endif
 
-extern void (*mutt_error)  (const char *format, ...);
-extern void (*mutt_message)(const char *format, ...);
-extern void (*mutt_perror) (const char *message);
-
 #endif /* _MUTT_MESSAGE_H */
index d77dc40fed65348b4db676b18066ae7fcc4edee9..e19b05bd5819ea095015bcfa881e4d42909f8fcb 100644 (file)
  * | mutt/buffer.c    | @subpage buffer    |
  * | mutt/charset.c   | @subpage charset   |
  * | mutt/date.c      | @subpage date      |
- * | mutt/debug.c     | @subpage debug     |
  * | mutt/exit.c      | @subpage exit      |
  * | mutt/file.c      | @subpage file      |
  * | mutt/hash.c      | @subpage hash      |
  * | mutt/idna.c      | @subpage idna      |
  * | mutt/list.c      | @subpage list      |
+ * | mutt/logging.c   | @subpage logging   |
  * | mutt/mapping.c   | @subpage mapping   |
  * | mutt/mbyte.c     | @subpage mbyte     |
  * | mutt/md5.c       | @subpage md5       |
  * | mutt/memory.c    | @subpage memory    |
- * | mutt/message.c   | @subpage message   |
  * | mutt/mime.c      | @subpage mime      |
  * | mutt/parameter.c | @subpage parameter |
  * | mutt/regex.c     | @subpage regex     |
 #include "buffer.h"
 #include "charset.h"
 #include "date.h"
-#include "debug.h"
 #include "exit.h"
 #include "file.h"
 #include "hash.h"
 #include "idna2.h"
 #include "list.h"
+#include "logging.h"
 #include "mapping.h"
 #include "mbyte.h"
 #include "md5.h"
index 085a3bf8eac9c7c157231a5ad5e634c4413cac77..151e1d6d32229758c11818519ce86379ecf48bbf 100644 (file)
@@ -34,7 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "buffer.h"
-#include "debug.h"
+#include "logging.h"
 #include "mbyte.h"
 #include "memory.h"
 #include "message.h"
index e6dd4ca9d5be8ae115339328d64015e09c6d19b7..92c1b8396ba09ed28d9c79073239991b399cd518 100644 (file)
@@ -34,7 +34,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
-#include "debug.h"
+#include "logging.h"
 #include "memory.h"
 #include "string2.h"
 #ifdef HAVE_SYSEXITS_H
index 10f0b3f81d19a5cee702ab40812783427c6c0aad..b52318333f884d22ab2692e96319522c0d5cf902 100644 (file)
  * @page mutt_logging Mutt Logging
  *
  * Mutt Logging
- *
- * | File                 | Description
- * | :------------------- | :-----------------------------------------------
- * | mutt_clear_error()   | Clear the message line (bottom line of screen)
  */
 
 #include "config.h"
 #include <errno.h>
-#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
 #include <sys/time.h>
 #include <time.h>
 #include "mutt/mutt.h"
 #include "globals.h"
 #include "mutt_curses.h"
+#include "protos.h"
 
 struct timeval LastError = { 0 };
 
+short DebugLevel = 0;    /**< Log file logging level */
+char *DebugFile = NULL;  /**< Log file name */
+const int NumOfLogs = 5; /**< How many log files to rotate */
+bool LogAllowDebugSet = false;
+
 /**
  * micro_elapsed - Number of microseconds between two timevals
  * @param begin Begin time
@@ -61,7 +65,7 @@ static long micro_elapsed(const struct timeval *begin, const struct timeval *end
  *
  * If a second hasn't elapsed since LastError, then wait.
  */
-void error_pause(void)
+static void error_pause(void)
 {
   struct timeval now = { 0 };
 
@@ -82,6 +86,41 @@ void error_pause(void)
   nanosleep(&wait, NULL);
 }
 
+/**
+ * rotate_logs - Rotate a set of numbered files
+ * @param file  Template filename
+ * @param count Maximum number of files
+ * @retval ptr Name of the 0'th file
+ *
+ * Given a template 'temp', rename files numbered 0 to (count-1).
+ *
+ * Rename:
+ * - ...
+ * - temp1 -> temp2
+ * - temp0 -> temp1
+ */
+static const char *rotate_logs(const char *file, int count)
+{
+  if (!file)
+    return NULL;
+
+  char old[PATH_MAX];
+  char new[PATH_MAX];
+
+  /* rotate the old debug logs */
+  for (count -= 2; count >= 0; count--)
+  {
+    snprintf(old, sizeof(old), "%s%d", file, count);
+    snprintf(new, sizeof(new), "%s%d", file, count + 1);
+
+    mutt_expand_path(old, sizeof(old));
+    mutt_expand_path(new, sizeof(new));
+    rename(old, new);
+  }
+
+  return mutt_str_strdup(old);
+}
+
 /**
  * mutt_clear_error - Clear the message line (bottom line of screen)
  */
@@ -96,3 +135,160 @@ void mutt_clear_error(void)
     mutt_window_clearline(MuttMessageWindow, 0);
 }
 
+/**
+ * log_disp_curses - Display a log line in the message line
+ * @param stamp    Unix time
+ * @param file     Source file
+ * @param line     Source line
+ * @param function Source function
+ * @param level    Logging level, e.g. #LL_WARNING
+ * @param ...      Format string and parameters, like printf()
+ * @retval >0 Success, number of characters written
+ */
+int log_disp_curses(time_t stamp, const char *file, int line,
+                    const char *function, int level, ...)
+{
+  if (level > DebugLevel)
+    return 0;
+
+  char buf[LONG_STRING];
+
+  va_list ap;
+  va_start(ap, level);
+  const char *fmt = va_arg(ap, const char *);
+  int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+
+  if (level == LL_PERROR)
+  {
+    char *buf2 = buf + ret;
+    int len = sizeof(buf) - ret;
+    char *p = strerror(errno);
+    if (!p)
+      p = _("unknown error");
+
+    ret += snprintf(buf2, len, ": %s (errno = %d)", p, errno);
+  }
+
+  log_disp_file(stamp, file, line, function, level, "%s", buf);
+
+  /* Don't display debugging message on screen */
+  if (level > LL_MESSAGE)
+    return 0;
+
+  /* Only pause if this is a message following an error */
+  if ((level > LL_ERROR) && OPT_MSG_ERR)
+    error_pause();
+
+  mutt_simple_format(ErrorBuf, sizeof(ErrorBuf), 0, MuttMessageWindow->cols,
+                     FMT_LEFT, 0, buf, sizeof(buf), 0);
+
+  if (!OPT_KEEP_QUIET)
+  {
+    if (level == LL_ERROR)
+      BEEP();
+    SETCOLOR((level == LL_ERROR) ? MT_COLOR_ERROR : MT_COLOR_MESSAGE);
+    mutt_window_mvaddstr(MuttMessageWindow, 0, 0, ErrorBuf);
+    NORMAL_COLOR;
+    mutt_window_clrtoeol(MuttMessageWindow);
+    mutt_refresh();
+  }
+
+  if (level <= LL_ERROR)
+  {
+    OPT_MSG_ERR = true;
+    if (gettimeofday(&LastError, NULL) < 0)
+      mutt_debug(1, "gettimeofday failed: %d\n", errno);
+  }
+  else
+  {
+    OPT_MSG_ERR = false;
+    LastError.tv_sec = 0;
+  }
+
+  return ret;
+}
+
+/**
+ * mutt_log_start - Enable file logging
+ *
+ * This also handles file rotation.
+ */
+int mutt_log_start(void)
+{
+  if (DebugLevel < 1)
+    return 0;
+
+  if (log_file_running())
+    return 0;
+
+  char ver[64];
+  snprintf(ver, sizeof(ver), "-%s%s", PACKAGE_VERSION, GitVer);
+  log_file_set_version(ver);
+
+  const char *name = rotate_logs(DebugFile, NumOfLogs);
+  if (!name)
+    return -1;
+
+  log_file_set_filename(name, false);
+
+  /* This will trigger the file creation */
+  if (log_file_set_level(DebugLevel, true) < 1)
+    return -1;
+
+  return 0;
+}
+
+/**
+ * mutt_log_stop - Close the log file
+ */
+void mutt_log_stop(void)
+{
+  log_file_close(false);
+}
+
+/**
+ * mutt_log_set_level - Change the logging level 
+ * @param level Logging level
+ * @param verbose If true, then log the event
+ * @retval  0 Success
+ * @retval -1 Error, level is out of range
+ */
+int mutt_log_set_level(int level, bool verbose)
+{
+  if (log_file_set_level(level, verbose) != 0)
+    return -1;
+
+  if (!LogAllowDebugSet)
+    return 0;
+
+  DebugLevel = level;
+  return 0;
+}
+
+/**
+ * mutt_log_set_file - Change the logging file
+ * @param file Name to use
+ * @param verbose If true, then log the event
+ * @retval  0 Success, file opened
+ * @retval -1 Error, see errno
+ *
+ * Close the old log, rotate the new logs and open the new log.
+ */
+int mutt_log_set_file(const char *file, bool verbose)
+{
+  if (mutt_str_strcmp(DebugFile, file) == 0)
+    return 0;
+
+  if (!LogAllowDebugSet)
+    return 0;
+
+  const char *name = rotate_logs(file, NumOfLogs);
+  if (!name)
+    return 0;
+
+  log_file_set_filename(name, verbose);
+  mutt_str_replace(&DebugFile, file);
+
+  return 0;
+}
index a252b7807342bbfeb732f8ba746ab26974312464..a59fff3b98d2938a3ae4b00462180ac9288e20c1 100644 (file)
 #ifndef _LOGGING2_H
 #define _LOGGING2_H
 
-#include <sys/time.h>
+#include <time.h>
 
-extern struct timeval LastError;
+extern short DebugLevel;
+extern char *DebugFile;
+extern bool LogAllowDebugSet;
 
-void mutt_clear_error(void);
-void error_pause(void);
+int log_disp_curses(time_t stamp, const char *file, int line, const char *function, int level, ...);
+
+int  mutt_log_start(void);
+void mutt_log_stop(void);
+int mutt_log_set_level(int level, bool verbose);
+int mutt_log_set_file(const char *file, bool verbose);
 
 #endif /* _LOGGING2_H */
index f1a476cd7da0a665dc5b08599246cdd8e6d8cc4d..b21b3ac1446277a1ee726a64c17dff8a4153195a 100644 (file)
--- a/muttlib.c
+++ b/muttlib.c
@@ -1511,37 +1511,6 @@ size_t mutt_realpath(char *buf)
   return mutt_str_strfcpy(buf, s, PATH_MAX);
 }
 
-char debugfilename[_POSIX_PATH_MAX];
-FILE *debugfile = NULL;
-int debuglevel;
-char *debugfile_cmdline = NULL;
-int debuglevel_cmdline;
-
-int mutt_debug_real(const char *function, const char *file, int line, int level, ...)
-{
-  va_list ap;
-  time_t now = time(NULL);
-  static char buf[23] = "";
-  static time_t last = 0;
-  int ret = 0;
-
-  if ((debuglevel < level) || !debugfile)
-    return 0;
-
-  if (now > last)
-  {
-    ret += strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
-    last = now;
-  }
-  ret += fprintf(debugfile, "[%s] %s() ", buf, function);
-
-  va_start(ap, level);
-  const char *fmt = va_arg(ap, const char *);
-  ret += vfprintf(debugfile, fmt, ap);
-  va_end(ap);
-  return ret;
-}
-
 /**
  * mutt_inbox_cmp - do two folders share the same path and one is an inbox
  * @param a First path
diff --git a/nntp.c b/nntp.c
index 3f01d482d193956a4a311acd5475ef7174f3b4d9..b8f941b1820025cdc321726a03eba504975c80d9 100644 (file)
--- a/nntp.c
+++ b/nntp.c
@@ -41,6 +41,7 @@
 #include "mailbox.h"
 #include "mutt_account.h"
 #include "mutt_curses.h"
+#include "mutt_logging.h"
 #include "mutt_socket.h"
 #include "mx.h"
 #include "ncrypt/ncrypt.h"
@@ -405,7 +406,7 @@ static int nntp_auth(struct NntpServer *nserv)
         /* username accepted, sending password */
         if (mutt_str_strncmp("381", buf, 3) == 0)
         {
-          if (debuglevel < MUTT_SOCK_LOG_FULL)
+          if (DebugLevel < MUTT_SOCK_LOG_FULL)
             mutt_debug(MUTT_SOCK_LOG_CMD, "%d> AUTHINFO PASS *\n", conn->fd);
           snprintf(buf, sizeof(buf), "AUTHINFO PASS %s\r\n", conn->account.pass);
           if (mutt_socket_write_d(conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
@@ -465,7 +466,7 @@ static int nntp_auth(struct NntpServer *nserv)
           /* send out client response */
           if (client_len)
           {
-            if (debuglevel >= MUTT_SOCK_LOG_FULL)
+            if (DebugLevel >= MUTT_SOCK_LOG_FULL)
             {
               char tmp[LONG_STRING];
               memcpy(tmp, client_out, client_len);
@@ -490,7 +491,7 @@ static int nntp_auth(struct NntpServer *nserv)
           }
 
           mutt_str_strcat(buf, sizeof(buf), "\r\n");
-          if (debuglevel < MUTT_SOCK_LOG_FULL)
+          if (DebugLevel < MUTT_SOCK_LOG_FULL)
           {
             if (strchr(buf, ' '))
               mutt_debug(MUTT_SOCK_LOG_CMD, "%d> AUTHINFO SASL %s%s\n",
@@ -507,11 +508,11 @@ static int nntp_auth(struct NntpServer *nserv)
           if ((mutt_str_strncmp(inbuf, "283 ", 4) != 0) &&
               (mutt_str_strncmp(inbuf, "383 ", 4) != 0))
           {
-            if (debuglevel < MUTT_SOCK_LOG_FULL)
+            if (DebugLevel < MUTT_SOCK_LOG_FULL)
               mutt_debug(MUTT_SOCK_LOG_CMD, "%d< %s\n", conn->fd, inbuf);
             break;
           }
-          if (debuglevel < MUTT_SOCK_LOG_FULL)
+          if (DebugLevel < MUTT_SOCK_LOG_FULL)
           {
             inbuf[3] = '\0';
             mutt_debug(MUTT_SOCK_LOG_CMD, "%d< %s sasl_data\n", conn->fd, inbuf);
@@ -525,7 +526,7 @@ static int nntp_auth(struct NntpServer *nserv)
             mutt_debug(1, "error base64-decoding server response.\n");
             break;
           }
-          else if (debuglevel >= MUTT_SOCK_LOG_FULL)
+          else if (DebugLevel >= MUTT_SOCK_LOG_FULL)
           {
             char tmp[LONG_STRING];
             memcpy(tmp, buf, len);
diff --git a/pager.c b/pager.c
index baf7c5c6bf773573d320fbf318ea9ea4c4ca9fc5..024b564d59912cf6a8f3658e63afb51cd6a3ed0d 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -1933,7 +1933,9 @@ static void pager_menu_redraw(struct Menu *pager_menu)
                              rd->search_flag | (rd->flags & MUTT_PAGER_NOWRAP),
                          &rd->quote_list, &rd->q_level, &rd->force_redraw,
                          &rd->search_re, rd->pager_window) > 0)
+        {
           rd->lines++;
+        }
         rd->curline++;
         mutt_window_move(rd->pager_window, rd->lines, 0);
       }
@@ -2042,7 +2044,7 @@ int mutt_pager(const char *banner, const char *fname, int flags, struct Pager *e
 
   struct Menu *pager_menu = NULL;
   int old_PagerIndexLines; /* some people want to resize it
-                                         * while inside the pager... */
+                            * while inside the pager... */
   int index_hint = 0;      /* used to restore cursor position */
   int oldcount = -1;
   int check;
index c7b7a9049f2812b7378be6d1538f99685fc733b7..9aa7ff5518b416b57672943a43dc1d7e3bc2ad21 100644 (file)
@@ -29,6 +29,7 @@
 #include "mutt.h"
 #include "globals.h"
 #include "mutt_account.h"
+#include "mutt_logging.h"
 #include "mutt_socket.h"
 #include "options.h"
 #include "pop.h"
@@ -291,7 +292,7 @@ static enum PopAuthRes pop_auth_user(struct PopData *pop_data, const char *metho
     snprintf(buf, sizeof(buf), "PASS %s\r\n", pop_data->conn->account.pass);
     ret = pop_query_d(pop_data, buf, sizeof(buf),
                       /* don't print the password unless we're at the ungodly debugging level */
-                      debuglevel < MUTT_SOCK_LOG_FULL ? "PASS *\r\n" : NULL);
+                      DebugLevel < MUTT_SOCK_LOG_FULL ? "PASS *\r\n" : NULL);
   }
 
   switch (ret)
index 73abab37517f65e5e1551dc56974d65633840c71..10fe555c527ab5fbf640f64d223bc085d8940950 100644 (file)
--- a/protos.h
+++ b/protos.h
@@ -166,8 +166,6 @@ void mutt_make_label_hash(struct Context *ctx);
 void mutt_label_hash_add(struct Context *ctx, struct Header *hdr);
 void mutt_label_hash_remove(struct Context *ctx, struct Header *hdr);
 int mutt_label_complete(char *buffer, size_t len, int numtabs);
-void mutt_curses_error(const char *fmt, ...);
-void mutt_curses_message(const char *fmt, ...);
 void mutt_encode_descriptions(struct Body *b, short recurse);
 void mutt_encode_path(char *dest, size_t dlen, const char *src);
 void mutt_enter_command(void);