Merge branch 'procmule'

This commit is contained in:
Dirk-Jan C. Binnema
2011-07-26 21:55:40 +03:00
31 changed files with 949 additions and 119 deletions

View File

@ -271,6 +271,7 @@ toys/Makefile
toys/mug/Makefile toys/mug/Makefile
toys/mug2/Makefile toys/mug2/Makefile
toys/muile/Makefile toys/muile/Makefile
toys/procmule/Makefile
man/Makefile man/Makefile
m4/Makefile m4/Makefile
contrib/Makefile contrib/Makefile

View File

@ -36,6 +36,8 @@ libmuguile_la_SOURCES= \
mu-guile-msg.h \ mu-guile-msg.h \
mu-guile-store.c \ mu-guile-store.c \
mu-guile-store.h \ mu-guile-store.h \
mu-guile-log.c \
mu-guile-log.h \
mu-guile-common.c \ mu-guile-common.c \
mu-guile-common.h mu-guile-common.h
@ -45,7 +47,8 @@ libmuguile_la_LIBADD= \
XFILES= \ XFILES= \
mu-guile-msg.x \ mu-guile-msg.x \
mu-guile-store.x mu-guile-store.x \
mu-guile-log.x
BUILT_SOURCES=$(XFILES) BUILT_SOURCES=$(XFILES)

View File

@ -21,6 +21,9 @@
#endif /*HAVE_CONFIG_H*/ #endif /*HAVE_CONFIG_H*/
#include "mu-guile-common.h" #include "mu-guile-common.h"
#include "mu-guile-store.h"
#include "mu-guile-msg.h"
#include "mu-guile-log.h"
void void
mu_guile_error (const char *func_name, int status, 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 * backward compat for pre-2.x guile - note, this will fail miserably
* if you don't use a UTF8 locale * if you don't use a UTF8 locale

View File

@ -17,8 +17,8 @@
** **
*/ */
#ifndef __MU_GUILE_UTILS_H__ #ifndef __MU_GUILE_COMMON_H__
#define __MU_GUILE_UTILS_H__ #define __MU_GUILE_COMMON_H__
#include <libguile.h> #include <libguile.h>
@ -30,8 +30,8 @@
G_BEGIN_DECLS G_BEGIN_DECLS
/** /**
* * output an error
* *
* @param func_name * @param func_name
* @param status * @param status
@ -42,7 +42,7 @@ void mu_guile_error (const char *func_name, int status,
const char *fmt, SCM args); const char *fmt, SCM args);
/** /**
* display a GError as a Guile error * display a GError as a Guile error
* *
* @param func_name function name * @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); 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 */ /* compatibility functions for old guile */
#if HAVE_PRE2_GUILE #if HAVE_PRE2_GUILE
SCM scm_from_utf8_string (const char* str); SCM scm_from_utf8_string (const char* str);
@ -58,5 +66,5 @@ char* scm_to_utf8_string (SCM scm);
G_END_DECLS G_END_DECLS
#endif /*__MU_GUILE_UTILS_H__*/ #endif /*__MU_GUILE_COMMON_H__*/

96
libmuguile/mu-guile-log.c Normal file
View File

@ -0,0 +1,96 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@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
** 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, "<write_log>");
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;
}

39
libmuguile/mu-guile-log.h Normal file
View File

@ -0,0 +1,39 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@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
** 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__*/

View File

