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/mug2/Makefile
toys/muile/Makefile
toys/procmule/Makefile
man/Makefile
m4/Makefile
contrib/Makefile

View File

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

View File

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

View File

@ -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 <libguile.h>
@ -31,7 +31,7 @@
G_BEGIN_DECLS
/**
*
* output an error
*
* @param func_name
* @param status
@ -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__*/

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
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));
}

View File

@ -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,14 +865,14 @@ output_sexp (MuMsgIter *iter, size_t *count)
{
MuMsgIter *myiter;
size_t mycount;
g_return_val_if_fail (iter, FALSE);
g_print ("(:messages\n");
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;
@ -879,19 +880,27 @@ output_sexp (MuMsgIter *iter, size_t *count)
if (mycount != 0)
g_print ("\n");
g_print (" (:message\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 ("(%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;

View File

@ -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");
}

View File

@ -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);

View File

@ -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 <log file>.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;

View File

@ -111,16 +111,16 @@ mu_msg_doc_get_num_field (MuMsgDoc *self, MuMsgFieldId mfid)
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<gint64>(t);
} else
} else {
return static_cast<gint64>(Xapian::sortable_unserialise(s));
}
} 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 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__*/

View File

@ -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;
}

View File

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

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>
**
@ -42,6 +42,7 @@
struct _MuRuntimeData {
gchar *_str[MU_RUNTIME_PATH_NUM];
MuConfig *_config;
gchar *_name; /* e.g., 'mu', 'mug' */
};
typedef struct _MuRuntimeData MuRuntimeData;
@ -51,6 +52,8 @@ static MuRuntimeData *_data = NULL;
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,13 +67,37 @@ 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,34 +112,27 @@ mu_runtime_init (const char* muhome_arg)
return FALSE;
}
if (!mu_log_init (muhome, TRUE, FALSE, FALSE)) {
g_free (muhome);
return 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;
}
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 (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,8 +234,8 @@ 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;
@ -217,6 +243,14 @@ init_paths (const char* muhome, MuRuntimeData *data)
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*

View File

@ -30,10 +30,12 @@ G_BEGIN_DECLS
* systems. To uninitialize, use mu_runtime_uninit
*
* @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);
/**

View File

@ -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());

View File

@ -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));

View File

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

View File

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

View File

@ -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",

View File

@ -34,7 +34,7 @@ noinst_PROGRAMS= \
# 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 \

View File

@ -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",

View File

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

View File

@ -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
(use-modules (ice-9 readline))
(activate-readline)
,----
| (use-modules (ice-9 readline))
| (activate-readline)
`----
in your ~/.guile configuration.

View File

@ -23,9 +23,8 @@
#include <mu-runtime.h>
#include <glib-object.h>
#include <libguile.h>
#include <libmuguile/mu-guile-common.h>
#include <libmuguile/mu-guile-msg.h>
#include <libmuguile/mu-guile-store.h>
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

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;
}