# 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)
#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"
{
mutt_str_strfcpy(d, ++p, len);
rc = 0;
- mutt_debug(1, "%s\n", d);
+ mutt_debug(1, "Hostname: %s\n", d);
freeaddrinfo(h);
}
#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"
#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"
++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;
}
}
#include "globals.h"
#include "header.h"
#include "mutt_curses.h"
-#include "mutt_logging.h"
#include "mutt_menu.h"
#include "opcodes.h"
#include "options.h"
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)
{
WHERE short NntpContext;
#endif
-WHERE short DebugLevel;
-WHERE char *DebugFile;
-
WHERE short MenuContext;
WHERE short PagerContext;
WHERE short PagerIndexLines;
#include "auth.h"
#include "globals.h"
#include "mutt_account.h"
+#include "mutt_logging.h"
#include "mutt_socket.h"
#include "options.h"
#include "protos.h"
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);
#include "auth.h"
#include "globals.h"
#include "mutt_account.h"
+#include "mutt_logging.h"
#include "mutt_socket.h"
#include "options.h"
#include "protos.h"
/* 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);
#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"
{
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);
mutt_debug(1, "error during read, %ld bytes read\n", pos);
idata->status = IMAP_FATAL;
+ mutt_buffer_free(&buf);
return -1;
}
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;
}
}
/* 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");
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)
*((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)
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:
{
}
/**
- * 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
int found = 0;
while (envp && *envp)
{
+ /* This will display all matches for "^QUERY" */
if (mutt_str_strncmp(tmp->data, *envp, len) == 0)
{
if (!found)
}
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))
{
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;
*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)
{
if (source_rc(buffer, &err) != 0)
{
- fputs(err.data, stderr);
- fputc('\n', stderr);
+ mutt_error("%s", err.data);
need_pause = 1;
}
}
{
if (source_rc(np->data, &err) != 0)
{
- fputs(err.data, stderr);
- fputc('\n', stderr);
+ mutt_error("%s", err.data);
need_pause = 1;
}
}
if (need_pause && !OPT_NO_CURSES)
{
+ log_queue_flush(log_disp_terminal);
if (mutt_any_key_to_continue(NULL) == 'q')
return 1;
}
#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"
#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"
#endif
char **envlist = NULL;
-void start_debug(void);
void mutt_exit(int code)
{
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;
}
}
#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;
if (version > 0)
{
+ log_queue_flush(log_disp_terminal);
if (version == 1)
print_version();
else
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))
{
* 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) */
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);
else
{
rc = 1;
- mutt_message("%s", np->data);
+ printf("%s\n", np->data);
}
}
mutt_list_free(&alias_queries);
{
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. */
{
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)
}
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)
{
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)
#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;
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 */
#include <string.h>
#include <time.h>
#include "date.h"
-#include "debug.h"
+#include "logging.h"
#include "memory.h"
#include "string2.h"
+++ /dev/null
-/**
- * @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;
-}
+++ /dev/null
-/**
- * @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 */
#include <unistd.h>
#include <utime.h>
#include "file.h"
-#include "debug.h"
+#include "logging.h"
#include "memory.h"
#include "message.h"
#include "string2.h"
#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
--- /dev/null
+/**
+ * @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;
+}
--- /dev/null
+/**
+ * @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 */
+
#include <unistd.h>
#include "memory.h"
#include "exit.h"
+#include "logging.h"
#include "message.h"
/**
+++ /dev/null
-/**
- * @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;
#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 */
* | 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"
#include <stdlib.h>
#include <string.h>
#include "buffer.h"
-#include "debug.h"
+#include "logging.h"
#include "mbyte.h"
#include "memory.h"
#include "message.h"
#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
* @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
*
* If a second hasn't elapsed since LastError, then wait.
*/
-void error_pause(void)
+static void error_pause(void)
{
struct timeval now = { 0 };
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)
*/
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;
+}
#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 */
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
#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"
/* 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 ||
/* 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);
}
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",
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);
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);
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);
}
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;
#include "mutt.h"
#include "globals.h"
#include "mutt_account.h"
+#include "mutt_logging.h"
#include "mutt_socket.h"
#include "options.h"
#include "pop.h"
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)
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);