diff --git a/scm/mu-scm-message.cc b/scm/mu-scm-message.cc index c60ef8b0..b39bfd6a 100644 --- a/scm/mu-scm-message.cc +++ b/scm/mu-scm-message.cc @@ -46,7 +46,7 @@ static const Message& to_message(SCM scm, const char *func, int pos) { if (!SCM_IS_A_P(scm, message_type)) - throw ScmError{ScmError::Id::WrongType, func, pos, scm, "mesagestore"}; + throw ScmError{ScmError::Id::WrongType, func, pos, scm, "message"}; return *reinterpret_cast(scm_foreign_object_ref(scm, 0)); } @@ -67,16 +67,19 @@ subr_cc_message_make(SCM message_path_scm) try { constexpr auto func{"cc-message-make"}; // message objects eat fds, tickle the gc... letting it handle it - // automatically is not soon enough. + // automatically is not soon enough. Note: if a script _holds_ + // references to this many messages, the map cannot shrink and this + // triggers a full GC on each call; slow, but better than running + // out of fds. if (message_map.size() >= 0.8 * max_message_map_size) scm_gc(); std::unique_lock lock{map_lock}; - // qttempt to give an good error message rather then getting something + // attempt to give a good error message rather than getting something // from GMime) if (message_map.size() >= max_message_map_size) - throw ScmError{"cc-make-message", "too many open messages"}; + throw ScmError{func, "too many open messages"}; // if we already have the message in our map, return it. auto path{from_scm(message_path_scm, func, 1)}; @@ -103,7 +106,7 @@ subr_cc_message_make(SCM message_path_scm) try { static SCM subr_cc_message_body(SCM message_scm, SCM html_scm) try { - constexpr auto func{"cc-message-make"}; + constexpr auto func{"cc-message-body"}; const auto& message{to_message(message_scm, func, 1)}; const auto html{from_scm(html_scm, func, 2)}; diff --git a/scm/mu-scm-mime.cc b/scm/mu-scm-mime.cc index 8a968f06..e962f4bd 100644 --- a/scm/mu-scm-mime.cc +++ b/scm/mu-scm-mime.cc @@ -80,7 +80,11 @@ make_mime_stream_port_type() { auto ptype = scm_make_port_type(const_cast("mime-stream"), mime_stream_read, {}); - scm_set_port_close(ptype, [](SCM port){g_mime_stream_close(from_scm_port(port));}); + scm_set_port_close(ptype, [](SCM port){ + auto stream{from_scm_port(port)}; + g_mime_stream_close(stream); + g_object_unref(stream); // the port owns the stream + }); scm_set_port_needs_close_on_gc(ptype, true); scm_set_port_seek(ptype, mime_stream_seek); @@ -164,10 +168,10 @@ subr_make_mime_stream_port(SCM mime_part_scm, SCM content_only_scm, GMimeStream *stream{}; try { auto part = part_from_scm(mime_part_scm, func, 1); - const auto decode{from_scm(decode_scm, - func, 2)}; const auto content_only{from_scm(content_only_scm, - func, 3)}; + func, 2)}; + const auto decode{from_scm(decode_scm, + func, 3)}; if (decode) stream = get_decoded_stream(part); else diff --git a/scm/mu-scm-store.cc b/scm/mu-scm-store.cc index 25d1cc2d..37df6b6c 100644 --- a/scm/mu-scm-store.cc +++ b/scm/mu-scm-store.cc @@ -167,9 +167,13 @@ subr_cc_store_mfind(SCM store_scm, SCM query_scm, SCM related_scm, SCM skip_dups SCM msgs{SCM_EOL}; // iterate in reverse order, so the message get consed // into the list in the right order. - for (auto it{qres->end()}; it-- != qres->begin();) - if (auto plist{it.document()->get_data()}; !plist.empty()) + for (auto it{qres->end()}; it-- != qres->begin();) { + const auto doc{it.document()}; + if (!doc) + continue; // e.g., removed since the query ran + if (auto plist{doc->get_data()}; !plist.empty()) msgs = scm_cons(to_scm(plist), msgs); + } return msgs; } catch (const ScmError& err) { diff --git a/scm/mu-scm.cc b/scm/mu-scm.cc index 395c6298..1440417a 100644 --- a/scm/mu-scm.cc +++ b/scm/mu-scm.cc @@ -160,12 +160,12 @@ maybe_remove_socket_path() // opportunistic, so no real warnings, but be careful deleting! - if (const int res = ::stat(sock.c_str(), &statbuf); res != 0) { - mu_debug("can't stat '{}'; err={}", sock, -res); + if (::stat(sock.c_str(), &statbuf) != 0) { + mu_debug("can't stat '{}': {}", sock, ::strerror(errno)); } else if ((statbuf.st_mode & S_IFMT) != S_IFSOCK) { mu_debug("{} is not a socket", sock); - } else if (const int ulres = ::unlink(sock.c_str()); ulres != 0) { - mu_debug("failed to unlink '{}'; err={}", sock, -ulres); + } else if (::unlink(sock.c_str()) != 0) { + mu_debug("failed to unlink '{}': {}", sock, ::strerror(errno)); } else { mu_debug("unlinked {}", sock); } @@ -308,7 +308,7 @@ test_scm_script() MemDb mdb; Config conf{mdb}; -; conf.set( + conf.set( std::vector{"user@example.com"}); auto store{Store::make_new(tempdir.path(), MuTestMaildir, conf)}; diff --git a/scm/mu-scm.hh b/scm/mu-scm.hh index 96cd72ce..62afaa32 100644 --- a/scm/mu-scm.hh +++ b/scm/mu-scm.hh @@ -188,18 +188,22 @@ namespace Mu::Scm { if (!pred) throw ScmError{ScmError::Id::WrongType, func, pos, ARG, expected}; }; + // note: use the C predicates (scm_is_string etc.); the Scheme + // predicates (scm_string_p etc.) return an SCM boolean, which + // is truthy as a C++ bool even when it is #f. using Type = std::remove_const_t; // *not* std::remove_const if constexpr (std::is_same_v) { - ensure(scm_string_p(ARG), ARG, "string"); - auto str{scm_to_utf8_string(ARG)}; - std::string res{str}; + ensure(scm_is_string(ARG), ARG, "string"); + size_t len{}; + auto str{scm_to_utf8_stringn(ARG, &len)}; + std::string res{str, len}; ::free(str); return res; } else if constexpr (std::is_same_v) { - ensure(scm_char_p(ARG), ARG, "character"); - return scm_to_char(ARG); + ensure(SCM_CHARP(ARG), ARG, "character"); + return static_cast(SCM_CHAR(ARG)); } else if constexpr (std::is_same_v) { - ensure(scm_boolean_p(ARG), ARG, "bool"); + ensure(scm_is_bool(ARG), ARG, "bool"); return scm_to_bool(ARG); } else if constexpr (std::is_same_v) { ensure(scm_is_signed_integer(ARG, std::numeric_limits::min(), @@ -292,7 +296,7 @@ namespace Mu::Scm { */ template static inline SCM alist_add(SCM alist, const Key& key, const Value& val, - KeyVals... keyvals) { + KeyVals&&... keyvals) { SCM res = scm_acons(to_scm(key), to_scm(val), alist); return alist_add(res, std::forward(keyvals)...); }