@ -76,6 +76,41 @@ SCM_DEFINE (msg_make_from_file, "mu:msg:make-from-file", 1, 0, 0,
#undef FUNC_NAME #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 static SCM
scm_from_string_or_null (const char *str) 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); prio = mu_msg_get_prio (msgwrap->_msg);
switch (prio) { switch (prio) {
case MU_MSG_PRIO_LOW: return scm_from_locale_symbol("low"); case MU_MSG_PRIO_LOW: return scm_from_locale_symbol("mu:low");
case MU_MSG_PRIO_NORMAL: return scm_from_locale_symbol("normal"); case MU_MSG_PRIO_NORMAL: return scm_from_locale_symbol("mu:normal");
case MU_MSG_PRIO_HIGH: return scm_from_locale_symbol("high"); case MU_MSG_PRIO_HIGH: return scm_from_locale_symbol("mu:high");
default: default:
g_return_val_if_reached (SCM_UNDEFINED); g_return_val_if_reached (SCM_UNDEFINED);
} }
@ -167,7 +202,12 @@ check_flag (MuMsgFlags flag, FlagData *fdata)
{ {
if (fdata->flags & flag) { if (fdata->flags & flag) {
SCM item; 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)); fdata->lst = scm_append_x (scm_list_2(fdata->lst, item));
} }
} }
@ -469,22 +509,22 @@ static void
define_symbols (void) define_symbols (void)
{ {
/* message priority */ /* message priority */
scm_c_define ("high", scm_from_int(MU_MSG_PRIO_HIGH)); scm_c_define ("mu:high", scm_from_int(MU_MSG_PRIO_HIGH));
scm_c_define ("low", scm_from_int(MU_MSG_PRIO_LOW)); scm_c_define ("mu:low", scm_from_int(MU_MSG_PRIO_LOW));
scm_c_define ("normal", scm_from_int(MU_MSG_PRIO_NORMAL)); scm_c_define ("mu:normal", scm_from_int(MU_MSG_PRIO_NORMAL));
/* message flags */ /* message flags */
scm_c_define ("new", scm_from_int(MU_MSG_FLAG_NEW)); scm_c_define ("mu:new", scm_from_int(MU_MSG_FLAG_NEW));
scm_c_define ("passed", scm_from_int(MU_MSG_FLAG_PASSED)); scm_c_define ("mu:passed", scm_from_int(MU_MSG_FLAG_PASSED));
scm_c_define ("replied", scm_from_int(MU_MSG_FLAG_REPLIED)); scm_c_define ("mu:replied", scm_from_int(MU_MSG_FLAG_REPLIED));
scm_c_define ("seen", scm_from_int(MU_MSG_FLAG_SEEN)); scm_c_define ("mu:seen", scm_from_int(MU_MSG_FLAG_SEEN));
scm_c_define ("trashed", scm_from_int(MU_MSG_FLAG_TRASHED)); scm_c_define ("mu:trashed", scm_from_int(MU_MSG_FLAG_TRASHED));
scm_c_define ("draft", scm_from_int(MU_MSG_FLAG_DRAFT)); scm_c_define ("mu:draft", scm_from_int(MU_MSG_FLAG_DRAFT));
scm_c_define ("flagged", scm_from_int(MU_MSG_FLAG_FLAGGED)); scm_c_define ("mu:flagged", scm_from_int(MU_MSG_FLAG_FLAGGED));
scm_c_define ("unread", scm_from_int(MU_MSG_FLAG_UNREAD)); scm_c_define ("mu:unread", scm_from_int(MU_MSG_FLAG_UNREAD));
scm_c_define ("signed", scm_from_int(MU_MSG_FLAG_SIGNED)); scm_c_define ("mu:signed", scm_from_int(MU_MSG_FLAG_SIGNED));
scm_c_define ("encrypted", scm_from_int(MU_MSG_FLAG_ENCRYPTED)); scm_c_define ("mu: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:has-attach", scm_from_int(MU_MSG_FLAG_HAS_ATTACH));
} }

View File

