diff --git a/configure.ac b/configure.ac index dc13d86e..2ce4607c 100644 --- a/configure.ac +++ b/configure.ac @@ -271,6 +271,7 @@ toys/Makefile toys/mug/Makefile toys/mug2/Makefile toys/muile/Makefile +toys/procmule/Makefile man/Makefile m4/Makefile contrib/Makefile diff --git a/libmuguile/Makefile.am b/libmuguile/Makefile.am index 6ca617e0..ef5cc35a 100644 --- a/libmuguile/Makefile.am +++ b/libmuguile/Makefile.am @@ -36,6 +36,8 @@ libmuguile_la_SOURCES= \ mu-guile-msg.h \ mu-guile-store.c \ mu-guile-store.h \ + mu-guile-log.c \ + mu-guile-log.h \ mu-guile-common.c \ mu-guile-common.h @@ -45,7 +47,8 @@ libmuguile_la_LIBADD= \ XFILES= \ mu-guile-msg.x \ - mu-guile-store.x + mu-guile-store.x \ + mu-guile-log.x BUILT_SOURCES=$(XFILES) diff --git a/libmuguile/mu-guile-common.c b/libmuguile/mu-guile-common.c index 919f48a5..08e627f0 100644 --- a/libmuguile/mu-guile-common.c +++ b/libmuguile/mu-guile-common.c @@ -21,6 +21,9 @@ #endif /*HAVE_CONFIG_H*/ #include "mu-guile-common.h" +#include "mu-guile-store.h" +#include "mu-guile-msg.h" +#include "mu-guile-log.h" void mu_guile_error (const char *func_name, int status, @@ -45,6 +48,15 @@ mu_guile_g_error (const char *func_name, GError *err) +void +mu_guile_init (void) +{ + scm_with_guile (&mu_guile_msg_init, NULL); + scm_with_guile (&mu_guile_store_init, NULL); + scm_with_guile (&mu_guile_log_init, NULL); +} + + /* * backward compat for pre-2.x guile - note, this will fail miserably * if you don't use a UTF8 locale diff --git a/libmuguile/mu-guile-common.h b/libmuguile/mu-guile-common.h index da82a19d..8bfde07f 100644 --- a/libmuguile/mu-guile-common.h +++ b/libmuguile/mu-guile-common.h @@ -17,8 +17,8 @@ ** */ -#ifndef __MU_GUILE_UTILS_H__ -#define __MU_GUILE_UTILS_H__ +#ifndef __MU_GUILE_COMMON_H__ +#define __MU_GUILE_COMMON_H__ #include @@ -30,8 +30,8 @@ G_BEGIN_DECLS -/** - * +/** + * output an error * * @param func_name * @param status @@ -42,7 +42,7 @@ void mu_guile_error (const char *func_name, int status, const char *fmt, SCM args); -/** +/** * display a GError as a Guile error * * @param func_name function name @@ -50,6 +50,14 @@ void mu_guile_error (const char *func_name, int status, */ void mu_guile_g_error (const char *func_name, GError *err); + +/** + * initialize the mu guile modules + * + */ +void mu_guile_init (void); + + /* compatibility functions for old guile */ #if HAVE_PRE2_GUILE SCM scm_from_utf8_string (const char* str); @@ -58,5 +66,5 @@ char* scm_to_utf8_string (SCM scm); G_END_DECLS -#endif /*__MU_GUILE_UTILS_H__*/ +#endif /*__MU_GUILE_COMMON_H__*/ diff --git a/libmuguile/mu-guile-log.c b/libmuguile/mu-guile-log.c new file mode 100644 index 00000000..4aefade9 --- /dev/null +++ b/libmuguile/mu-guile-log.c @@ -0,0 +1,96 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include "mu-guile-common.h" +#include "mu-guile-log.h" + +enum _LogType { + LOG_INFO, + LOG_WARNING, + LOG_CRITICAL +}; +typedef enum _LogType LogType; + + +static SCM +write_log (LogType logtype, SCM FRM, SCM ARGS) +#define FUNC_NAME __FUNCTION__ +{ + SCM str; + + SCM_ASSERT (scm_is_string(FRM), FRM, SCM_ARG1, ""); + SCM_VALIDATE_REST_ARGUMENT(ARGS); + + str = scm_simple_format (SCM_BOOL_F, FRM, ARGS); + + if (scm_is_string (str)) { + + gchar *output; + output = scm_to_utf8_string (str); + + switch (logtype) { + case LOG_INFO: g_message ("%s", output); break; + case LOG_WARNING: g_warning ("%s", output); break; + case LOG_CRITICAL: g_critical ("%s", output); break; + } + } + + return SCM_UNSPECIFIED; + +#undef FUNC_NAME +} + + +SCM_DEFINE (log_info, "mu:log:info", 1, 0, 1, (SCM FRM, SCM ARGS), + "log some message using a list of ARGS applied to FRM " + "(in 'simple-format' notation).\n") +#define FUNC_NAME s_info +{ + return write_log (LOG_INFO, FRM, ARGS); +} +#undef FUNC_NAME + +SCM_DEFINE (log_warning, "mu:log:warning", 1, 0, 1, (SCM FRM, SCM ARGS), + "log some warning using a list of ARGS applied to FRM (in 'simple-format' " + "notation).\n") +#define FUNC_NAME s_warning +{ + return write_log (LOG_WARNING, FRM, ARGS); +} +#undef FUNC_NAME + +SCM_DEFINE (log_critical, "mu:log:critical", 1, 0, 1, (SCM FRM, SCM ARGS), + "log some critical message using a list of ARGS applied to FRM " + "(in 'simple-format' notation).\n") +#define FUNC_NAME s_critical +{ + return write_log (LOG_CRITICAL, FRM, ARGS); +} +#undef FUNC_NAME + + +void* +mu_guile_log_init (void *data) +{ +#include "mu-guile-log.x" + + return NULL; +} + + diff --git a/libmuguile/mu-guile-log.h b/libmuguile/mu-guile-log.h new file mode 100644 index 00000000..e3bd3709 --- /dev/null +++ b/libmuguile/mu-guile-log.h @@ -0,0 +1,39 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef __MU_GUILE_LOG_H__ +#define __MU_GUILE_LOG_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +/** + * initialize mu logging functions + * + */ +void *mu_guile_log_init (void *data); + + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*__MU_GUILE_LOG_H__*/ diff --git a/libmuguile/mu-guile-msg.c b/libmuguile/mu-guile-msg.c index c9e4c347..ac55be58 100644 --- a/libmuguile/mu-guile-msg.c +++ b/libmuguile/mu-guile-msg.c @@ -76,6 +76,41 @@ SCM_DEFINE (msg_make_from_file, "mu:msg:make-from-file", 1, 0, 0, #undef FUNC_NAME +SCM_DEFINE (msg_move, "mu:msg:move-to-maildir", 2, 0, 0, + (SCM MSG, SCM TARGETMDIR), + "Move message to another maildir TARGETMDIR. Note that this the " + "base-level Maildir, ie. /home/user/Maildir/archive, and must" + " _not_ include the 'cur' or 'new' part. mu_msg_move_to_maildir " + "will make sure that the copy is from new/ to new/ and cur/ to " + "cur/. Also note that the target maildir must be on the same " + "filesystem. Returns #t if it worked, #f otherwise.\n") +#define FUNC_NAME s_msg_move +{ + GError *err; + MuMsgWrapper *msgwrap; + gboolean rv; + + SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME); + SCM_ASSERT (scm_is_string (TARGETMDIR), TARGETMDIR, SCM_ARG2, FUNC_NAME); + + msgwrap = (MuMsgWrapper*) SCM_CDR(MSG); + + err = NULL; + rv = mu_msg_move_to_maildir (msgwrap->_msg, + scm_to_utf8_string (TARGETMDIR), &err); + if (!rv && err) { + mu_guile_g_error (FUNC_NAME, err); + g_error_free (err); + } + + return rv ? SCM_BOOL_T : SCM_BOOL_F; +} +#undef FUNC_NAME + + + + + static SCM scm_from_string_or_null (const char *str) { @@ -146,9 +181,9 @@ SCM_DEFINE (msg_prio, "mu:msg:priority", 1, 0, 0, prio = mu_msg_get_prio (msgwrap->_msg); switch (prio) { - case MU_MSG_PRIO_LOW: return scm_from_locale_symbol("low"); - case MU_MSG_PRIO_NORMAL: return scm_from_locale_symbol("normal"); - case MU_MSG_PRIO_HIGH: return scm_from_locale_symbol("high"); + case MU_MSG_PRIO_LOW: return scm_from_locale_symbol("mu:low"); + case MU_MSG_PRIO_NORMAL: return scm_from_locale_symbol("mu:normal"); + case MU_MSG_PRIO_HIGH: return scm_from_locale_symbol("mu:high"); default: g_return_val_if_reached (SCM_UNDEFINED); } @@ -167,7 +202,12 @@ check_flag (MuMsgFlags flag, FlagData *fdata) { if (fdata->flags & flag) { SCM item; - item = scm_list_1 (scm_from_locale_symbol(mu_msg_flag_name(flag))); + char *flagsym; + + flagsym = g_strconcat ("mu:", mu_msg_flag_name(flag), NULL); + item = scm_list_1 (scm_from_locale_symbol(flagsym)); + g_free (flagsym); + fdata->lst = scm_append_x (scm_list_2(fdata->lst, item)); } } @@ -469,22 +509,22 @@ static void define_symbols (void) { /* message priority */ - scm_c_define ("high", scm_from_int(MU_MSG_PRIO_HIGH)); - scm_c_define ("low", scm_from_int(MU_MSG_PRIO_LOW)); - scm_c_define ("normal", scm_from_int(MU_MSG_PRIO_NORMAL)); + scm_c_define ("mu:high", scm_from_int(MU_MSG_PRIO_HIGH)); + scm_c_define ("mu:low", scm_from_int(MU_MSG_PRIO_LOW)); + scm_c_define ("mu:normal", scm_from_int(MU_MSG_PRIO_NORMAL)); /* message flags */ - scm_c_define ("new", scm_from_int(MU_MSG_FLAG_NEW)); - scm_c_define ("passed", scm_from_int(MU_MSG_FLAG_PASSED)); - scm_c_define ("replied", scm_from_int(MU_MSG_FLAG_REPLIED)); - scm_c_define ("seen", scm_from_int(MU_MSG_FLAG_SEEN)); - scm_c_define ("trashed", scm_from_int(MU_MSG_FLAG_TRASHED)); - scm_c_define ("draft", scm_from_int(MU_MSG_FLAG_DRAFT)); - scm_c_define ("flagged", scm_from_int(MU_MSG_FLAG_FLAGGED)); - scm_c_define ("unread", scm_from_int(MU_MSG_FLAG_UNREAD)); - scm_c_define ("signed", scm_from_int(MU_MSG_FLAG_SIGNED)); - scm_c_define ("encrypted", scm_from_int(MU_MSG_FLAG_ENCRYPTED)); - scm_c_define ("has-attach", scm_from_int(MU_MSG_FLAG_HAS_ATTACH)); + scm_c_define ("mu:new", scm_from_int(MU_MSG_FLAG_NEW)); + scm_c_define ("mu:passed", scm_from_int(MU_MSG_FLAG_PASSED)); + scm_c_define ("mu:replied", scm_from_int(MU_MSG_FLAG_REPLIED)); + scm_c_define ("mu:seen", scm_from_int(MU_MSG_FLAG_SEEN)); + scm_c_define ("mu:trashed", scm_from_int(MU_MSG_FLAG_TRASHED)); + scm_c_define ("mu:draft", scm_from_int(MU_MSG_FLAG_DRAFT)); + scm_c_define ("mu:flagged", scm_from_int(MU_MSG_FLAG_FLAGGED)); + scm_c_define ("mu:unread", scm_from_int(MU_MSG_FLAG_UNREAD)); + scm_c_define ("mu:signed", scm_from_int(MU_MSG_FLAG_SIGNED)); + scm_c_define ("mu:encrypted", scm_from_int(MU_MSG_FLAG_ENCRYPTED)); + scm_c_define ("mu:has-attach", scm_from_int(MU_MSG_FLAG_HAS_ATTACH)); } diff --git a/libmuguile/mu-guile-store.h b/libmuguile/mu-guile-store.h index 4ce295ea..47c60a8d 100644 --- a/libmuguile/mu-guile-store.h +++ b/libmuguile/mu-guile-store.h @@ -25,7 +25,7 @@ extern "C" { #endif /*__cplusplus*/ -/** +/** * initialize mu:store functions * */ diff --git a/src/mu-cmd-find.c b/src/mu-cmd-find.c index 38ec964b..1b9d67f5 100644 --- a/src/mu-cmd-find.c +++ b/src/mu-cmd-find.c @@ -853,7 +853,8 @@ print_attr_sexp (const char* elm, const char *str, gboolean nl) return; /* empty: don't include */ esc = mu_str_escape_c_literal (str); - g_print (" (:%s \"%s\")%s", elm, esc, nl ? "\n" : ""); + + g_print (" :%s \"%s\"%s", elm, esc, nl ? "\n" : ""); g_free (esc); } @@ -864,35 +865,43 @@ output_sexp (MuMsgIter *iter, size_t *count) { MuMsgIter *myiter; size_t mycount; - - g_return_val_if_fail (iter, FALSE); - - g_print ("(:messages\n"); + g_return_val_if_fail (iter, FALSE); + for (myiter = iter, mycount = 0; !mu_msg_iter_is_done (myiter); mu_msg_iter_next (myiter), ++mycount) { + unsigned date, date_high, date_low; + MuMsg *msg; if (!(msg = mu_msg_iter_get_msg (iter, NULL))) /* don't unref */ return FALSE; if (mycount != 0) g_print ("\n"); + + /* emacs likes it's date in a particular way... */ + date = (unsigned) mu_msg_get_date (msg); + date_high = date >> 16; + date_low = date & 0xffff; - g_print (" (:message\n"); + g_print ("(%u\n", (unsigned)mycount); print_attr_sexp ("from", mu_msg_get_from (msg),TRUE); print_attr_sexp ("to", mu_msg_get_to (msg),TRUE); print_attr_sexp ("cc", mu_msg_get_cc (msg),TRUE); print_attr_sexp ("subject", mu_msg_get_subject (msg),TRUE); - g_print (" (:date %u)\n", (unsigned) mu_msg_get_date (msg)); - g_print (" (:size %u)\n", (unsigned) mu_msg_get_size (msg)); + g_print (" :date %u\n", date); + g_print (" :date-high %u\n", date_high); + g_print (" :date-low %u\n", date_low); + g_print (" :size %u\n", (unsigned) mu_msg_get_size (msg)); print_attr_sexp ("msgid", mu_msg_get_msgid (msg),TRUE); print_attr_sexp ("path", mu_msg_get_path (msg),TRUE); print_attr_sexp ("maildir", mu_msg_get_maildir (msg),FALSE); - g_print (")"); + g_print (")\n;;eom"); } - g_print (")\n"); - + + fputs ("\n", stdout); + if (count) *count = mycount; diff --git a/src/mu-config.c b/src/mu-config.c index caaf8f1e..bceab6bf 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -449,7 +449,7 @@ show_usage (gboolean noerror) static void show_version (void) { - g_print ("mu (mail indexer/searcher) " VERSION "\n" + g_print ("mu (mail indexer/searcher) version " VERSION "\n" "Copyright (C) 2008-2011 Dirk-Jan C. Binnema (GPLv3+)\n"); } diff --git a/src/mu-log.c b/src/mu-log.c index 7b341210..3f660758 100644 --- a/src/mu-log.c +++ b/src/mu-log.c @@ -165,23 +165,14 @@ log_file_backup_maybe (const char *logfile) gboolean -mu_log_init (const char* muhome, - gboolean backup, gboolean quiet, gboolean debug) +mu_log_init (const char* logfile, gboolean backup, + gboolean quiet, gboolean debug) { int fd; - gchar *logfile; /* only init once... */ g_return_val_if_fail (!MU_LOG, FALSE); - g_return_val_if_fail (muhome, FALSE); - - if (!mu_util_create_dir_maybe(muhome, 0700)) { - g_warning ("failed to init log in %s", muhome); - return FALSE; - } - - logfile = g_strdup_printf ("%s%c%s", muhome, - G_DIR_SEPARATOR, MU_LOG_FILE); + g_return_val_if_fail (logfile, FALSE); if (backup && !log_file_backup_maybe(logfile)) { g_warning ("failed to backup log file"); @@ -192,7 +183,6 @@ mu_log_init (const char* muhome, if (fd < 0) g_warning ("%s: open() of '%s' failed: %s", __FUNCTION__, logfile, strerror(errno)); - g_free (logfile); if (fd < 0 || !mu_log_init_with_fd (fd, FALSE, quiet, debug)) { try_close (fd); diff --git a/src/mu-log.h b/src/mu-log.h index 7db55c83..323a41f4 100644 --- a/src/mu-log.h +++ b/src/mu-log.h @@ -31,7 +31,8 @@ G_BEGIN_DECLS /** * write logging information to a log file * - * @param muhome the mu home directory + * @param full path to the log file (does not have to exist yet, but + * it's directory must) * @param backup if TRUE and size of log file > MU_MAX_LOG_FILE_SIZE, move * the log file to .old and start a new one. The .old file will overwrite * existing files of that name @@ -40,7 +41,7 @@ G_BEGIN_DECLS * * @return TRUE if initialization succeeds, FALSE otherwise */ -gboolean mu_log_init (const char* muhome, gboolean backup, +gboolean mu_log_init (const char *logfile, gboolean backup, gboolean quiet, gboolean debug) G_GNUC_WARN_UNUSED_RESULT; diff --git a/src/mu-msg-doc.cc b/src/mu-msg-doc.cc index b7fdde99..8bdde617 100644 --- a/src/mu-msg-doc.cc +++ b/src/mu-msg-doc.cc @@ -105,22 +105,22 @@ mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid) g_return_val_if_fail (self, -1); g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), -1); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1); - + /* date is a special case, because we store dates as * strings */ try { const std::string s (self->doc().get_value(mfid)); if (s.empty()) - return -1; + return 0; else if (mfid == MU_MSG_FIELD_ID_DATE) { time_t t; t = mu_date_str_to_time_t (s.c_str(), FALSE/*utc*/); return static_cast(t); - } else + } else { return static_cast(Xapian::sortable_unserialise(s)); - - } MU_XAPIAN_CATCH_BLOCK_RETURN(-1); - + } + + } MU_XAPIAN_CATCH_BLOCK_RETURN(-1); } diff --git a/src/mu-msg-file.h b/src/mu-msg-file.h index a6f992e5..0a800ce5 100644 --- a/src/mu-msg-file.h +++ b/src/mu-msg-file.h @@ -106,9 +106,9 @@ GSList* mu_msg_file_get_str_list_field (MuMsgFile *self, * @param self a valid MuMsgFile * @param msfid the message field id to get (must be string-based one) * - * @return the numeric value, or -1 + * @return the numeric value, or -1 in case of error */ -gint64 mu_msg_file_get_num_field (MuMsgFile *self, MuMsgFieldId msfid); +gint64 mu_msg_file_get_num_field (MuMsgFile *self, MuMsgFieldId mfid); #endif /*__MU_MSG_FILE_H__*/ diff --git a/src/mu-msg.c b/src/mu-msg.c index 073494d3..25b80db4 100644 --- a/src/mu-msg.c +++ b/src/mu-msg.c @@ -669,3 +669,147 @@ mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid) return 0; /* TODO: handle lists */ } + + + + +enum _MaildirType { + MAILDIR_TYPE_CUR, + MAILDIR_TYPE_NEW, + MAILDIR_TYPE_OTHER +}; +typedef enum _MaildirType MaildirType; + +static MaildirType +get_maildir_type (const char *path) +{ + MaildirType mtype; + gchar *dirname; + + dirname = g_path_get_dirname (path); + /* g_path_get_dirname does not specify if the name includes + * the closing '/'... if it does, remove it */ + if (dirname[strlen(dirname) - 1 ] == G_DIR_SEPARATOR) + dirname[strlen(dirname) - 1] = '\0'; + + if (g_str_has_suffix (dirname, "cur")) + mtype = MAILDIR_TYPE_CUR; + else if (g_str_has_suffix (dirname, "new")) + mtype = MAILDIR_TYPE_CUR; + else + mtype = MAILDIR_TYPE_OTHER; + + g_free (dirname); + + return mtype; +} + + +char* +get_new_fullpath (const char *oldpath, const char *targetmdir, + MaildirType mtype) +{ + char *filename, *newfullpath; + const char* mdirsub; + + filename = g_path_get_basename (oldpath); + + if (mtype == MAILDIR_TYPE_CUR) + mdirsub = "cur"; + else if (mtype == MAILDIR_TYPE_NEW) + mdirsub = "new"; + else { + g_free (filename); + g_return_val_if_reached (NULL); + return NULL; + } + + newfullpath = g_strdup_printf ("%s%c%s%c%s", + targetmdir, + G_DIR_SEPARATOR, + mdirsub, + G_DIR_SEPARATOR, + filename); + g_free (filename); + return newfullpath; + +} + + +static gboolean +msg_move (const char* oldpath, const char *newfullpath, GError **err) +{ + if (access (oldpath, R_OK) != 0) { + g_set_error (err, 0, MU_ERROR_FILE, "cannot read %s", + oldpath); + return FALSE; + } + + + if (access (newfullpath, F_OK) == 0) { + g_set_error (err, 0, MU_ERROR_FILE, "%s already exists", + newfullpath); + return FALSE; + } + + if (rename (oldpath, newfullpath) != 0) { + g_set_error (err, 0, MU_ERROR_FILE, "error moving %s to %s", + oldpath, newfullpath); + return FALSE; + } + + /* double check -- is the target really there? */ + if (access (newfullpath, F_OK) != 0) { + g_set_error (err, 0, MU_ERROR_FILE, "can't find target (%s)", + newfullpath); + return FALSE; + } + + if (access (oldpath, F_OK) == 0) { + g_set_error (err, 0, MU_ERROR_FILE, "source is still there (%s)", + oldpath); + return FALSE; + } + + return TRUE; +} + +/* + * move a msg to another maildir, trying to maintain 'integrity', + * ie. msg in 'new/' will go to new/, one in cur/ goes to cur/. be + * super-paranoid here... + */ +gboolean +mu_msg_move_to_maildir (MuMsg *self, const char* targetmdir, GError **err) +{ + const char *oldpath; + MaildirType mtype; + char *newfullpath; + + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (targetmdir, FALSE); + g_return_val_if_fail (g_path_is_absolute(targetmdir), FALSE); + g_return_val_if_fail (mu_util_check_dir (targetmdir, TRUE, TRUE), FALSE); + + oldpath = mu_msg_get_path (self); + + mtype = get_maildir_type (oldpath); + g_return_val_if_fail (mtype==MAILDIR_TYPE_CUR||mtype==MAILDIR_TYPE_NEW, + FALSE); + + newfullpath = get_new_fullpath (oldpath, targetmdir, mtype); + g_return_val_if_fail (newfullpath, FALSE); + + if (!msg_move (oldpath, newfullpath, err)) + goto error; + + /* update our path to new one... */ + mu_msg_cache_set_str (self->_cache, MU_MSG_FIELD_ID_PATH, newfullpath, + TRUE); + return TRUE; + +error: + g_free (newfullpath); + return FALSE; + +} diff --git a/src/mu-msg.h b/src/mu-msg.h index 62162143..d3d55b14 100644 --- a/src/mu-msg.h +++ b/src/mu-msg.h @@ -357,6 +357,28 @@ const GSList* mu_msg_get_tags (MuMsg *self); int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid); + +/** + * move a message to another maildir; the function returns the full + * path to the new message, and changes the msg to now point to the + * new maildir + * + * @param msg a message with an existing file system path in an actual + * maildir + * @param targetmdir the target maildir; note that this the base-level + * Maildir, ie. /home/user/Maildir/archive, and must _not_ include the + * 'cur' or 'new' part. mu_msg_move_to_maildir will make sure that the + * copy is from new/ to new/ and cur/ to cur/. Also note that the target + * maildir must be on the same filesystem. * + * @param err (may be NULL) may contain error information; note if the + * function return FALSE, err is not set for all error condition + * (ie. not for parameter errors) + * @return TRUE if it worked, FALSE otherwise + */ +gboolean mu_msg_move_to_maildir (MuMsg *msg, const char* targetmdir, + GError **err); + + enum _MuMsgContactType { /* Reply-To:? */ MU_MSG_CONTACT_TYPE_TO = 0, MU_MSG_CONTACT_TYPE_FROM, diff --git a/src/mu-runtime.c b/src/mu-runtime.c index 5abd24b1..f0a24f1d 100644 --- a/src/mu-runtime.c +++ b/src/mu-runtime.c @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- +/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ** ** Copyright (C) 2010 Dirk-Jan C. Binnema ** @@ -42,6 +42,7 @@ struct _MuRuntimeData { gchar *_str[MU_RUNTIME_PATH_NUM]; MuConfig *_config; + gchar *_name; /* e.g., 'mu', 'mug' */ }; typedef struct _MuRuntimeData MuRuntimeData; @@ -49,8 +50,10 @@ typedef struct _MuRuntimeData MuRuntimeData; static gboolean _initialized = FALSE; static MuRuntimeData *_data = NULL; -static void runtime_free (void); -static gboolean init_paths (const char* muhome, MuRuntimeData *data); +static void runtime_free (void); +static gboolean init_paths (const char* muhome, MuRuntimeData *data); +static const char* runtime_path (MuRuntimePath path); + static gboolean mu_dir_is_readable_and_writable (const char *muhome) @@ -64,14 +67,38 @@ mu_dir_is_readable_and_writable (const char *muhome) return FALSE; } +static gboolean +init_log (const char *muhome, const char *name, + gboolean log_stderr, gboolean quiet, gboolean debug) +{ + gboolean rv; + char *logpath; + + if (log_stderr) + return mu_log_init_with_fd (fileno(stderr), FALSE, + quiet, debug); + + logpath = g_strdup_printf ("%s%c%s%c%s.log", + muhome, G_DIR_SEPARATOR, + MU_LOG_DIRNAME, G_DIR_SEPARATOR, + name); + rv = mu_log_init (logpath, TRUE, quiet, debug); + g_free (logpath); + + return rv; +} + + + gboolean -mu_runtime_init (const char* muhome_arg) +mu_runtime_init (const char* muhome_arg, const char *name) { gchar *muhome; g_return_val_if_fail (!_initialized, FALSE); - + g_return_val_if_fail (name, FALSE); + if (!mu_util_init_system()) return FALSE; @@ -85,35 +112,28 @@ mu_runtime_init (const char* muhome_arg) return FALSE; } - if (!mu_log_init (muhome, TRUE, FALSE, FALSE)) { + _data = g_new0 (MuRuntimeData, 1); + _data->_str[MU_RUNTIME_PATH_MUHOME] = muhome; + init_paths (muhome, _data); + _data->_name = g_strdup (name); + + if (!init_log (muhome, name, FALSE, TRUE, FALSE)) { + runtime_free (); g_free (muhome); return FALSE; } - _data = g_new0 (MuRuntimeData, 1); - _data->_str[MU_RUNTIME_PATH_MUHOME] = muhome; - init_paths (muhome, _data); - return _initialized = TRUE; } -static gboolean -init_log (MuConfig *opts) -{ - if (opts->log_stderr) - return mu_log_init_with_fd (fileno(stderr), FALSE, - opts->quiet, opts->debug); - else - return mu_log_init (opts->muhome, TRUE, opts->quiet, - opts->debug); -} gboolean -mu_runtime_init_from_cmdline (int *pargc, char ***pargv) +mu_runtime_init_from_cmdline (int *pargc, char ***pargv, const char *name) { - g_return_val_if_fail (!_initialized, FALSE); - + g_return_val_if_fail (!_initialized, FALSE); + g_return_val_if_fail (name, FALSE); + if (!mu_util_init_system()) return FALSE; @@ -129,15 +149,19 @@ mu_runtime_init_from_cmdline (int *pargc, char ***pargv) return FALSE; } - if (!init_log (_data->_config)) { - runtime_free (); - return FALSE; - } - + _data->_name = g_strdup (name); _data->_str[MU_RUNTIME_PATH_MUHOME] = g_strdup (_data->_config->muhome); init_paths (_data->_str[MU_RUNTIME_PATH_MUHOME], _data); + if (!init_log (runtime_path(MU_RUNTIME_PATH_MUHOME), name, + _data->_config->log_stderr, + _data->_config->quiet, + _data->_config->debug)) { + runtime_free (); + return FALSE; + } + return _initialized = TRUE; } @@ -150,6 +174,8 @@ runtime_free (void) for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i) g_free (_data->_str[i]); + g_free (_data->_name); + mu_config_destroy (_data->_config); mu_log_uninit(); @@ -208,15 +234,23 @@ init_paths (const char* muhome, MuRuntimeData *data) G_DIR_SEPARATOR, MU_CONTACTS_FILENAME); data->_str [MU_RUNTIME_PATH_LOG] = - g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, - MU_LOG_DIRNAME); - + g_strdup_printf ("%s%c%s", muhome, + G_DIR_SEPARATOR, MU_LOG_DIRNAME); + if (!create_dirs_maybe (data)) return FALSE; - + return TRUE; } +/* so we can called when _initialized is FALSE still */ +static const char* +runtime_path (MuRuntimePath path) +{ + return _data->_str[path]; +} + + const char* mu_runtime_path (MuRuntimePath path) @@ -224,7 +258,7 @@ mu_runtime_path (MuRuntimePath path) g_return_val_if_fail (_initialized, NULL); g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL); - return _data->_str[path]; + return runtime_path (path); } MuConfig* diff --git a/src/mu-runtime.h b/src/mu-runtime.h index 6a635652..9c3a9421 100644 --- a/src/mu-runtime.h +++ b/src/mu-runtime.h @@ -29,11 +29,13 @@ G_BEGIN_DECLS * initialize the mu runtime system; initializes logging and other * systems. To uninitialize, use mu_runtime_uninit * - * @param muhome path where to find the mu home directory (typicaly, ~/.mu) + * @param muhome path where to find the mu home directory (typicaly, ~/.mu) + * @param name of the main program, ie. 'mu', 'mug' or + * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ -gboolean mu_runtime_init (const char* muhome); +gboolean mu_runtime_init (const char *muhome, const char *name); /** @@ -44,10 +46,13 @@ gboolean mu_runtime_init (const char* muhome); * * @param ptr to the param count (typically, argc) * @param ptr to the params (typically, argv) + * @param name of the main program, ie. 'mu', 'mug' or + * 'procmule'. this influences the name of the e.g. the logfile * * @return TRUE if succeeded, FALSE in case of error */ -gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv); +gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv, + const char *name); /** diff --git a/src/mu.cc b/src/mu.cc index 7bc02199..4cfb6520 100644 --- a/src/mu.cc +++ b/src/mu.cc @@ -29,7 +29,7 @@ main (int argc, char *argv[]) { int rv; - if (!mu_runtime_init_from_cmdline (&argc, &argv)) + if (!mu_runtime_init_from_cmdline (&argc, &argv, "mu")) return 1; rv = mu_config_execute (mu_runtime_config()); diff --git a/src/tests/test-mu-runtime.c b/src/tests/test-mu-runtime.c index f47bd7a3..60fee22e 100644 --- a/src/tests/test-mu-runtime.c +++ b/src/tests/test-mu-runtime.c @@ -38,10 +38,10 @@ test_mu_runtime_init (void) tmpdir = test_mu_common_get_random_tmpdir(); g_assert (tmpdir); - g_assert (mu_runtime_init (tmpdir) == TRUE); + g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE); mu_runtime_uninit (); - g_assert (mu_runtime_init (tmpdir) == TRUE); + g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE); mu_runtime_uninit (); g_free (tmpdir); @@ -62,7 +62,7 @@ test_mu_runtime_data (void) bmfile = g_strdup_printf ("%s%c%s", homedir, G_DIR_SEPARATOR, "bookmarks"); - g_assert (mu_runtime_init (homedir) == TRUE); + g_assert (mu_runtime_init (homedir, "test-mu-runtime") == TRUE); g_assert_cmpstr (homedir, ==, mu_runtime_path (MU_RUNTIME_PATH_MUHOME)); g_assert_cmpstr (xdir, ==, mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB)); diff --git a/toys/Makefile.am b/toys/Makefile.am index aac6132f..dd3ddc90 100644 --- a/toys/Makefile.am +++ b/toys/Makefile.am @@ -32,3 +32,12 @@ endif if HAVE_GUILE SUBDIRS += muile endif + +# for procmule, we need guile and gio +if HAVE_GUILE +if HAVE_GIO +SUBDIRS += procmule +endif +endif + + diff --git a/toys/mug/Makefile.am b/toys/mug/Makefile.am index 235473be..603c875d 100644 --- a/toys/mug/Makefile.am +++ b/toys/mug/Makefile.am @@ -35,7 +35,7 @@ noinst_PROGRAMS= \ # note, mug.cc is '.cc' only because libmu must explicitly # be linked as c++, not c. mug_SOURCES= \ - mug.cc \ + mug.c \ mug-msg-list-view.c \ mug-msg-list-view.h \ mug-msg-view.h \ @@ -43,7 +43,19 @@ mug_SOURCES= \ mug-query-bar.h \ mug-query-bar.c \ mug-shortcuts.c \ - mug-shortcuts.h + mug-shortcuts.h \ + dummy.cc + +# we need to use dummy.cc to enforce c++ linking... +BUILT_SOURCES= \ + dummy.cc + +dummy.cc: + touch dummy.cc + +DISTCLEANFILES= \ + $(BUILT_SOURCES) + mug_LDADD= \ ${top_builddir}/src/libmu.la \ diff --git a/toys/mug/mug.cc b/toys/mug/mug.c similarity index 99% rename from toys/mug/mug.cc rename to toys/mug/mug.c index 2495b4f6..f85bce4c 100644 --- a/toys/mug/mug.cc +++ b/toys/mug/mug.c @@ -369,7 +369,7 @@ main (int argc, char *argv[]) return 1; } - mu_runtime_init (mugdata.muhome); + mu_runtime_init (mugdata.muhome, "mug"); mugshell = mug_shell (&mugdata); g_signal_connect (G_OBJECT (mugshell), "destroy", diff --git a/toys/mug2/Makefile.am b/toys/mug2/Makefile.am index 6f2168c1..7b3062ff 100644 --- a/toys/mug2/Makefile.am +++ b/toys/mug2/Makefile.am @@ -28,13 +28,13 @@ INCLUDES=-I${top_srcdir} -I${top_srcdir}/src $(GTK_CFLAGS) $(WEBKIT_CFLAGS) -DIC AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter -noinst_PROGRAMS= \ +noinst_PROGRAMS= \ mug2 # note, mug.cc is '.cc' only because libmu must explicitly # be linked as c++, not c. mug2_SOURCES= \ - mug.cc \ + mug.c \ mug-msg-list-view.c \ mug-msg-list-view.h \ mug-msg-view.h \ @@ -42,7 +42,19 @@ mug2_SOURCES= \ mug-query-bar.h \ mug-query-bar.c \ mug-shortcuts.c \ - mug-shortcuts.h + mug-shortcuts.h \ + dummy.cc + +# we need to use dummy.cc to enforce c++ linking... +BUILT_SOURCES= \ + dummy.cc + +dummy.cc: + touch dummy.cc + +DISTCLEANFILES= \ + $(BUILT_SOURCES) + mug2_LDADD= \ ${top_builddir}/src/libmu.la \ diff --git a/toys/mug2/mug.cc b/toys/mug2/mug.c similarity index 99% rename from toys/mug2/mug.cc rename to toys/mug2/mug.c index 4c8bde3a..07e29be0 100644 --- a/toys/mug2/mug.cc +++ b/toys/mug2/mug.c @@ -420,7 +420,7 @@ main (int argc, char *argv[]) } g_option_context_free (octx); - mu_runtime_init (mugdata.muhome); + mu_runtime_init (mugdata.muhome, "mug2"); mugshell = mug_shell (&mugdata); g_signal_connect (G_OBJECT (mugshell), "destroy", diff --git a/toys/muile/Makefile.am b/toys/muile/Makefile.am index 938b81b1..64f0fc4a 100644 --- a/toys/muile/Makefile.am +++ b/toys/muile/Makefile.am @@ -29,7 +29,20 @@ noinst_PROGRAMS= \ muile muile_SOURCES= \ - muile.cc + muile.c \ + dummy.cc + +# we need to use dummy.cc to enforce c++ linking... +BUILT_SOURCES= \ + dummy.cc + +dummy.cc: + touch dummy.cc + +DISTCLEANFILES= \ + $(BUILT_SOURCES) + + muile_LDFLAGS= \ ${top_builddir}/libmuguile/libmuguile.la diff --git a/toys/muile/README b/toys/muile/README index 93eca2c2..d4ab1645 100644 --- a/toys/muile/README +++ b/toys/muile/README @@ -8,7 +8,9 @@ `guile'[1] is an interpreter/library for the Scheme programming language[2], specifically meant for extending other programs. It is, in fact, the official GNU language for doing so. 'muile' requires guile 2.x to get the full - support; older versions will not support e.g. the 'mu-stats.scm' things + support. + +older versions will not support e.g. the 'mu-stats.scm' things discussed below. The combination of mu + guile is called `muile', and allows you to write @@ -53,14 +55,17 @@ | scheme@(guile-user)> (mu:msg:subject msg) | $1 = "See me in bikini :-)" | scheme@(guile-user)> (mu:msg:flags msg) - | $2 = (attach unread) + | $2 = (mu:attach mu:unread) `---- and so on. Note, it's probably easiest to explore the various mu: methods - using autocompletion; to enable that make sure you have + using autocompletion; to enable that make sure you have - (use-modules (ice-9 readline)) - (activate-readline) + + ,---- + | (use-modules (ice-9 readline)) + | (activate-readline) + `---- in your ~/.guile configuration. diff --git a/toys/muile/muile.cc b/toys/muile/muile.c similarity index 92% rename from toys/muile/muile.cc rename to toys/muile/muile.c index d55d8abf..259d735a 100644 --- a/toys/muile/muile.cc +++ b/toys/muile/muile.c @@ -23,9 +23,8 @@ #include #include -#include +#include #include -#include struct _MuileConfig { const char *muhome; @@ -97,13 +96,13 @@ main (int argc, char *argv[]) goto error; } - if (!mu_runtime_init (opts->muhome /* NULL is okay */)) { + if (!mu_runtime_init (opts->muhome /* NULL is okay */, + "muile")) { usage (); goto error; } - scm_with_guile (&mu_guile_msg_init, NULL); - scm_with_guile (&mu_guile_store_init, NULL); + mu_guile_init (); /* initialize mu guile modules */ if (opts->msgpath) { if (!(gboolean)scm_with_guile diff --git a/toys/procmule/Makefile.am b/toys/procmule/Makefile.am new file mode 100644 index 00000000..7543376f --- /dev/null +++ b/toys/procmule/Makefile.am @@ -0,0 +1,49 @@ +## Copyright (C) 2011 Dirk-Jan C. Binnema +## +## 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 +## t he Free Software Foundation; either version 3 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, write to the Free Software Foundation, +## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +include $(top_srcdir)/gtest.mk + +INCLUDES=-I${top_srcdir} -I${top_srcdir}/src ${GUILE_CFLAGS} ${GLIB_CFLAGS} + +# don't use -Werror, as it might break on other compilers +# use -Wno-unused-parameters, because some callbacks may not +# really need all the params they get +AM_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement +AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter + +noinst_PROGRAMS= \ + procmule + +procmule_SOURCES= \ + procmule.c \ + dummy.cc + +# we need to use dummy.cc to enforce c++ linking... +BUILT_SOURCES= \ + dummy.cc + +dummy.cc: + touch dummy.cc + +# is this needed? +DISTCLEANFILES= \ + $(BUILT_SOURCES) + +procmule_LDFLAGS= \ + ${top_builddir}/libmuguile/libmuguile.la \ + ${top_builddir}/src/libmu.la \ + ${GIO_LIBS} + diff --git a/toys/procmule/README b/toys/procmule/README new file mode 100644 index 00000000..69d9165d --- /dev/null +++ b/toys/procmule/README @@ -0,0 +1,32 @@ +* README + + The toy here is called 'procmule', which tries to offer procmail[1]-like + functionality from mu, with the procmailrc configuration language replaced + with guile and the mu-guile-bindings. + + Of course, I'm not arrogant enough to claim that I can replace a time-tested, + classical Unix tool so easily. On the other hand, I'm gluing existing tools + together. + + Now, the big difference with procmail is that procmail is an MDA - a mail + delivery agent. In practice, it typically reads a new e-mail message from + standard input (it's called from the local mail daemon), and then decides what + to do with it. + + In contrast, procmule watches a directory (or series of directories) for + changes - as soon as a new message appears in one of these directories, + procmule evaluates a Guile/Scheme program (typically, ~/.mu/procmule.scm) with + the message available as 'mu:current-msg'. + + + + +[1] http://www.procmail.org/ + +# Local Variables: +# mode: org; org-startup-folded: nil +# End: + + + + diff --git a/toys/procmule/procmule.c b/toys/procmule/procmule.c new file mode 100644 index 00000000..5e437685 --- /dev/null +++ b/toys/procmule/procmule.c @@ -0,0 +1,295 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** 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 3, 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, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include +#include +#include +#include + +#include "mu-runtime.h" +#include "mu-util.h" + +struct _ChildData { + char **shell_argv; + int shell_argc; +}; +typedef struct _ChildData ChildData; + +static ChildData * +child_data_new (const char *muhome) +{ + ChildData *data; + + data = g_new0 (ChildData, 1); + + data->shell_argv = g_new0 (char*,3); + data->shell_argc = 0; + + data->shell_argv[data->shell_argc++] = g_strdup ("procmule"); + data->shell_argv[data->shell_argc++] = g_strdup ("-s"); + data->shell_argv[data->shell_argc++] = + g_strdup_printf ("%s%cprocmule.scm", muhome, G_DIR_SEPARATOR); + + return data; +} + +static void +child_data_destroy (ChildData *data) +{ + if (!data) + return; + + g_strfreev (data->shell_argv); + g_free (data); +} + +static void +on_dir_change (GFileMonitor *mon, GFile *file, GFile *other_file, + GFileMonitorEvent event_type, ChildData *data) +{ + gchar *path; + + path = g_file_get_path (file); + + /* ignore all except create events */ + if (event_type != G_FILE_MONITOR_EVENT_CREATED) + return; + + if (fork() == 0) { /* run guile in child */ + + mu_guile_init (); /* initialize mu guile modules */ + + if (!(gboolean)scm_with_guile + ((MuGuileFunc*)&mu_guile_msg_load_current, path)) { + g_warning ("failed to set message in guile env"); + return; + } + scm_shell (data->shell_argc, data->shell_argv); /* never returns */ + } + + g_free (path); +} + +static GFileMonitor* +create_monitor (const char *path, ChildData *data) +{ + GFile *dir; + GFileMonitor *dirmon; + GError *err; + + if (!mu_util_check_dir (path, TRUE, FALSE)) { + g_warning ("must be a readable dir: '%s'", path); + return NULL; + } + + dir = g_file_new_for_path (path); + + err = NULL; + dirmon = g_file_monitor_directory (dir, G_FILE_MONITOR_NONE, + NULL, &err); + if (!dirmon) { + g_warning ("error adding monitor: %s", err->message); + g_error_free (err); + } + + g_object_unref (dir); + + if (dirmon) + g_signal_connect (dirmon, "changed", + G_CALLBACK(on_dir_change), data); + + return dirmon; +} + +static void +destroy_watchlist (GSList *lst) +{ + g_slist_foreach (lst, (GFunc)g_object_unref, NULL); + g_slist_free (lst); +} + + +GSList* +create_watchlist (char **dirs, ChildData *data) +{ + GSList *watchlist; + char **cur; + + /* TODO: check for dups */ + for (watchlist = NULL, cur = dirs; cur && *cur; ++cur) { + GFileMonitor *dirmon; + dirmon = create_monitor (*cur, data); + if (!dirmon) { + destroy_watchlist (watchlist); + return NULL; + } + watchlist = g_slist_prepend (watchlist, dirmon); + } + + return watchlist; +} + + +struct _PMConfig { + char *muhome; + char **watchdirs; + +}; +typedef struct _PMConfig PMConfig; + + +static void +expand_paths (PMConfig *opts) +{ + char **cur; + + for (cur = opts->watchdirs; cur && *cur; ++cur) + *cur = mu_util_dir_expand (*cur); + + if (opts->muhome) + opts->muhome = mu_util_dir_expand (opts->muhome); +} + + +static PMConfig * +pm_config_new (int *argcp, char ***argvp) +{ + GOptionContext *octx; + + PMConfig *opts = g_new0 (PMConfig, 1); + GOptionEntry entries[] = { + {"muhome", 0, 0, G_OPTION_ARG_FILENAME, &opts->muhome, + "specify an alternative mu directory", NULL}, + {"watch", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opts->watchdirs, + "directory to watch (may be specified multiple times)", NULL}, + {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}/* sentinel */ + }; + + octx = g_option_context_new ("- procmule options"); + g_option_context_add_main_entries (octx, entries, "Procmule"); + + if (!g_option_context_parse (octx, argcp, argvp, NULL)) { + g_printerr ("error in options\n"); + goto error; + } + + if (!opts->watchdirs) { + g_printerr ("specify at least one --watch=\n"); + goto error; + } + + expand_paths (opts); + + g_option_context_free (octx); + return opts; + +error: + if (octx) + g_option_context_free (octx); + g_free (opts); + return NULL; +} + +static void +pm_config_destroy (PMConfig *conf) +{ + if (!conf) + return; + + g_free (conf->muhome); + g_strfreev (conf->watchdirs); + + g_free (conf); +} + + +static void +usage (void) +{ + g_print ("usage: procmule [--muhome=] [--watch=]\n"); + g_print ("also, see toys/procmule/README\n"); +} + + +static gboolean +watch_dirs (char **watchdirs) +{ + ChildData *child_data; + GSList *watchlist; + GMainLoop *loop; + + child_data = child_data_new + (mu_runtime_path(MU_RUNTIME_PATH_MUHOME)); + + watchlist = create_watchlist (watchdirs, child_data); + if (!watchlist) + goto error; + + loop = g_main_loop_new (NULL, TRUE); + + g_main_loop_run (loop); + g_main_loop_unref (loop); + + destroy_watchlist (watchlist); + + return TRUE; + +error: + child_data_destroy (child_data); + return FALSE; + +} + + +int +main (int argc, char *argv[]) +{ + PMConfig *opts; + + g_type_init (); + g_thread_init (NULL); + +#ifdef HAVE_PRE2_GUILE + g_warning ("Note: pre-2.x version of guile: procmule will not function " + "correctly unless you're using UTF-8 locale."); +#endif /* HAVE_PRE2_GUILE */ + + opts = pm_config_new (&argc, &argv); + if (!opts) { + usage (); + goto error; + } + + if (!mu_runtime_init (opts->muhome, "procmule")) { + usage (); + goto error; + } + + watch_dirs (opts->watchdirs); /* do it! */ + mu_runtime_uninit (); + + pm_config_destroy (opts); + + return 0; + +error: + pm_config_destroy (opts); + return 1; + +}