From: Richard Russon Date: Tue, 30 Jan 2018 00:41:41 +0000 (+0000) Subject: redirect all messages through a log dispatcher X-Git-Tag: neomutt-20180323~13^2~4 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=81e7278ebe3f2c8e5e4c318de5aed090883ee7bf;p=neomutt redirect all messages through a log dispatcher --- diff --git a/Makefile.autosetup b/Makefile.autosetup index 3525ae40b..fb6b83dfa 100644 --- a/Makefile.autosetup +++ b/Makefile.autosetup @@ -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) diff --git a/conn/getdomain.c b/conn/getdomain.c index 0ed11f63e..ddde39ec1 100644 --- a/conn/getdomain.c +++ b/conn/getdomain.c @@ -36,7 +36,7 @@ #include #include #include -#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); } diff --git a/conn/sasl.c b/conn/sasl.c index 77b25e2c5..214167aa8 100644 --- a/conn/sasl.c +++ b/conn/sasl.c @@ -53,7 +53,7 @@ #include #include #include -#include "mutt/debug.h" +#include "mutt/logging.h" #include "mutt/memory.h" #include "mutt/message.h" #include "mutt/string2.h" diff --git a/conn/ssl.c b/conn/ssl.c index c2d6b24a4..ee749ac12 100644 --- a/conn/ssl.c +++ b/conn/ssl.c @@ -52,7 +52,6 @@ #include #include #include -#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 05d754889..229c69e79 100644 --- 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; } } diff --git a/curs_lib.c b/curs_lib.c index f5b9dd96a..3ad927fa2 100644 --- a/curs_lib.c +++ b/curs_lib.c @@ -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) { diff --git a/globals.h b/globals.h index ede907e09..e8e2cdf01 100644 --- 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; diff --git a/imap/auth_gss.c b/imap/auth_gss.c index 542bf9b09..0c4c1ae6e 100644 --- a/imap/auth_gss.c +++ b/imap/auth_gss.c @@ -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); diff --git a/imap/auth_login.c b/imap/auth_login.c index f9769632e..6f3f89d00 100644 --- a/imap/auth_login.c +++ b/imap/auth_login.c @@ -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); diff --git a/imap/imap.c b/imap/imap.c index 30a5b6256..980b7edbc 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -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 6697c02e1..f59ec8c0c 100644 --- 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 8d5050116..e1528655b 100644 --- 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 4df8bd594..69c81bffc 100644 --- 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 db911b678..04ad119ce 100644 --- 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 */ diff --git a/mutt/date.c b/mutt/date.c index 2a797e0f7..d57264eca 100644 --- a/mutt/date.c +++ b/mutt/date.c @@ -33,7 +33,7 @@ #include #include #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 index 0f059d668..000000000 --- a/mutt/debug.c +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file - * Debug messages - * - * @authors - * Copyright (C) 2017 Richard Russon - * - * @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 . - */ - -/** - * @page debug Debug messages - * - * Output debugging messages, suitable for a developer. - */ - -#include "config.h" -#include -#include - -/** - * 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 index 19646e3ff..000000000 --- a/mutt/debug.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file - * Debug messages - * - * @authors - * Copyright (C) 2017 Richard Russon - * - * @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 . - */ - -#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 */ diff --git a/mutt/file.c b/mutt/file.c index abc568a1c..f543f23a4 100644 --- a/mutt/file.c +++ b/mutt/file.c @@ -42,7 +42,7 @@ #include #include #include "file.h" -#include "debug.h" +#include "logging.h" #include "memory.h" #include "message.h" #include "string2.h" diff --git a/mutt/idna.c b/mutt/idna.c index 459ecaa13..5f200e73b 100644 --- a/mutt/idna.c +++ b/mutt/idna.c @@ -31,8 +31,8 @@ #include #include #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 index 000000000..e1705567e --- /dev/null +++ b/mutt/logging.c @@ -0,0 +1,472 @@ +/** + * @file + * Logging Dispatcher + * + * @authors + * Copyright (C) 2018 Richard Russon + * + * @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 . + */ + +/** + * @page logging Logging Dispatcher + * + * Logging Dispatcher + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#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] 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] 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 index 000000000..cf99d0709 --- /dev/null +++ b/mutt/logging.h @@ -0,0 +1,96 @@ +/** + * @file + * Message logging + * + * @authors + * Copyright (C) 2017 Richard Russon + * + * @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 . + */ + +#ifndef _LOGGING_H +#define _LOGGING_H + +#include +#include +#include +#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 */ + diff --git a/mutt/memory.c b/mutt/memory.c index 065d124c1..f2333fcf3 100644 --- a/mutt/memory.c +++ b/mutt/memory.c @@ -35,6 +35,7 @@ #include #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 index 78b1dfeb5..000000000 --- a/mutt/message.c +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @file - * Message logging - * - * @authors - * Copyright (C) 2017 Richard Russon - * - * @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 . - */ - -/** - * @page message Message logging - * - * Display informational messages for the user. - * - * These library stubs print the messages to stdout/stderr. - */ - -#include "config.h" -#include -#include -#include -#include -#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; diff --git a/mutt/message.h b/mutt/message.h index 7964fa049..ea3351cde 100644 --- a/mutt/message.h +++ b/mutt/message.h @@ -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 */ diff --git a/mutt/mutt.h b/mutt/mutt.h index d77dc40fe..e19b05bd5 100644 --- a/mutt/mutt.h +++ b/mutt/mutt.h @@ -32,17 +32,16 @@ * | 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 | @@ -63,12 +62,12 @@ #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" diff --git a/mutt/regex.c b/mutt/regex.c index 085a3bf8e..151e1d6d3 100644 --- a/mutt/regex.c +++ b/mutt/regex.c @@ -34,7 +34,7 @@ #include #include #include "buffer.h" -#include "debug.h" +#include "logging.h" #include "mbyte.h" #include "memory.h" #include "message.h" diff --git a/mutt/string.c b/mutt/string.c index e6dd4ca9d..92c1b8396 100644 --- a/mutt/string.c +++ b/mutt/string.c @@ -34,7 +34,7 @@ #include #include #include -#include "debug.h" +#include "logging.h" #include "memory.h" #include "string2.h" #ifdef HAVE_SYSEXITS_H diff --git a/mutt_logging.c b/mutt_logging.c index 10f0b3f81..b52318333 100644 --- a/mutt_logging.c +++ b/mutt_logging.c @@ -24,23 +24,27 @@ * @page mutt_logging Mutt Logging * * Mutt Logging - * - * | File | Description - * | :------------------- | :----------------------------------------------- - * | mutt_clear_error() | Clear the message line (bottom line of screen) */ #include "config.h" #include -#include +#include +#include +#include #include #include #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; +} diff --git a/mutt_logging.h b/mutt_logging.h index a252b7807..a59fff3b9 100644 --- a/mutt_logging.h +++ b/mutt_logging.h @@ -23,11 +23,17 @@ #ifndef _LOGGING2_H #define _LOGGING2_H -#include +#include -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 */ diff --git a/muttlib.c b/muttlib.c index f1a476cd7..b21b3ac14 100644 --- 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 3f01d482d..b8f941b18 100644 --- 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 baf7c5c6b..024b564d5 100644 --- 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; diff --git a/pop_auth.c b/pop_auth.c index c7b7a9049..9aa7ff551 100644 --- a/pop_auth.c +++ b/pop_auth.c @@ -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) diff --git a/protos.h b/protos.h index 73abab375..10fe555c5 100644 --- 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);