Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package maildir-utils for openSUSE:Factory checked in at 2023-04-09 20:32:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/maildir-utils (Old) and /work/SRC/openSUSE:Factory/.maildir-utils.new.19717 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "maildir-utils" Sun Apr 9 20:32:58 2023 rev:42 rq:1078170 version:1.10.2 Changes: -------- --- /work/SRC/openSUSE:Factory/maildir-utils/maildir-utils.changes 2023-04-03 00:52:00.506372031 +0200 +++ /work/SRC/openSUSE:Factory/.maildir-utils.new.19717/maildir-utils.changes 2023-04-09 20:32:58.660547577 +0200 @@ -1,0 +2,15 @@ +Sun Apr 9 14:53:13 UTC 2023 - Michael Vetter <mvet...@suse.com> + +- Update to 1.10.2: + * improve unit tests + * fix threading issue with file-based logging + * re-enable --after option for mu cfind + * allow extracting message-parts from message (mu extract) + * allow using relative message paths (view/extract/verify) + Mu4e: + * by default, don't reuse frame with composing in new frame + * allow for disabling global modeline items + * create random name for newly detached views + * documentation update + +------------------------------------------------------------------- Old: ---- mu-1.10.1.tar.xz New: ---- mu-1.10.2.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ maildir-utils.spec ++++++ --- /var/tmp/diff_new_pack.Gy91bo/_old 2023-04-09 20:32:59.404551879 +0200 +++ /var/tmp/diff_new_pack.Gy91bo/_new 2023-04-09 20:32:59.412551925 +0200 @@ -17,7 +17,7 @@ Name: maildir-utils -Version: 1.10.1 +Version: 1.10.2 Release: 0 Summary: Maildir indexer and searcher License: GPL-3.0-or-later ++++++ mu-1.10.1.tar.xz -> mu-1.10.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/Makefile new/mu-1.10.2/Makefile --- old/mu-1.10.1/Makefile 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/Makefile 2023-04-09 14:05:50.000000000 +0200 @@ -50,7 +50,7 @@ @ln -sf $(BUILDDIR)/compile_commands.json $(CURDIR) || /bin/true $(BUILDDIR): - @$(MESON) $(MESON_FLAGS) $(BUILDDIR) + @$(MESON) setup $(MESON_FLAGS) $(BUILDDIR) check: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/README.org new/mu-1.10.2/README.org --- old/mu-1.10.1/README.org 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/README.org 2023-04-09 14:05:50.000000000 +0200 @@ -17,8 +17,8 @@ *Note*: you are looking at the *development* branch, which is where new code is being developed and tested, and which may occasionally break. -Distribution and non-adventurous users are instead recommended to use the [[https://github.com/djcb/mu/tree/release/1.8][1.8 -Release Branch]] or to pick up one of the [[https://github.com/djcb/mu/releases][1.8 Releases]]. +Distributions and non-adventurous users are instead recommended to use the [[https://github.com/djcb/mu/tree/release/1.10][1.10 +Release Branch]] or to pick up one of the [[https://github.com/djcb/mu/releases][1.10 Releases]]. Given the enormous amounts of e-mail many people gather and the importance of e-mail message in our work-flows, it's essential to quickly deal with all that @@ -41,7 +41,7 @@ - ~mu-guile~: bindings for the Guile/Scheme programming language (version 3.0 and later) -~mu~ is written in C and C++; ~mu4e~ is written in ~elisp~ and ~mu-guile~ in a mix of C++ and +~mu~ is written in C++; ~mu4e~ is written in ~elisp~ and ~mu-guile~ in a mix of C++ and Scheme. Note, ~mu~ is available in Linux distributions (e.g. Debian/Ubuntu and Fedora) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-message-part.cc new/mu-1.10.2/lib/message/mu-message-part.cc --- old/mu-1.10.1/lib/message/mu-message-part.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-message-part.cc 2023-04-09 14:05:50.000000000 +0200 @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> +** Copyright (C) 2023 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> ** ** 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 @@ -156,16 +156,18 @@ return mime_object().to_string_opt(); } - - Result<size_t> MessagePart::to_file(const std::string& path, bool overwrite) const noexcept { - if (!mime_object().is_part()) - return Err(Error::Code::InvalidArgument, - "not a part"); - else + if (mime_object().is_part()) return MimePart{mime_object()}.to_file(path, overwrite); + else if (mime_object().is_message_part()) { + if (auto&& msg{MimeMessagePart{mime_object()}.get_message()}; !msg) + return Err(Error::Code::Message, "failed to get message-part"); + else + return msg->to_file(path, overwrite); + } else + return mime_object().to_file(path, overwrite); } bool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-message-part.hh new/mu-1.10.2/lib/message/mu-message-part.hh --- old/mu-1.10.1/lib/message/mu-message-part.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-message-part.hh 2023-04-09 14:05:50.000000000 +0200 @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> +** Copyright (C) 2023 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> ** ** 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 @@ -53,7 +53,6 @@ */ ~MessagePart(); - /** * Get the underlying MimeObject; you need to include mu-mime-object.hh * to do anything useful with it. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-message.cc new/mu-1.10.2/lib/message/mu-message.cc --- old/mu-1.10.1/lib/message/mu-message.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-message.cc 2023-04-09 14:05:50.000000000 +0200 @@ -73,11 +73,13 @@ static void fill_document(Message::Private& priv); static Result<struct stat> -get_statbuf(const std::string& path) +get_statbuf(const std::string& path, Message::Options opts = Message::Options::None) { - if (!g_path_is_absolute(path.c_str())) + if (none_of(opts & Message::Options::AllowRelativePath) && + !g_path_is_absolute(path.c_str())) return Err(Error::Code::File, "path '%s' is not absolute", path.c_str()); + if (::access(path.c_str(), R_OK) != 0) return Err(Error::Code::File, "file @ '%s' is not readable", path.c_str()); @@ -97,7 +99,7 @@ Message::Message(const std::string& path, Message::Options opts): priv_{std::make_unique<Private>(opts)} { - const auto statbuf{get_statbuf(path)}; + const auto statbuf{get_statbuf(path, opts)}; if (!statbuf) throw statbuf.error(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-message.hh new/mu-1.10.2/lib/message/mu-message.hh --- old/mu-1.10.1/lib/message/mu-message.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-message.hh 2023-04-09 14:05:50.000000000 +0200 @@ -45,10 +45,12 @@ class Message { public: enum struct Options { - None = 0, /**< Defaults */ - Decrypt = 1 << 0, /**< Attempt to decrypt */ - RetrieveKeys = 1 << 1, /**< Auto-retrieve crypto keys (implies network - * access) */ + None = 0, /**< Defaults */ + Decrypt = 1 << 0, /**< Attempt to decrypt */ + RetrieveKeys = 1 << 1, /**< Auto-retrieve crypto keys (implies network + * access) */ + AllowRelativePath = 1 << 2, /**< Allow relateive paths for filename + * in make_from_path */ }; /** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-mime-object.cc new/mu-1.10.2/lib/message/mu-mime-object.cc --- old/mu-1.10.1/lib/message/mu-mime-object.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-mime-object.cc 2023-04-09 14:05:50.000000000 +0200 @@ -125,8 +125,6 @@ return hdrs; } - - Result<size_t> MimeObject::write_to_stream(const MimeFormatOptions& f_opts, MimeStream& stream) const @@ -139,6 +137,22 @@ return Ok(static_cast<size_t>(written)); } +Result<size_t> +MimeObject::to_file(const std::string& path, bool overwrite) const noexcept +{ + GError *err{}; + auto strm{g_mime_stream_fs_open(path.c_str(), + O_WRONLY | O_CREAT | O_TRUNC |(overwrite ? 0 : O_EXCL), + S_IRUSR|S_IWUSR, + &err)}; + if (!strm) + return Err(Error::Code::File, &err, "failed to open '%s'", path.c_str()); + + MimeStream stream{MimeStream::make_from_stream(strm)}; + return write_to_stream({}, stream); +} + + Option<std::string> MimeObject::to_string_opt() const noexcept { @@ -525,11 +539,8 @@ buffer.resize(buflen); return buffer; - } - - Result<size_t> MimePart::to_file(const std::string& path, bool overwrite) const noexcept { @@ -558,9 +569,6 @@ return Ok(static_cast<size_t>(written)); } - - - void MimeMultipart::for_each(const ForEachFunc& func) const noexcept { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/mu-mime-object.hh new/mu-1.10.2/lib/message/mu-mime-object.hh --- old/mu-1.10.1/lib/message/mu-mime-object.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/mu-mime-object.hh 2023-04-09 14:05:50.000000000 +0200 @@ -829,6 +829,16 @@ */ Option<std::string> to_string_opt() const noexcept; + /** + * Write object to a file + * + * @param path path to file + * @param overwrite if true, overwrite existing file, if it bqexists + * + * @return size of the wrtten file, or an error. + */ + Result<size_t> to_file(const std::string& path, bool overwrite) const noexcept; + /* * subtypes. */ @@ -1101,7 +1111,6 @@ */ Option<std::string> to_string() const noexcept; - /** * Write part to a file * @@ -1113,7 +1122,6 @@ Result<size_t> to_file(const std::string& path, bool overwrite) const noexcept; - /** * Types of Content Encoding. * diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/message/test-mu-message.cc new/mu-1.10.2/lib/message/test-mu-message.cc --- old/mu-1.10.1/lib/message/test-mu-message.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/message/test-mu-message.cc 2023-04-09 14:05:50.000000000 +0200 @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> +** Copyright (C) 2023 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> ** ** 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 @@ -249,6 +249,10 @@ { auto&& part{message->parts().at(3)}; g_assert_true(part.mime_type() == "message/rfc822"); + + const auto fname{*cache_path + "/msgpart"}; + g_assert_cmpuint(part.to_file(fname, true).value_or(123), ==, 139); + g_assert_true(::access(fname.c_str(), F_OK) == 0); } { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/mu-maildir.cc new/mu-1.10.2/lib/mu-maildir.cc --- old/mu-1.10.1/lib/mu-maildir.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/mu-maildir.cc 2023-04-09 14:05:50.000000000 +0200 @@ -155,7 +155,7 @@ if (auto&& res = check_subdir(src, in_cur); !res) return Err(std::move(res.error())); - char *srcfile{g_path_get_basename(src.c_str())}; + const auto srcfile{to_string_gchar(g_path_get_basename(src.c_str()))}; /* create targetpath; note: make the filename *cough* unique by * including a hash of the srcname in the targetname. This helps if @@ -165,15 +165,13 @@ if (unique_names) fulltargetpath = join_paths(targetpath, in_cur ? "cur" : "new", - format("%u_%s", + format("%08x-%s", g_str_hash(src.c_str()), - srcfile)); + srcfile.c_str())); else fulltargetpath = join_paths(targetpath, in_cur ? "cur" : "new", - srcfile); - g_free(srcfile); - + srcfile.c_str()); return fulltargetpath; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/utils/meson.build new/mu-1.10.2/lib/utils/meson.build --- old/mu-1.10.1/lib/utils/meson.build 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/utils/meson.build 2023-04-09 14:05:50.000000000 +0200 @@ -65,4 +65,10 @@ cpp_args: ['-DBUILD_TESTS'], dependencies: [glib_dep, config_h_dep, lib_mu_utils_dep])) +test('test-logger', + executable('test-logger', 'mu-logger.cc', + install: false, + cpp_args: ['-DBUILD_TESTS'], + dependencies: [glib_dep, lib_mu_utils_dep])) + subdir('tests') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/utils/mu-logger.cc new/mu-1.10.2/lib/utils/mu-logger.cc --- old/mu-1.10.1/lib/utils/mu-logger.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/utils/mu-logger.cc 2023-04-09 14:05:50.000000000 +0200 @@ -28,6 +28,9 @@ #include <fstream> #include <cstring> +#include <thread> +#include <mutex> + #include "mu-logger.hh" using namespace Mu; @@ -36,6 +39,7 @@ static Mu::Logger::Options MuLogOptions; static std::ofstream MuStream; static auto MaxLogFileSize = 1000 * 1024; +static std::mutex logger_mtx; static std::string MuLogPath; @@ -45,6 +49,13 @@ if (MuStream.is_open()) return true; + const auto logdir{to_string_gchar(g_path_get_dirname(MuLogPath.c_str()))}; + if (g_mkdir_with_parents(logdir.c_str(), 0700) != 0) { + std::cerr << "creating " << logdir << " failed:" << g_strerror(errno) + << std::endl; + return false; + } + MuStream.open(MuLogPath, std::ios::out | std::ios::app); if (!MuStream.is_open()) { std::cerr << "opening " << MuLogPath << " failed:" << g_strerror(errno) @@ -84,6 +95,8 @@ static GLogWriterOutput log_file(GLogLevelFlags level, const GLogField* fields, gsize n_fields, gpointer user_data) { + std::lock_guard lock{logger_mtx}; + if (!maybe_open_logfile()) return G_LOG_WRITER_UNHANDLED; @@ -124,7 +137,6 @@ return Ok(Logger(path, opts)); } - Mu::Logger::Logger(const std::string& path, Mu::Logger::Options opts) { if (g_getenv("MU_LOG_STDOUTERR")) @@ -147,7 +159,8 @@ } // log to the journal, or, if not available to a file. - if (log_journal(level, fields, n_fields, user_data) != G_LOG_WRITER_HANDLED) + if (any_of(MuLogOptions & Options::File) || + log_journal(level, fields, n_fields, user_data) != G_LOG_WRITER_HANDLED) return log_file(level, fields, n_fields, user_data); else return G_LOG_WRITER_HANDLED; @@ -172,3 +185,56 @@ MuLogInitialized = false; } + + +#ifdef BUILD_TESTS +#include <vector> +#include <atomic> + +#include "mu-test-utils.hh" + +static void +test_logger_threads(void) +{ + const auto testpath{test_random_tmpdir() + "/test.log"}; + g_message("log-file: %s", testpath.c_str()); + + auto logger = Logger::make(testpath.c_str(), Logger::Options::File | Logger::Options::Debug); + assert_valid_result(logger); + + const auto thread_num = 16; + std::atomic<bool> running = true; + + std::vector<std::thread> threads; + + /* log to the logger file from many threass */ + for (auto n = 0; n != thread_num; ++n) + threads.emplace_back( + std::thread([n,&running]{ + while (running) { + g_debug("log message from thread <%d>", n); + std::this_thread::yield(); + } + })); + + using namespace std::chrono_literals; + std::this_thread::sleep_for(1s); + running = false; + + for (auto n = 0; n != 16; ++n) + if (threads[n].joinable()) + threads[n].join(); +} + + +int +main(int argc, char* argv[]) +{ + mu_test_init(&argc, &argv); + + g_test_add_func("/utils/logger", test_logger_threads); + + return g_test_run(); +} + +#endif /*BUILD_TESTS*/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/utils/mu-logger.hh new/mu-1.10.2/lib/utils/mu-logger.hh --- old/mu-1.10.1/lib/utils/mu-logger.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/utils/mu-logger.hh 2023-04-09 14:05:50.000000000 +0200 @@ -1,5 +1,5 @@ /* -** Copyright (C) 2020-2022 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> +** Copyright (C) 2020-2023 Dirk-Jan C. Binnema <d...@djcbsoftware.nl> ** ** 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 @@ -39,12 +39,12 @@ enum struct Options { None = 0, /**< Nothing specific */ StdOutErr = 1 << 1, /**< Log to stdout/stderr */ - Debug = 1 << 2, /**< Include debug-level logs */ + File = 1 << 2, /**< Force logging to file, even if journal available */ + Debug = 1 << 3, /**< Include debug-level logs */ }; - /** - * Initialize the logging system. + * Initialize the logging sub-system. * * Note that the path is only used if structured logging fails -- * practically, it goes to the file if there's no systemd/journald. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/utils/mu-test-utils.cc new/mu-1.10.2/lib/utils/mu-test-utils.cc --- old/mu-1.10.1/lib/utils/mu-test-utils.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/utils/mu-test-utils.cc 2023-04-09 14:05:50.000000000 +0200 @@ -93,6 +93,11 @@ void Mu::mu_test_init(int *argc, char ***argv) { + const auto tmpdir{test_random_tmpdir()}; + + g_setenv("MU_TEST", "yes", TRUE); + g_setenv("XDG_CACHE_HOME", tmpdir.c_str(), TRUE); + g_test_init(argc, argv, NULL); if (!g_test_verbose()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/lib/utils/mu-test-utils.hh new/mu-1.10.2/lib/utils/mu-test-utils.hh --- old/mu-1.10.1/lib/utils/mu-test-utils.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/lib/utils/mu-test-utils.hh 2023-04-09 14:05:50.000000000 +0200 @@ -36,7 +36,7 @@ } /** - * mu wrapper for g_test_init + * mu wrapper for g_test_init. Sets environment variable MU_TEST to 1. * * @param argc * @param argv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/man/mu-cfind.1.org new/mu-1.10.2/man/mu-cfind.1.org --- old/mu-1.10.1/man/mu-cfind.1.org 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/man/mu-cfind.1.org 2023-04-09 14:05:50.000000000 +0200 @@ -70,15 +70,15 @@ addresses was seen in one of the address fields; this is to exclude addresses only seen in mailing-list messages. See the ~--my-address~ parameter to *mu init*. -# ** --after=<timestamp> only show addresses last seen after -# =<timestamp>=. =<timestamp>= is a UNIX *time_t* value, the number of -# seconds since 1970-01-01 (in UTC). +** --after=<timestamp> only show addresses last seen after +=<timestamp>=. =<timestamp>= is a UNIX *time_t* value, the number of +seconds since 1970-01-01 (in UTC). -# From the command line, you can use the *date* command to get this value. For -# example, only consider addresses last seen after 2009-06-01, you could specify -# #+begin_example -# --after=`date +%s --date='2009-06-01'` -# #+end_example +From the command line, you can use the *date* command to get this value. For +example, only consider addresses last seen after 2020-06-01, you could specify +#+begin_example + --after=`date +%s --date='2020-06-01'` +#+end_example #+include: "muhome.inc" :minlevel 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/meson.build new/mu-1.10.2/meson.build --- old/mu-1.10.1/meson.build 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/meson.build 2023-04-09 14:05:50.000000000 +0200 @@ -17,7 +17,7 @@ ################################################################################ # project setup project('mu', ['c', 'cpp'], - version: '1.10.1', + version: '1.10.2', meson_version: '>= 0.56.0', license: 'GPL-3.0-or-later', default_options : [ @@ -216,3 +216,8 @@ ################################################################################ # write-out config.h configure_file(output : 'config.h', configuration : config_h_data) + +if gmime_dep.version() == '3.2.13' + warning('gmime version 3.2.13 detected, which as a decoding bug') + warning('See: https://github.com/jstedfast/gmime/issues/133') +endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/mu-cmd-find.cc new/mu-1.10.2/mu/mu-cmd-find.cc --- old/mu-1.10.1/mu/mu-cmd-find.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/mu-cmd-find.cc 2023-04-09 14:05:50.000000000 +0200 @@ -178,7 +178,11 @@ else if (info.footer) return true; - if (auto&& res = maildir_link(msg->path(), opts.find.linksdir); !res) { + /* during test, do not create "unique names" (i.e., names with path + * hashes), so we get a predictable result */ + const auto unique_names{!g_getenv("MU_TEST")&&!g_test_initialized()}; + + if (auto&& res = maildir_link(msg->path(), opts.find.linksdir, unique_names); !res) { res.error().fill_g_error(err); return false; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/mu-cmd.cc new/mu-1.10.2/mu/mu-cmd.cc --- old/mu-1.10.1/mu/mu-cmd.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/mu-cmd.cc 2023-04-09 14:05:50.000000000 +0200 @@ -156,12 +156,7 @@ { using Format = Options::View::Format; - // make absolute. - const auto fpath{to_string_opt_gchar(g_canonicalize_filename(fname.c_str(), NULL))}; - if (!fpath) - return Err(Error::Code::File, "invalid file '%s'", fname.c_str()); - - auto message{Message::make_from_path(*fpath, message_options(opts.view))}; + auto message{Message::make_from_path(fname, message_options(opts.view))}; if (!message) return Err(message.error()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/mu-cmd.hh new/mu-1.10.2/mu/mu-cmd.hh --- old/mu-1.10.1/mu/mu-cmd.hh 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/mu-cmd.hh 2023-04-09 14:05:50.000000000 +0200 @@ -40,7 +40,7 @@ constexpr Message::Options message_options(const CmdOpts& cmdopts) { - Message::Options mopts{}; + Message::Options mopts{Message::Options::AllowRelativePath}; if (cmdopts.decrypt) mopts |= Message::Options::Decrypt; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/mu-options.cc new/mu-1.10.2/mu/mu-options.cc --- old/mu-1.10.1/mu/mu-options.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/mu-options.cc 2023-04-09 14:05:50.000000000 +0200 @@ -191,6 +191,10 @@ "Regular expression pattern to match"); sub.add_flag("--personal,-p", opts.cfind.personal, "Only show 'personal' contacts"); + sub.add_option("--after", opts.cfind.after, + "Only show results after some timestamps") + ->type_name("<time_t>") + ->check(CLI::PositiveNumber); sub.add_option("--maxnum,-n", opts.cfind.maxnum, "Maximum number of results") ->type_name("<number>") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/mu.cc new/mu-1.10.2/mu/mu.cc --- old/mu-1.10.1/mu/mu.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/mu.cc 2023-04-09 14:05:50.000000000 +0200 @@ -104,7 +104,6 @@ * there's a subcommand */ - /* * set up logging */ @@ -113,6 +112,8 @@ lopts |= Logger::Options::StdOutErr; if (opts->debug) lopts |= Logger::Options::Debug; + if (g_getenv("MU_TEST")) + lopts |= Logger::Options::File; const auto logger = Logger::make(opts->runtime_path(RuntimePath::LogFile), lopts); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu/tests/test-mu-cmd.cc new/mu-1.10.2/mu/tests/test-mu-cmd.cc --- old/mu-1.10.1/mu/tests/test-mu-cmd.cc 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu/tests/test-mu-cmd.cc 2023-04-09 14:05:50.000000000 +0200 @@ -248,8 +248,9 @@ output = erroutput = NULL; /* furthermore, two symlinks should be there */ - const auto f1{format("%s/cur/3419760385_rfc822.1", tmpdir.c_str())}; - const auto f2{format("%s/cur/3419760386_rfc822.2", tmpdir.c_str())}; + const auto f1{format("%s/cur/rfc822.1", tmpdir.c_str())}; + const auto f2{format("%s/cur/rfc822.2", tmpdir.c_str())}; + g_assert_cmpuint(determine_dtype(f1.c_str(), true), ==, DT_LNK); g_assert_cmpuint(determine_dtype(f2.c_str(), true), ==, DT_LNK); @@ -306,51 +307,51 @@ /* search ("subject:atoms", 1); */ /* } */ -static void -test_mu_extract_01(void) -{ - gchar *cmdline, *output, *erroutput, *tmpdir; - - tmpdir = test_mu_common_get_random_tmpdir(); - g_assert(g_mkdir_with_parents(tmpdir, 0700) == 0); - - cmdline = g_strdup_printf("%s extract %s%cFoo%ccur%cmail5", - MU_PROGRAM, - MU_TESTMAILDIR2, - G_DIR_SEPARATOR, - G_DIR_SEPARATOR, - G_DIR_SEPARATOR); - - if (g_test_verbose()) - g_print("cmd: %s\n", cmdline); - - output = erroutput = NULL; - g_assert(g_spawn_command_line_sync(cmdline, &output, &erroutput, NULL, NULL)); - - // ERROR:test-mu-cmd.cc:347:void test_mu_extract_01(): assertion failed (output == - // "MIME-parts in this message:\n" " 1 <none> text/plain [<none>] (27 bytes)\n" " 2 - // sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n" " 3 custer.jpg image/jpeg - // [inline] (21.6\302\240kB)\n"): - // ("MIME-parts in this message:\n 1 <none> text/plain [<none>] (27 bytes)\n 2 - // sittingbull.jpg image/jpeg [inline] (23.9 kB)\n 3 custer.jpg image/jpeg [inline] (21.6 - // kB)\n" == "MIME-parts in this message:\n 1 <none> text/plain [<none>] (27 bytes)\n 2 - // sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n 3 custer.jpg image/jpeg [inline] - // (21.6\302\240kB)\n") - /* g_assert_cmpstr (output, */ - /* ==, */ - /* "MIME-parts in this message:\n" */ - /* " 1 <none> text/plain [<none>] (27 bytes)\n" */ - /* " 2 sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n" */ - /* " 3 custer.jpg image/jpeg [inline] (21.6\302\240kB)\n"); */ - - /* we expect zero lines of error output */ - g_assert_cmpuint(newlines_in_output(erroutput), ==, 0); - - g_free(output); - g_free(erroutput); - g_free(cmdline); - g_free(tmpdir); -} +// static void +// test_mu_extract_01(void) +// { +// gchar *cmdline, *output, *erroutput, *tmpdir; + +// tmpdir = test_mu_common_get_random_tmpdir(); +// g_assert(g_mkdir_with_parents(tmpdir, 0700) == 0); + +// cmdline = g_strdup_printf("%s extract %s%cFoo%ccur%cmail5", +// MU_PROGRAM, +// MU_TESTMAILDIR2, +// G_DIR_SEPARATOR, +// G_DIR_SEPARATOR, +// G_DIR_SEPARATOR); + +// if (g_test_verbose()) +// g_print("cmd: %s\n", cmdline); + +// output = erroutput = NULL; +// g_assert(g_spawn_command_line_sync(cmdline, &output, &erroutput, NULL, NULL)); + +// // ERROR:test-mu-cmd.cc:347:void test_mu_extract_01(): assertion failed (output == +// // "MIME-parts in this message:\n" " 1 <none> text/plain [<none>] (27 bytes)\n" " 2 +// // sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n" " 3 custer.jpg image/jpeg +// // [inline] (21.6\302\240kB)\n"): +// // ("MIME-parts in this message:\n 1 <none> text/plain [<none>] (27 bytes)\n 2 +// // sittingbull.jpg image/jpeg [inline] (23.9 kB)\n 3 custer.jpg image/jpeg [inline] (21.6 +// // kB)\n" == "MIME-parts in this message:\n 1 <none> text/plain [<none>] (27 bytes)\n 2 +// // sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n 3 custer.jpg image/jpeg [inline] +// // (21.6\302\240kB)\n") +// /* g_assert_cmpstr (output, */ +// /* ==, */ +// /* "MIME-parts in this message:\n" */ +// /* " 1 <none> text/plain [<none>] (27 bytes)\n" */ +// /* " 2 sittingbull.jpg image/jpeg [inline] (23.9\302\240kB)\n" */ +// /* " 3 custer.jpg image/jpeg [inline] (21.6\302\240kB)\n"); */ + +// /* we expect zero lines of error output */ +// g_assert_cmpuint(newlines_in_output(erroutput), ==, 0); + +// g_free(output); +// g_free(erroutput); +// g_free(cmdline); +// g_free(tmpdir); +// } static gint64 get_file_size(const char* path) @@ -820,11 +821,6 @@ { int rv; - /* currently, something is broken on Ubuntu CI (but not elsewhere); - * selectively turn this test off */ - if (!g_getenv("RUN_TEST_MU_CMD")) - return 0; - mu_test_init(&argc, &argv); if (!set_en_us_utf8_locale()) @@ -839,14 +835,13 @@ g_test_add_func("/mu-cmd/test-mu-find-file", test_mu_find_file); g_test_add_func("/mu-cmd/test-mu-find-mime", test_mu_find_mime); - g_test_add_func ("/mu-cmd/test-mu-find-links", test_mu_find_links); g_test_add_func("/mu-cmd/test-mu-find-text-in-rfc822", test_mu_find_text_in_rfc822); g_test_add_func("/mu-cmd/test-mu-find-03", test_mu_find_03); g_test_add_func("/mu-cmd/test-mu-find-maildir-special", test_mu_find_maildir_special); - g_test_add_func("/mu-cmd/test-mu-extract-01", test_mu_extract_01); + // g_test_add_func("/mu-cmd/test-mu-extract-01", test_mu_extract_01); g_test_add_func("/mu-cmd/test-mu-extract-02", test_mu_extract_02); g_test_add_func("/mu-cmd/test-mu-extract-03", test_mu_extract_03); g_test_add_func("/mu-cmd/test-mu-extract-overwrite", test_mu_extract_overwrite); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu4e/TODO new/mu-1.10.2/mu4e/TODO --- old/mu-1.10.1/mu4e/TODO 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu4e/TODO 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -* TODO - -*** mu4e-get-sub-maildirs -*** extract mailing list name -*** mark thread -*** bounce support -*** sorting -*** tool bars -*** refiling-by-pattern -*** inspect message (muile) -*** message statistics -*** include exchange handling / org integration -*** integrate bbdb -*** identity support - - -# Local Variables: -# mode: org; org-startup-folded: nil -# End: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu4e/mu4e-modeline.el new/mu-1.10.2/mu4e/mu4e-modeline.el --- old/mu-1.10.1/mu4e/mu4e-modeline.el 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu4e/mu4e-modeline.el 2023-04-09 14:05:50.000000000 +0200 @@ -48,6 +48,14 @@ :type 'boolean :group 'mu4e-modeline) +(defcustom mu4e-modeline-show-global t + "Whether to populate global modeline segments. + +If non-nil, show both buffer-specific and global modeline items, +otherwise only present buffer-specific information." + :type 'boolean + :group 'mu4e-modeline) + (defvar-local mu4e--modeline-buffer-items nil "List of buffer-local items for the mu4e modeline. Each element is function that evaluates to a string.") @@ -90,15 +98,17 @@ (mapconcat (lambda (func) (or (funcall func) "")) lst " "))) (global-string ;; global string is _cached_ as it may be expensive. - (or mu4e--modeline-global-string-cached - (setq mu4e--modeline-global-string-cached - (funcall collect mu4e--modeline-global-items))))) + (and + mu4e-modeline-show-global + (or mu4e--modeline-global-string-cached + (setq mu4e--modeline-global-string-cached + (funcall collect mu4e--modeline-global-items)))))) (concat ;; (local) buffer items are _not_ cached, so they'll get update ;; automatically when leaving the buffer. (mu4e--modeline-quote-and-truncate (funcall collect mu4e--modeline-buffer-items)) - " " + (and global-string " ") global-string))) (define-minor-mode mu4e-modeline-mode diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu4e/mu4e-view.el new/mu-1.10.2/mu4e/mu4e-view.el --- old/mu-1.10.1/mu4e/mu4e-view.el 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu4e/mu4e-view.el 2023-04-09 14:05:50.000000000 +0200 @@ -327,11 +327,14 @@ (unless mu4e-linked-headers-buffer (mu4e-error "This view buffer is already detached.")) (mu4e-message "Detached view buffer from %s" - (prog1 mu4e-linked-headers-buffer + (progn mu4e-linked-headers-buffer (with-current-buffer mu4e-linked-headers-buffer (when (eq (selected-window) mu4e~headers-view-win) (setq mu4e~headers-view-win nil))) - (setq mu4e-linked-headers-buffer nil)))) + (setq mu4e-linked-headers-buffer nil) + ;; automatically rename mu4e-view-article buffer when + ;; detaching; will get renamed back when reattaching + (rename-buffer (make-temp-name (buffer-name)) t)))) (defun mu4e-view-attach (headers-buffer) "Attaches a view buffer to a headers buffer." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu4e/mu4e-window.el new/mu-1.10.2/mu4e/mu4e-window.el --- old/mu-1.10.1/mu4e/mu4e-window.el 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu4e/mu4e-window.el 2023-04-09 14:05:50.000000000 +0200 @@ -297,22 +297,28 @@ ('(view . vertical) '((window-min-width . fit-window-to-buffer))) (`(,_ . t) nil))) (window-action (cond - ((eq buffer-type 'main) '(display-buffer-full-frame)) + ((eq buffer-type 'main) '(display-buffer-reuse-window + display-buffer-reuse-mode-window + display-buffer-full-frame)) ((and (eq buffer-type 'compose) mu4e-compose-in-new-frame) '(display-buffer-pop-up-frame)) ((memq buffer-type '(headers compose)) - '(display-buffer-reuse-mode-window display-buffer-same-window)) + '(display-buffer-reuse-window + display-buffer-reuse-mode-window + display-buffer-same-window)) ((memq mu4e-split-view '(horizontal vertical)) '(display-buffer-in-direction)) ((memq mu4e-split-view '(single-window)) - '(display-buffer-same-window)) + '(display-buffer-reuse-window + display-buffer-reuse-mode-window + display-buffer-same-window)) ;; I cannot discern a difference between ;; `single-window' and "anything else" in ;; `mu4e-split-view'. - (t '(display-buffer-same-window)))) - (arg `((display-buffer-reuse-window - display-buffer-reuse-mode-window - ,@window-action) + (t '(display-buffer-reuse-window + display-buffer-reuse-mode-window + display-buffer-same-window)))) + (arg `((,@window-action) ,@window-size ,direction ))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mu-1.10.1/mu4e/mu4e.texi new/mu-1.10.2/mu4e/mu4e.texi --- old/mu-1.10.1/mu4e/mu4e.texi 2023-04-02 08:28:53.000000000 +0200 +++ new/mu-1.10.2/mu4e/mu4e.texi 2023-04-09 14:05:50.000000000 +0200 @@ -842,7 +842,9 @@ in such cases, there won't be any further notifications. The @emph{Maildirs} item is very similar to Bookmarks -- consider maildirs here -as being a special kind of bookmark query that matches a Maildir. +as being a special kind of bookmark query that matches a Maildir. You can +configure this using the variable @code{mu4e-maildir-shortcuts}; see its +docstring and @xref{Maildir searches} for more details. @node Miscellaneous @section Miscellaneous @@ -1205,10 +1207,20 @@ easily by customizing @code{display-buffer-alist}, which governs how Emacs -- and thus @t{mu4e} -- must display your buffers. -That means you can instruct @t{mu4e} to place message views in separate tabs or -frames, if you so desire. +Let's look at some examples. + +@subsection Fine-tuning the main buffer display + +By default @t{mu4e}'s main buffer occupies the complete frame, but this can be +changed to use the current window: + +@lisp +(add-to-list 'display-buffer-alist + `(,(regexp-quote mu4e-main-buffer-name) + display-buffer-same-window)) +@end lisp -@subsection Display Customization Example +@subsection Fine-tuning headers buffer display You do not need to configure @code{mu4e-split-view} for this to work. In the absence of explicit rules to the contrary, @t{mu4e} will fall back on the value @@ -1219,9 +1231,10 @@ @lisp (add-to-list 'display-buffer-alist - '(("\\*mu4e-headers\\*" display-buffer-in-side-window - (side . right) - (window-width . 0.5))) + `(,(regexp-quote mu4e-headers-buffer-name) + display-buffer-in-side-window + (side . right) + (window-width . 0.5))) @end lisp You can type @key{C-x w s} to toggle the side windows to hide or show them at @@ -1504,13 +1517,13 @@ typing @key{Z}. You can freely rename a message view buffer -- such as with @key{C-x x -r} -- if you want multiple, open messages. +r} -- if you want a custom, non-randomized name. Detached messages are often useful for workflows involving lots of simultaneous messages. You can @emph{tear off} the window a message is in and place it in a -new tab by typing @key{C-x w ^ f}. You can also detach a window and +new frame by typing @key{C-x w ^ f}. You can also detach a window and put it in its own tab with @key{C-x w ^ t}. @node Editor view @@ -3112,7 +3125,7 @@ handled through a minor-mode @code{mu4e-modeline-mode}, which is enabled by default when @t{mu4e} is running. -To completely turn off the modeline support, set @code{mu4e-support-modeline} to +To completely turn off the modeline support, set @code{mu4e-modeline-support} to @t{nil} before starting @t{mu4e}. @t{mu4e} shares information on the modeline in two ways: @@ -3125,8 +3138,11 @@ @item global: information about the results for the ``favorite query'' @end itemize -All of the bookmark items provide more details in their @code{help-echo}, i.e., -their tooltip. +The global indicators can be disabled by setting @code{mu4e-modeline-show-global} +to @t{nil}. + +All of the bookmark items provide more details in their @code{help-echo}, +i.e., their tooltip. @subsection Query parameters bookmark item The query parameters in the modeline start with the various query flags (such as