@ -25,7 +25,7 @@
extern "C" { extern "C" {
#endif /*__cplusplus*/ #endif /*__cplusplus*/
/** /**
* initialize mu:store functions * initialize mu:store functions
* *
*/ */

View File

@ -853,7 +853,8 @@ print_attr_sexp (const char* elm, const char *str, gboolean nl)
return; /* empty: don't include */ return; /* empty: don't include */
esc = mu_str_escape_c_literal (str); 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); g_free (esc);
} }
@ -864,35 +865,43 @@ output_sexp (MuMsgIter *iter, size_t *count)
{ {
MuMsgIter *myiter; MuMsgIter *myiter;
size_t mycount; size_t mycount;
g_return_val_if_fail (iter, FALSE);
g_return_val_if_fail (iter, FALSE);
g_print ("(:messages\n");
for (myiter = iter, mycount = 0; !mu_msg_iter_is_done (myiter); for (myiter = iter, mycount = 0; !mu_msg_iter_is_done (myiter);
mu_msg_iter_next (myiter), ++mycount) { mu_msg_iter_next (myiter), ++mycount) {
unsigned date, date_high, date_low;
MuMsg *msg; MuMsg *msg;
if (!(msg = mu_msg_iter_get_msg (iter, NULL))) /* don't unref */ if (!(msg = mu_msg_iter_get_msg (iter, NULL))) /* don't unref */
return FALSE; return FALSE;
if (mycount != 0) if (mycount != 0)
g_print ("\n"); 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 ("from", mu_msg_get_from (msg),TRUE);
print_attr_sexp ("to", mu_msg_get_to (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 ("cc", mu_msg_get_cc (msg),TRUE);
print_attr_sexp ("subject", mu_msg_get_subject (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 (" :date %u\n", date);
g_print (" (:size %u)\n", (unsigned) mu_msg_get_size (msg)); 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 ("msgid", mu_msg_get_msgid (msg),TRUE);
print_attr_sexp ("path", mu_msg_get_path (msg),TRUE); print_attr_sexp ("path", mu_msg_get_path (msg),TRUE);
print_attr_sexp ("maildir", mu_msg_get_maildir (msg),FALSE); print_attr_sexp ("maildir", mu_msg_get_maildir (msg),FALSE);
g_print (")"); g_print (")\n;;eom");
} }
g_print (")\n");
fputs ("\n", stdout);
if (count) if (count)
*count = mycount; *count = mycount;

View File

@ -449,7 +449,7 @@ show_usage (gboolean noerror)
static void static void
show_version (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"); "Copyright (C) 2008-2011 Dirk-Jan C. Binnema (GPLv3+)\n");
} }

View File

@ -165,23 +165,14 @@ log_file_backup_maybe (const char *logfile)
gboolean gboolean
mu_log_init (const char* muhome, mu_log_init (const char* logfile, gboolean backup,
gboolean backup, gboolean quiet, gboolean debug) gboolean quiet, gboolean debug)
{ {
int fd; int fd;
gchar *logfile;
/* only init once... */ /* only init once... */
g_return_val_if_fail (!MU_LOG, FALSE); g_return_val_if_fail (!MU_LOG, FALSE);
g_return_val_if_fail (muhome, FALSE); g_return_val_if_fail (logfile, 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);
if (backup && !log_file_backup_maybe(logfile)) { if (backup && !log_file_backup_maybe(logfile)) {
g_warning ("failed to backup log file"); g_warning ("failed to backup log file");
@ -192,7 +183,6 @@ mu_log_init (const char* muhome,
if (fd < 0) if (fd < 0)
g_warning ("%s: open() of '%s' failed: %s", __FUNCTION__, g_warning ("%s: open() of '%s' failed: %s", __FUNCTION__,
logfile, strerror(errno)); logfile, strerror(errno));
g_free (logfile);
if (fd < 0 || !mu_log_init_with_fd (fd, FALSE, quiet, debug)) { if (fd < 0 || !mu_log_init_with_fd (fd, FALSE, quiet, debug)) {
try_close (fd); try_close (fd);

View File

@ -31,7 +31,8 @@ G_BEGIN_DECLS
/** /**
* write logging information to a log file * 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 * @param backup if TRUE and size of log file > MU_MAX_LOG_FILE_SIZE, move
* the log file to <log file>.old and start a new one. The .old file will overwrite * the log file to <log file>.old and start a new one. The .old file will overwrite
* existing files of that name * existing files of that name
@ -40,7 +41,7 @@ G_BEGIN_DECLS
* *
* @return TRUE if initialization succeeds, FALSE otherwise * @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) gboolean quiet, gboolean debug)
G_GNUC_WARN_UNUSED_RESULT; G_GNUC_WARN_UNUSED_RESULT;

View File

@ -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 (self, -1);
g_return_val_if_fail (mu_msg_field_id_is_valid(mfid), -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); g_return_val_if_fail (mu_msg_field_is_numeric(mfid), -1);
/* date is a special case, because we store dates as /* date is a special case, because we store dates as
* strings */ * strings */
try { try {
const std::string s (self->doc().get_value(mfid)); const std::string s (self->doc().get_value(mfid));
if (s.empty()) if (s.empty())
return -1; return 0;
else if (mfid == MU_MSG_FIELD_ID_DATE) { else if (mfid == MU_MSG_FIELD_ID_DATE) {
time_t t; time_t t;
t = mu_date_str_to_time_t (s.c_str(), FALSE/*utc*/); t = mu_date_str_to_time_t (s.c_str(), FALSE/*utc*/);
return static_cast<gint64>(t); return static_cast<gint64>(t);
} else } else {
return static_cast<gint64>(Xapian::sortable_unserialise(s)); return static_cast<gint64>(Xapian::sortable_unserialise(s));
}
} MU_XAPIAN_CATCH_BLOCK_RETURN(-1);
} MU_XAPIAN_CATCH_BLOCK_RETURN(-1);
} }

View File

@ -106,9 +106,9 @@ GSList* mu_msg_file_get_str_list_field (MuMsgFile *self,
* @param self a valid MuMsgFile * @param self a valid MuMsgFile
* @param msfid the message field id to get (must be string-based one) * @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__*/ #endif /*__MU_MSG_FILE_H__*/

View File

@ -669,3 +669,147 @@ mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid)
return 0; /* TODO: handle lists */ 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;
}

View File

@ -357,6 +357,28 @@ const GSList* mu_msg_get_tags (MuMsg *self);
int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid); 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:? */ enum _MuMsgContactType { /* Reply-To:? */
MU_MSG_CONTACT_TYPE_TO = 0, MU_MSG_CONTACT_TYPE_TO = 0,
MU_MSG_CONTACT_TYPE_FROM, MU_MSG_CONTACT_TYPE_FROM,

View File

@ -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 <djcb@djcbsoftware.nl> ** Copyright (C) 2010 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
** **
@ -42,6 +42,7 @@
struct _MuRuntimeData { struct _MuRuntimeData {
gchar *_str[MU_RUNTIME_PATH_NUM]; gchar *_str[MU_RUNTIME_PATH_NUM];
MuConfig *_config; MuConfig *_config;
gchar *_name; /* e.g., 'mu', 'mug' */
}; };
typedef struct _MuRuntimeData MuRuntimeData; typedef struct _MuRuntimeData MuRuntimeData;
@ -49,8 +50,10 @@ typedef struct _MuRuntimeData MuRuntimeData;
static gboolean _initialized = FALSE; static gboolean _initialized = FALSE;
static MuRuntimeData *_data = NULL; static MuRuntimeData *_data = NULL;
static void runtime_free (void); static void runtime_free (void);
static gboolean init_paths (const char* muhome, MuRuntimeData *data); static gboolean init_paths (const char* muhome, MuRuntimeData *data);
static const char* runtime_path (MuRuntimePath path);
static gboolean static gboolean
mu_dir_is_readable_and_writable (const char *muhome) mu_dir_is_readable_and_writable (const char *muhome)
@ -64,14 +67,38 @@ mu_dir_is_readable_and_writable (const char *muhome)
return FALSE; 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 gboolean
mu_runtime_init (const char* muhome_arg) mu_runtime_init (const char* muhome_arg, const char *name)
{ {
gchar *muhome; gchar *muhome;
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()) if (!mu_util_init_system())
return FALSE; return FALSE;
@ -85,35 +112,28 @@ mu_runtime_init (const char* muhome_arg)
return FALSE; 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); g_free (muhome);
return FALSE; return FALSE;
} }
_data = g_new0 (MuRuntimeData, 1);
_data->_str[MU_RUNTIME_PATH_MUHOME] = muhome;
init_paths (muhome, _data);
return _initialized = TRUE; 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 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()) if (!mu_util_init_system())
return FALSE; return FALSE;
@ -129,15 +149,19 @@ mu_runtime_init_from_cmdline (int *pargc, char ***pargv)
return FALSE; return FALSE;
} }
if (!init_log (_data->_config)) { _data->_name = g_strdup (name);
runtime_free ();
return FALSE;
}
_data->_str[MU_RUNTIME_PATH_MUHOME] = _data->_str[MU_RUNTIME_PATH_MUHOME] =
g_strdup (_data->_config->muhome); g_strdup (_data->_config->muhome);
init_paths (_data->_str[MU_RUNTIME_PATH_MUHOME], _data); 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; return _initialized = TRUE;
} }
@ -150,6 +174,8 @@ runtime_free (void)
for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i) for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i)
g_free (_data->_str[i]); g_free (_data->_str[i]);
g_free (_data->_name);
mu_config_destroy (_data->_config); mu_config_destroy (_data->_config);
mu_log_uninit(); mu_log_uninit();
@ -208,15 +234,23 @@ init_paths (const char* muhome, MuRuntimeData *data)
G_DIR_SEPARATOR, MU_CONTACTS_FILENAME); G_DIR_SEPARATOR, MU_CONTACTS_FILENAME);
data->_str [MU_RUNTIME_PATH_LOG] = data->_str [MU_RUNTIME_PATH_LOG] =
g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR, g_strdup_printf ("%s%c%s", muhome,
MU_LOG_DIRNAME); G_DIR_SEPARATOR, MU_LOG_DIRNAME);
if (!create_dirs_maybe (data)) if (!create_dirs_maybe (data))
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
/* so we can called when _initialized is FALSE still */
static const char*
runtime_path (MuRuntimePath path)
{
return _data->_str[path];
}
const char* const char*
mu_runtime_path (MuRuntimePath path) 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 (_initialized, NULL);
g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL); g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL);
return _data->_str[path]; return runtime_path (path);
} }
MuConfig* MuConfig*

