diff --git a/guile/meson.build b/guile/meson.build index b66a4ecb..394ce152 100644 --- a/guile/meson.build +++ b/guile/meson.build @@ -28,7 +28,7 @@ compile_scm=configure_file( run_command('chmod', '+x', compile_scm, check: true) scm_compiler=join_paths(meson.current_build_dir(), 'compile-scm') -snarf = find_program('guile-snarf') +snarf = find_program('guile-snarf3.0','guile-snarf') # there must be a better way of feeding the include paths to snarf... snarf_args=['-o', '@OUTPUT@', '@INPUT@', '-I' + meson.current_source_dir() + '/..', '-I' + meson.current_source_dir() + '/../lib', diff --git a/guile/mu-guile-message.cc b/guile/mu-guile-message.cc index 6ebcc348..e27fd78a 100644 --- a/guile/mu-guile-message.cc +++ b/guile/mu-guile-message.cc @@ -17,11 +17,18 @@ ** */ #include "mu-guile-message.hh" + +extern "C" { #include "libguile/scm.h" +#include "libguile/strings.h" +} + #include "message/mu-message.hh" +#include "utils/mu-utils.hh" #include #include +#include #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" #include @@ -44,24 +51,51 @@ static std::array SYMB_FLAGS; static SCM SYMB_CONTACT_TO, SYMB_CONTACT_CC, SYMB_CONTACT_BCC, SYMB_CONTACT_FROM; static long MSG_TAG; + +using MessageSPtr = std::unique_ptr; + static gboolean mu_guile_scm_is_msg(SCM scm) { return SCM_NIMP(scm) && (long)SCM_CAR(scm) == MSG_TAG; } -SCM -mu_guile_msg_to_scm(Message&& msg) +static SCM +message_scm_create(Xapian::Document&& doc) { - void *data{scm_gc_malloc(sizeof(Message), "msg")}; - auto msgptr = reinterpret_cast( - scm_gc_malloc(sizeof(MuMsgWrapper), "msg")); - msgwrap->_msg = std::move(msg); - msgwrap->_unrefme = false; + /* placement-new */ - SCM_RETURN_NEWSMOB(MSG_TAG, + void *scm_mem{scm_gc_malloc(sizeof(Message), "msg")}; + Message* msgp = new(scm_mem)Message(std::move(doc)); - msgwrap); + SCM_RETURN_NEWSMOB(MSG_TAG, msgp); +} + +static const Message* +message_from_scm(SCM msg_smob) +{ + return reinterpret_cast(SCM_CDR(msg_smob)); +} + +static size_t +message_scm_free(SCM msg_smob) +{ + if (auto msg = message_from_scm(msg_smob); msg) + msg->~Message(); + + return sizeof(Message); +} + +static int +message_scm_print(SCM msg_smob, SCM port, scm_print_state* pstate) +{ + scm_puts("#path().c_str(), port); + + scm_puts(">", port); + return 1; } struct FlagData { @@ -121,6 +155,13 @@ msg_string_list_field(const Message& msg, Field::Id field_id) return scmlst; } +static SCM +msg_contact_list_field(const Message& msg, Field::Id field_id) +{ + return scm_from_utf8_string( + to_string(msg.document().contacts_value(field_id)).c_str()); +} + static SCM get_body(const Message& msg, bool html) { @@ -139,46 +180,37 @@ SCM_DEFINE(get_field, "Get the field FIELD from message MSG.\n") #define FUNC_NAME s_get_field { - MuMsgWrapper* msgwrap; - size_t field_id; - msgwrap = (MuMsgWrapper*)SCM_CDR(MSG); - const auto& msg{msgwrap->_msg}; - - MU_GUILE_INITIALIZED_OR_ERROR; - SCM_ASSERT(mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); + auto msg{message_from_scm(MSG)}; + SCM_ASSERT(msg, MSG, SCM_ARG1, FUNC_NAME); + SCM_ASSERT(scm_integer_p(FIELD), FIELD, SCM_ARG2, FUNC_NAME); - - field_id = scm_to_int(FIELD); - if (field_id == MU_GUILE_MSG_FIELD_ID_TIMESTAMP) - return scm_from_uint((unsigned)msgwrap->_msg.date()); - - const auto field_opt{field_from_number(static_cast(field_id))}; + const auto field_opt{field_from_number(static_cast(scm_to_int(FIELD)))}; SCM_ASSERT(!!field_opt, FIELD, SCM_ARG2, FUNC_NAME); switch (field_opt->id) { case Field::Id::Priority: - return get_prio_scm(msg); + return get_prio_scm(*msg); case Field::Id::Flags: - return get_flags_scm(msg); - case Field::Id::BodyHtml: - return get_body(msg, true); + return get_flags_scm(*msg); case Field::Id::BodyText: - return get_body(msg, false); - + return get_body(*msg, false); default: break; } switch (field_opt->type) { case Field::Type::String: - return mu_guile_scm_from_string(msg.document().string_value(field_opt->id)); + return mu_guile_scm_from_string(msg->document().string_value(field_opt->id)); case Field::Type::ByteSize: case Field::Type::TimeT: case Field::Type::Integer: - return scm_from_uint(msg.document().integer_value(field_opt->id)); + return scm_from_uint(msg->document().integer_value(field_opt->id)); case Field::Type::StringList: - return msg_string_list_field(msg, field_opt->id); - default: SCM_ASSERT(0, FIELD, SCM_ARG2, FUNC_NAME); + return msg_string_list_field(*msg, field_opt->id); + case Field::Type::ContactList: + return msg_contact_list_field(*msg, field_opt->id); + default: + SCM_ASSERT(0, FIELD, SCM_ARG2, FUNC_NAME); } } #undef FUNC_NAME @@ -211,13 +243,14 @@ SCM_DEFINE(get_contacts, "Get a list of contact information pairs.\n") #define FUNC_NAME s_get_contacts { - MuMsgWrapper* msgwrap; - SCM list; - + SCM list; MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT(mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); + auto msg{message_from_scm(MSG)}; + SCM_ASSERT(msg, MSG, SCM_ARG1, FUNC_NAME); + SCM_ASSERT(scm_symbol_p(CONTACT_TYPE) || scm_is_bool(CONTACT_TYPE), CONTACT_TYPE, SCM_ARG2, @@ -244,11 +277,9 @@ SCM_DEFINE(get_contacts, } } - msgwrap = (MuMsgWrapper*)SCM_CDR(MSG); - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" - list = contacts_to_list(msgwrap->_msg, field_id); + list = contacts_to_list(*msg, field_id); #pragma GCC diagnostic pop /* explicitly close the file backend, so we won't run out of fds */ @@ -269,19 +300,18 @@ SCM_DEFINE(get_parts, "elements which are list of the form (index name mime-type size).\n") #define FUNC_NAME s_get_parts { - MuMsgWrapper* msgwrap; - MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT(mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); + auto msg{message_from_scm(MSG)}; + SCM_ASSERT(msg, MSG, SCM_ARG1, FUNC_NAME); SCM_ASSERT(scm_is_bool(ATTS_ONLY), ATTS_ONLY, SCM_ARG2, FUNC_NAME); SCM attlist = SCM_EOL; /* empty list */ bool attachments_only = ATTS_ONLY == SCM_BOOL_T ? TRUE : FALSE; - const Message& msg{reinterpret_cast(SCM_CDR(MSG))->_msg}; size_t n{}; - for (auto&& part: msg.parts()) { + for (auto&& part: msg->parts()) { if (attachments_only && !part.is_attachment()) continue; @@ -291,7 +321,7 @@ SCM_DEFINE(get_parts, SCM elm = scm_list_5( /* msg */ - mu_guile_scm_from_string(msgwrap->_msg.path()), + mu_guile_scm_from_string(msg->path().c_str()), /* index */ scm_from_uint(n++), /* filename or #f */ @@ -305,7 +335,7 @@ SCM_DEFINE(get_parts, } /* explicitly close the file backend, so we won't run of fds */ - msg.unload_mime_message(); + msg->unload_mime_message(); return attlist; } @@ -323,27 +353,21 @@ SCM_DEFINE(get_header, MU_GUILE_INITIALIZED_OR_ERROR; SCM_ASSERT(mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); + auto msg{message_from_scm(MSG)}; + SCM_ASSERT(msg, MSG, SCM_ARG1, FUNC_NAME); + SCM_ASSERT(scm_is_string(HEADER) || HEADER == SCM_UNDEFINED, HEADER, SCM_ARG2, FUNC_NAME); - const Message& msg{reinterpret_cast(SCM_CDR(MSG))->_msg}; - char *header = scm_to_utf8_string(HEADER); - SCM val = mu_guile_scm_from_string(msg.header(header).value_or("")); + SCM val = mu_guile_scm_from_string(msg->header(header).value_or("")); free(header); /* explicitly close the file backend, so we won't run of fds */ - msg.unload_mime_message(); + msg->unload_mime_message(); return val; } #undef FUNC_NAME - -static Mu::Option -get_query_results(Mu::Store& store, const char* expr, int maxnum) -{ - return store.run_query(expr, {}, Mu::QueryFlags::None, maxnum); -} - SCM_DEFINE(for_each_message, "mu:c:for-each-message", 3, @@ -369,19 +393,18 @@ SCM_DEFINE(for_each_message, return SCM_UNSPECIFIED; /* nothing to do */ if (EXPR == SCM_BOOL_T) - expr = strdup(""); /* note, "" matches *all* messages */ + expr = strdup("\"\""); /* note, "" matches *all* messages */ else expr = scm_to_utf8_string(EXPR); - const auto res{get_query_results(mu_guile_store(), expr, scm_to_int(MAXNUM))}; + const auto res = mu_guile_store().run_query(expr,{}, {}, scm_to_int(MAXNUM)); free(expr); if (!res) return SCM_UNSPECIFIED; for (auto&& mi : *res) { - if (auto msg{mi.message()}; msg) { - auto msgsmob{mu_guile_msg_to_scm(std::move(msg.value()))}; - scm_call_1(FUNC, msgsmob); + if (auto xdoc{mi.document()}; xdoc) { + scm_call_1(FUNC, message_scm_create(std::move(xdoc.value()))); } } @@ -423,7 +446,8 @@ static void define_vars(void) { field_for_each([](auto&& field){ - const auto name{"mu:field:" + std::string{field.name}}; + const auto name{"mu:field:" + + std::string{field.alias.empty() ? field.name : field.alias}}; scm_c_define(name.c_str(), scm_from_uint(field.value_no())); scm_c_export(name.c_str(), NULL); }); @@ -435,43 +459,13 @@ define_vars(void) } -static size_t -msg_free(SCM msg_smob) -{ - MuMsgWrapper* msgwrap; - msgwrap = (MuMsgWrapper*)SCM_CDR(msg_smob); - - if (msgwrap->_unrefme) - mu_msg_unref(msgwrap->_msg); - - return sizeof(MuMsgWrapper); -} - -static int -msg_print(SCM msg_smob, SCM port, scm_print_state* pstate) -{ - MuMsgWrapper* msgwrap; - msgwrap = (MuMsgWrapper*)SCM_CDR(msg_smob); - - scm_puts("#_msg), port); - - scm_puts(">", port); - - return 1; -} - void* mu_guile_message_init(void* data) { - MSG_TAG = scm_make_smob_type("msg", sizeof(MuMsgWrapper)); + MSG_TAG = scm_make_smob_type("message", sizeof(Message)); - scm_set_smob_free(MSG_TAG, msg_free); - scm_set_smob_print(MSG_TAG, msg_print); + scm_set_smob_free(MSG_TAG, message_scm_free); + scm_set_smob_print(MSG_TAG, message_scm_print); define_vars(); define_symbols(); diff --git a/guile/mu-guile.cc b/guile/mu-guile.cc index 2927b6d1..3da8eb32 100644 --- a/guile/mu-guile.cc +++ b/guile/mu-guile.cc @@ -23,9 +23,12 @@ #include #include + #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" +extern "C" { #include +} #pragma GCC diagnostic pop #include @@ -71,26 +74,42 @@ mu_guile_g_error(const char* func_name, GError* err) /* there can be only one */ -static std::unique_ptr StoreSingleton; +static Option StoreSingleton = Nothing; -static gboolean +static bool mu_guile_init_instance(const char* muhome) try { setlocale(LC_ALL, ""); if (!mu_runtime_init(muhome, "guile", true) || StoreSingleton) return FALSE; - StoreSingleton = std::make_unique(mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB)); + const auto path{mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB)}; + auto store = Store::make(path); + if (!store) { + g_critical("error creating store @ %s: %s", path, store.error().what()); + throw store.error(); + } else + StoreSingleton.emplace(std::move(store.value())); g_debug("mu-guile: opened store @ %s (n=%zu); maildir: %s", StoreSingleton->properties().database_path.c_str(), StoreSingleton->size(), StoreSingleton->properties().root_maildir.c_str()); - return TRUE; + return true; +} catch (const Xapian::Error& xerr) { + g_critical("%s: xapian error '%s'", __func__, xerr.get_msg().c_str()); + return false; +} catch (const std::runtime_error& re) { + g_critical("%s: error: %s", __func__, re.what()); + return false; +} catch (const std::exception& e) { + g_critical("%s: caught exception: %s", __func__, e.what()); + return false; } catch (...) { - return FALSE; + g_critical("%s: caught exception", __func__); + return false; } static void @@ -107,7 +126,7 @@ mu_guile_store() if (!StoreSingleton) g_error("mu guile not initialized"); - return *StoreSingleton.get(); + return StoreSingleton.value(); } gboolean @@ -131,7 +150,6 @@ SCM_DEFINE_PUBLIC(mu_initialize, #define FUNC_NAME s_mu_initialize { char* muhome; - gboolean rv; SCM_ASSERT(scm_is_string(MUHOME) || MUHOME == SCM_BOOL_F || SCM_UNBNDP(MUHOME), MUHOME, @@ -146,14 +164,12 @@ SCM_DEFINE_PUBLIC(mu_initialize, else muhome = scm_to_utf8_string(MUHOME); - rv = mu_guile_init_instance(muhome); - if (!rv) { + if (!mu_guile_init_instance(muhome)) { free(muhome); mu_guile_error(FUNC_NAME, 0, "Failed to initialize mu", SCM_UNSPECIFIED); } - g_debug("mu-guile: initialized @ %s (%p)", - muhome ? muhome : "", StoreSingleton.get()); + g_debug("mu-guile: initialized @ %s", muhome ? muhome : ""); free(muhome); /* cleanup when we're exiting */ diff --git a/guile/mu-guile.hh b/guile/mu-guile.hh index 6265995e..ea886a22 100644 --- a/guile/mu-guile.hh +++ b/guile/mu-guile.hh @@ -21,7 +21,9 @@ #define __MU_GUILE_H__ #include +extern "C" { #include +} #include /** diff --git a/guile/tests/meson.build b/guile/tests/meson.build index 12426381..7cb61f5a 100644 --- a/guile/tests/meson.build +++ b/guile/tests/meson.build @@ -25,6 +25,7 @@ test('test-mu-guile', install: false, cpp_args: [ '-DABS_SRCDIR="' + meson.current_source_dir() + '"', - '-DGUILE_LOAD_PATH="' + guile_load_path + '"' + '-DGUILE_LOAD_PATH="' + guile_load_path + '"', + '-DGUILE_EXTENSIONS_PATH="' + guile_load_path + '"' ], dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) diff --git a/guile/tests/test-mu-guile.cc b/guile/tests/test-mu-guile.cc index c0e145b4..b7172bf4 100644 --- a/guile/tests/test-mu-guile.cc +++ b/guile/tests/test-mu-guile.cc @@ -29,64 +29,66 @@ #include "test-mu-common.hh" #include +#include -/* Tests For The command line interface, uses testdir2 */ +using namespace Mu; -static gchar* +static std::string test_dir; + +static std::string fill_database(void) { - gchar * cmdline, *tmpdir; - GError* err; - - tmpdir = test_mu_common_get_random_tmpdir(); - cmdline = g_strdup_printf("/bin/sh -c '" - "%s init --muhome=%s --maildir=%s --quiet; " - "%s index --muhome=%s --quiet'", - MU_PROGRAM, - tmpdir, - MU_TESTMAILDIR2, - MU_PROGRAM, - tmpdir); + const auto cmdline = format( + "/bin/sh -c '" + "%s init --muhome=%s --maildir=%s --quiet; " + "%s index --muhome=%s --quiet'", + MU_PROGRAM, + test_dir.c_str(), + MU_TESTMAILDIR2, + MU_PROGRAM, + test_dir.c_str()); if (g_test_verbose()) - g_print("%s\n", cmdline); + g_print("%s\n", cmdline.c_str()); - err = NULL; - if (!g_spawn_command_line_sync(cmdline, NULL, NULL, NULL, &err)) { + GError *err{}; + if (!g_spawn_command_line_sync(cmdline.c_str(), NULL, NULL, NULL, &err)) { g_printerr("Error: %s\n", err ? err->message : "?"); + g_clear_error(&err); g_assert(0); } - g_free(cmdline); - return tmpdir; + return test_dir; } static void test_something(const char* what) { - char *dir, *cmdline; - gint result; - - g_setenv("GUILE_AUTO_COMPILE", "0", TRUE); g_setenv("GUILE_LOAD_PATH", GUILE_LOAD_PATH, TRUE); - - dir = fill_database(); - cmdline = g_strdup_printf("%s -q -e main %s/test-mu-guile.scm " - "--muhome=%s --test=%s", - GUILE_BINARY, - ABS_SRCDIR, - dir, - what); + g_setenv("GUILE_EXTENSIONS_PATH",GUILE_EXTENSIONS_PATH, TRUE); if (g_test_verbose()) - g_print("cmdline: %s\n", cmdline); + g_print("GUILE_LOAD_PATH: %s\n", GUILE_LOAD_PATH); - result = system(cmdline); - g_assert(result == 0); + const auto dir = fill_database(); + const auto cmdline = format("%s -q -e main %s/test-mu-guile.scm " + "--muhome=%s --test=%s", + GUILE_BINARY, + ABS_SRCDIR, + dir.c_str(), what); - g_free(dir); - g_free(cmdline); + if (g_test_verbose()) + g_print("cmdline: %s\n", cmdline.c_str()); + + GError *err{}; + int status{}; + if (!g_spawn_command_line_sync(cmdline.c_str(), NULL, NULL, &status, &err) || + status != 0) { + g_printerr("Error: %s\n", err ? err->message : "something went wrong"); + g_clear_error(&err); + g_assert(0); + } } static void @@ -111,6 +113,9 @@ int main(int argc, char* argv[]) { int rv; + TempDir tempdir; + test_dir = tempdir.path(); + g_test_init(&argc, &argv, NULL); if (!set_en_us_utf8_locale()) diff --git a/lib/mu-script.cc b/lib/mu-script.cc index 02e3830f..ccecf17b 100644 --- a/lib/mu-script.cc +++ b/lib/mu-script.cc @@ -23,7 +23,9 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" +extern "C" { #include +} #pragma GCC diagnostic pop #endif /*BUILD_GUILE*/ diff --git a/meson.build b/meson.build index 43d7701e..48d382da 100644 --- a/meson.build +++ b/meson.build @@ -179,9 +179,10 @@ endif if not get_option('guile').disabled() and guile_dep.found() config_h_data.set('BUILD_GUILE', 1) - config_h_data.set_quoted('GUILE_BINARY', 'guile') - message('guile is disabled for now') - #subdir('guile') + config_h_data.set_quoted('GUILE_BINARY', + guile_dep.get_pkgconfig_variable('guile')) + #message('guile is disabled for now') + subdir('guile') endif config_h_data.set_quoted('MU_PROGRAM', mu.full_path())