View File

@ -29,11 +29,13 @@ G_BEGIN_DECLS
* initialize the mu runtime system; initializes logging and other * initialize the mu runtime system; initializes logging and other
* systems. To uninitialize, use mu_runtime_uninit * 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 * @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 param count (typically, argc)
* @param ptr to the params (typically, argv) * @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 * @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);
/** /**

View File

@ -29,7 +29,7 @@ main (int argc, char *argv[])
{ {
int rv; int rv;
if (!mu_runtime_init_from_cmdline (&argc, &argv)) if (!mu_runtime_init_from_cmdline (&argc, &argv, "mu"))
return 1; return 1;
rv = mu_config_execute (mu_runtime_config()); rv = mu_config_execute (mu_runtime_config());

View File

@ -38,10 +38,10 @@ test_mu_runtime_init (void)
tmpdir = test_mu_common_get_random_tmpdir(); tmpdir = test_mu_common_get_random_tmpdir();
g_assert (tmpdir); g_assert (tmpdir);
g_assert (mu_runtime_init (tmpdir) == TRUE); g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE);
mu_runtime_uninit (); mu_runtime_uninit ();
g_assert (mu_runtime_init (tmpdir) == TRUE); g_assert (mu_runtime_init (tmpdir, "test-mu-runtime") == TRUE);
mu_runtime_uninit (); mu_runtime_uninit ();
g_free (tmpdir); g_free (tmpdir);
@ -62,7 +62,7 @@ test_mu_runtime_data (void)
bmfile = g_strdup_printf ("%s%c%s", homedir, bmfile = g_strdup_printf ("%s%c%s", homedir,
G_DIR_SEPARATOR, "bookmarks"); 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 (homedir, ==, mu_runtime_path (MU_RUNTIME_PATH_MUHOME));
g_assert_cmpstr (xdir, ==, mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB)); g_assert_cmpstr (xdir, ==, mu_runtime_path (MU_RUNTIME_PATH_XAPIANDB));

View File

@ -32,3 +32,12 @@ endif
if HAVE_GUILE if HAVE_GUILE
SUBDIRS += muile SUBDIRS += muile
endif endif
# for procmule, we need guile and gio
if HAVE_GUILE
if HAVE_GIO
SUBDIRS += procmule
endif
endif

View File

@ -35,7 +35,7 @@ noinst_PROGRAMS= \
# note, mug.cc is '.cc' only because libmu must explicitly # note, mug.cc is '.cc' only because libmu must explicitly
# be linked as c++, not c. # be linked as c++, not c.
mug_SOURCES= \ mug_SOURCES= \
mug.cc \ mug.c \
mug-msg-list-view.c \ mug-msg-list-view.c \
mug-msg-list-view.h \ mug-msg-list-view.h \
mug-msg-view.h \ mug-msg-view.h \
@ -43,7 +43,19 @@ mug_SOURCES= \
mug-query-bar.h \ mug-query-bar.h \
mug-query-bar.c \ mug-query-bar.c \
mug-shortcuts.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= \ mug_LDADD= \
${top_builddir}/src/libmu.la \ ${top_builddir}/src/libmu.la \

View File

@ -369,7 +369,7 @@ main (int argc, char *argv[])
return 1; return 1;
} }
mu_runtime_init (mugdata.muhome); mu_runtime_init (mugdata.muhome, "mug");
mugshell = mug_shell (&mugdata); mugshell = mug_shell (&mugdata);
g_signal_connect (G_OBJECT (mugshell), "destroy", g_signal_connect (G_OBJECT (mugshell), "destroy",

View File

@ -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_CFLAGS=-Wall -Wextra -Wno-unused-parameter -Wdeclaration-after-statement
AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter AM_CXXFLAGS=-Wall -Wextra -Wno-unused-parameter
noinst_PROGRAMS= \ noinst_PROGRAMS= \
mug2 mug2
# note, mug.cc is '.cc' only because libmu must explicitly # note, mug.cc is '.cc' only because libmu must explicitly
# be linked as c++, not c. # be linked as c++, not c.
mug2_SOURCES= \ mug2_SOURCES= \
mug.cc \ mug.c \
mug-msg-list-view.c \ mug-msg-list-view.c \
mug-msg-list-view.h \ mug-msg-list-view.h \
mug-msg-view.h \ mug-msg-view.h \
@ -42,7 +42,19 @@ mug2_SOURCES= \
mug-query-bar.h \ mug-query-bar.h \
mug-query-bar.c \ mug-query-bar.c \
mug-shortcuts.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= \ mug2_LDADD= \
${top_builddir}/src/libmu.la \ ${top_builddir}/src/libmu.la \

View File

@ -420,7 +420,7 @@ main (int argc, char *argv[])
} }
g_option_context_free (octx); g_option_context_free (octx);
mu_runtime_init (mugdata.muhome); mu_runtime_init (mugdata.muhome, "mug2");
mugshell = mug_shell (&mugdata); mugshell = mug_shell (&mugdata);
g_signal_connect (G_OBJECT (mugshell), "destroy", g_signal_connect (G_OBJECT (mugshell), "destroy",

View File

@ -29,7 +29,20 @@ noinst_PROGRAMS= \
muile muile
muile_SOURCES= \ 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= \ muile_LDFLAGS= \
${top_builddir}/libmuguile/libmuguile.la ${top_builddir}/libmuguile/libmuguile.la

View File

@ -8,7 +8,9 @@
`guile'[1] is an interpreter/library for the Scheme programming language[2], `guile'[1] is an interpreter/library for the Scheme programming language[2],
specifically meant for extending other programs. It is, in fact, the 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 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. discussed below.
The combination of mu + guile is called `muile', and allows you to write The combination of mu + guile is called `muile', and allows you to write
@ -53,14 +55,17 @@
| scheme@(guile-user)> (mu:msg:subject msg) | scheme@(guile-user)> (mu:msg:subject msg)
| $1 = "See me in bikini :-)" | $1 = "See me in bikini :-)"
| scheme@(guile-user)> (mu:msg:flags msg) | 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 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. in your ~/.guile configuration.

View File

@ -23,9 +23,8 @@
#include <mu-runtime.h> #include <mu-runtime.h>
#include <glib-object.h> #include <glib-object.h>
#include <libguile.h> #include <libmuguile/mu-guile-common.h>
#include <libmuguile/mu-guile-msg.h> #include <libmuguile/mu-guile-msg.h>
#include <libmuguile/mu-guile-store.h>
struct _MuileConfig { struct _MuileConfig {
const char *muhome; const char *muhome;
@ -97,13 +96,13 @@ main (int argc, char *argv[])
goto error; goto error;
} }
if (!mu_runtime_init (opts->muhome /* NULL is okay */)) { if (!mu_runtime_init (opts->muhome /* NULL is okay */,
"muile")) {
usage (); usage ();
goto error; goto error;
} }
scm_with_guile (&mu_guile_msg_init, NULL); mu_guile_init (); /* initialize mu guile modules */
scm_with_guile (&mu_guile_store_init, NULL);
if (opts->msgpath) { if (opts->msgpath) {
if (!(gboolean)scm_with_guile if (!(gboolean)scm_with_guile

49
toys/procmule/Makefile.am Normal file
View File

@ -0,0 +1,49 @@
## Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@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
## 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}

32
toys/procmule/README Normal file
View File

@ -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:

295
toys/procmule/procmule.c Normal file
View File

@ -0,0 +1,295 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@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
** 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 <glib.h>
#include <gio/gio.h>
#include <libmuguile/mu-guile-common.h>
#include <libmuguile/mu-guile-msg.h>
#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=<dir>\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=<dir>] [--watch=<dir1>]\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;
}