Merge branch 'procmule'
This commit is contained in:
@ -23,7 +23,14 @@ else
|
|||||||
widgets=
|
widgets=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
SUBDIRS=m4 man src $(widgets) contrib toys
|
if HAVE_GUILE
|
||||||
|
guile=libmuguile
|
||||||
|
else
|
||||||
|
guile=
|
||||||
|
endif
|
||||||
|
|
||||||
|
SUBDIRS=m4 man src $(widgets) $(guile) contrib toys
|
||||||
|
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS=-I m4
|
ACLOCAL_AMFLAGS=-I m4
|
||||||
|
|
||||||
|
|||||||
12
NEWS
12
NEWS
@ -1,5 +1,17 @@
|
|||||||
* NEWS (user visible changes)
|
* NEWS (user visible changes)
|
||||||
|
|
||||||
|
* Release 0.9.7 <>
|
||||||
|
|
||||||
|
- enable threading (using -t/--threads) with mu find
|
||||||
|
- don't enforce UTF-8 output, use locale (fixes issue #11)
|
||||||
|
- add mail threading (sorta fixes issue #13)
|
||||||
|
- add header line to --format=mutt-ab (mu cfind), (fixes issue #42)
|
||||||
|
- terminate mu view results with a form-feed marker (use --terminate) (fixes
|
||||||
|
issue #41)
|
||||||
|
- search X-Label: tags (fixes issue #40)
|
||||||
|
- added toys/muile, the mu guile shells, which allows for message stats etc.
|
||||||
|
|
||||||
|
|
||||||
** Release 0.9.6 <2011-05-28 Sat>
|
** Release 0.9.6 <2011-05-28 Sat>
|
||||||
|
|
||||||
- FreeBSD build fix
|
- FreeBSD build fix
|
||||||
|
|||||||
19
TODO
19
TODO
@ -1,24 +1,35 @@
|
|||||||
* Future release
|
* Future release
|
||||||
|
|
||||||
- [ ] mu stats
|
|
||||||
- [ ] config system (config file) (?)
|
- [ ] config system (config file) (?)
|
||||||
- [ ] don't make test mail files executable
|
- [ ] don't make test mail files executable
|
||||||
- [ ] add version info to output of configure, mu.log
|
|
||||||
- [ ] make 'make check' more useful for others
|
- [ ] make 'make check' more useful for others
|
||||||
- [ ] completion for zsh
|
- [ ] completion for zsh
|
||||||
- [ ] follow symlinks when indexing
|
- [ ] follow symlinks when indexing
|
||||||
- [ ] better 'usage' info
|
- [ ] better 'usage' info
|
||||||
- [ ] generalize mu_str_normalize for all unicode
|
- [ ] generalize mu_str_normalize for all unicode
|
||||||
- [ ] mail threads
|
|
||||||
- [ ] check wmy (date) for begin-end
|
- [ ] check wmy (date) for begin-end
|
||||||
|
- [ ] tagging rw
|
||||||
|
|
||||||
|
** release 0.9.7 [100%]
|
||||||
|
|
||||||
|
- [X] mu stats
|
||||||
|
- [X] tagging (ro, based on X-Label)
|
||||||
|
- [X] mail threads
|
||||||
|
- [X] guile interface
|
||||||
|
- [X] add version info to output of configure, mu.log
|
||||||
|
- [X] use system locale in output
|
||||||
|
|
||||||
* Releases already done (see NEWS for features)
|
* Releases already done (see NEWS for features)
|
||||||
|
|
||||||
** release 0.9.6 [100%]
|
** release 0.9.6 [100%]
|
||||||
|
|
||||||
|
(see NEWS)
|
||||||
|
|
||||||
** release 0.9.5 [100%]
|
** release 0.9.5 [100%]
|
||||||
|
|
||||||
|
(see NEWS)
|
||||||
|
|
||||||
** release 0.9.4 [100%]
|
** release 0.9.4 [100%]
|
||||||
|
|
||||||
- [X] add 'mu cfind' to find contacts
|
- [X] add 'mu cfind' to find contacts
|
||||||
@ -37,7 +48,7 @@
|
|||||||
- [X] xml,json,sexp output (experimental)
|
- [X] xml,json,sexp output (experimental)
|
||||||
- [X] check all strings are really UTF8
|
- [X] check all strings are really UTF8
|
||||||
- [X] add drag & drop support for attachments
|
- [X] add drag & drop support for attachments
|
||||||
- [X] add drop & drop support for body images
|
- [X] add drag & drop support for body images
|
||||||
- [X] fix size value
|
- [X] fix size value
|
||||||
- [X] fix matching strange folder names
|
- [X] fix matching strange folder names
|
||||||
- [X] separate exit code for 'not found' vs other errors
|
- [X] separate exit code for 'not found' vs other errors
|
||||||
|
|||||||
27
configure.ac
27
configure.ac
@ -208,6 +208,27 @@ AM_CONDITIONAL(HAVE_GIO, [test "x$have_gio" = "xyes"])
|
|||||||
# should we build the widgets/ dir?
|
# should we build the widgets/ dir?
|
||||||
AM_CONDITIONAL(BUILD_WIDGETS, [test "x$have_webkit" = "xyes" -a "x$have_gio" = "xyes"])
|
AM_CONDITIONAL(BUILD_WIDGETS, [test "x$have_webkit" = "xyes" -a "x$have_gio" = "xyes"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# check for guile & guile-snarf
|
||||||
|
AC_PATH_PROG(GUILE, [guile-config], [], [$PATH])
|
||||||
|
AS_IF([test "x$GUILE" != "x"],
|
||||||
|
[GUILE_CFLAGS=`$GUILE compile`; GUILE_LIBS=`$GUILE link`])
|
||||||
|
AC_SUBST(GUILE_LIBS)
|
||||||
|
AC_SUBST(GUILE_CFLAGS)
|
||||||
|
|
||||||
|
AC_PATH_PROG(GUILE_SNARF, [guile-snarf], [], [$PATH])
|
||||||
|
AS_IF([test "x$GUILE_SNARF" != "x"],[
|
||||||
|
AC_DEFINE_UNQUOTED([GUILE_SNARF], ["$GUILE_SNARF"],[Path to guile-snarf])],[
|
||||||
|
AC_MSG_WARN([cannot find guile-snarf])])
|
||||||
|
|
||||||
|
AM_CONDITIONAL(HAVE_GUILE,[test "$xGUILE" != "x" -a "x$GUILE_SNARF != "x])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# check for xdg-open
|
# check for xdg-open
|
||||||
AS_IF([test "x$buildgui"="xyes"],[
|
AS_IF([test "x$buildgui"="xyes"],[
|
||||||
AC_PATH_PROG(XDGOPEN, [xdg-open], [], [$PATH])
|
AC_PATH_PROG(XDGOPEN, [xdg-open], [], [$PATH])
|
||||||
@ -232,9 +253,11 @@ Makefile
|
|||||||
src/Makefile
|
src/Makefile
|
||||||
src/tests/Makefile
|
src/tests/Makefile
|
||||||
widgets/Makefile
|
widgets/Makefile
|
||||||
|
libmuguile/Makefile
|
||||||
toys/Makefile
|
toys/Makefile
|
||||||
toys/mug/Makefile
|
toys/mug/Makefile
|
||||||
toys/mug2/Makefile
|
toys/mug2/Makefile
|
||||||
|
toys/muile/Makefile
|
||||||
man/Makefile
|
man/Makefile
|
||||||
m4/Makefile
|
m4/Makefile
|
||||||
contrib/Makefile
|
contrib/Makefile
|
||||||
@ -268,6 +291,10 @@ if test "x$have_webkit" = "xyes"; then
|
|||||||
echo "Webkit version : $webkit_version"
|
echo "Webkit version : $webkit_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "x$GUILE" != "x"; then
|
||||||
|
echo "Guile version : `$GUILE --version 2>&1`"
|
||||||
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Build unit tests (glib >= 2.22) : $have_gtest"
|
echo "Build unit tests (glib >= 2.22) : $have_gtest"
|
||||||
echo "Build 'mug' toy-ui (requires GTK+) : $buildgui"
|
echo "Build 'mug' toy-ui (requires GTK+) : $buildgui"
|
||||||
|
|||||||
61
libmuguile/Makefile.am
Normal file
61
libmuguile/Makefile.am
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
|
# enforce compiling this dir first before decending into tests/
|
||||||
|
SUBDIRS= .
|
||||||
|
INCLUDES=-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_LTLIBRARIES= \
|
||||||
|
libmuguile.la
|
||||||
|
|
||||||
|
libmuguile_la_SOURCES= \
|
||||||
|
mu-guile-msg.c \
|
||||||
|
mu-guile-msg.h \
|
||||||
|
mu-guile-store.c \
|
||||||
|
mu-guile-store.h \
|
||||||
|
mu-guile-common.c \
|
||||||
|
mu-guile-common.h
|
||||||
|
|
||||||
|
libmuguile_la_LIBADD= \
|
||||||
|
${top_builddir}/src/libmu.la \
|
||||||
|
${GUILE_LIBS}
|
||||||
|
|
||||||
|
XFILES= \
|
||||||
|
mu-guile-msg.x \
|
||||||
|
mu-guile-store.x
|
||||||
|
|
||||||
|
BUILT_SOURCES=$(XFILES) $(DOCFILES)
|
||||||
|
|
||||||
|
|
||||||
|
snarfcppopts= $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $(INCLUDES)
|
||||||
|
SUFFIXES = .x
|
||||||
|
.c.x:
|
||||||
|
$(GUILE_SNARF) -o $@ $< $(snarfcppopts)
|
||||||
|
|
||||||
|
## Add -MG to make the .x magic work with auto-dep code.
|
||||||
|
MKDEP = $(CC) -M -MG $(snarfcppopts)
|
||||||
|
|
||||||
|
|
||||||
|
DISTCLEANFILES=$(XFILES)
|
||||||
41
libmuguile/mu-guile-common.c
Normal file
41
libmuguile/mu-guile-common.c
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
** 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"
|
||||||
|
|
||||||
|
void
|
||||||
|
mu_guile_error (const char *func_name, int status,
|
||||||
|
const char *fmt, SCM args)
|
||||||
|
{
|
||||||
|
scm_error_scm (scm_from_locale_symbol ("MuError"),
|
||||||
|
scm_from_utf8_string (func_name ? func_name : "<nameless>"),
|
||||||
|
scm_from_utf8_string (fmt), args,
|
||||||
|
scm_list_1 (scm_from_int (status)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
mu_guile_g_error (const char *func_name, GError *err)
|
||||||
|
{
|
||||||
|
scm_error_scm (scm_from_locale_symbol ("MuError"),
|
||||||
|
scm_from_utf8_string (func_name),
|
||||||
|
scm_from_utf8_string (err->message),
|
||||||
|
SCM_UNDEFINED, SCM_UNDEFINED);
|
||||||
|
}
|
||||||
51
libmuguile/mu-guile-common.h
Normal file
51
libmuguile/mu-guile-common.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
** 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_UTILS_H__
|
||||||
|
#define __MU_GUILE_UTILS_H__
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param func_name
|
||||||
|
* @param status
|
||||||
|
* @param fmt
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* @param err Gerror
|
||||||
|
*/
|
||||||
|
void mu_guile_g_error (const char *func_name, GError *err);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /*__MU_GUILE_UTILS_H__*/
|
||||||
|
|
||||||
512
libmuguile/mu-guile-msg.c
Normal file
512
libmuguile/mu-guile-msg.c
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
/*
|
||||||
|
** 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-msg.h>
|
||||||
|
#include <mu-query.h>
|
||||||
|
#include <mu-runtime.h>
|
||||||
|
|
||||||
|
#include "mu-guile-msg.h"
|
||||||
|
#include "mu-guile-common.h"
|
||||||
|
|
||||||
|
struct _MuMsgWrapper {
|
||||||
|
MuMsg *_msg;
|
||||||
|
gboolean _unrefme;
|
||||||
|
};
|
||||||
|
typedef struct _MuMsgWrapper MuMsgWrapper;
|
||||||
|
|
||||||
|
static long MSG_TAG;
|
||||||
|
|
||||||
|
static int
|
||||||
|
mu_guile_scm_is_msg (SCM scm)
|
||||||
|
{
|
||||||
|
return SCM_NIMP(scm) && (long) SCM_CAR (scm) == MSG_TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM
|
||||||
|
mu_guile_msg_to_scm (MuMsg *msg)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
|
||||||
|
g_return_val_if_fail (msg, SCM_UNDEFINED);
|
||||||
|
|
||||||
|
msgwrap = scm_gc_malloc (sizeof (MuMsgWrapper), "msg");
|
||||||
|
msgwrap->_msg = msg;
|
||||||
|
msgwrap->_unrefme = FALSE;
|
||||||
|
|
||||||
|
SCM_RETURN_NEWSMOB (MSG_TAG, msgwrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_make_from_file, "mu:msg:make-from-file", 1, 0, 0,
|
||||||
|
(SCM PATH),
|
||||||
|
"Create a message object based on the message in PATH.\n")
|
||||||
|
#define FUNC_NAME s_msg_make_from_file
|
||||||
|
{
|
||||||
|
MuMsg *msg;
|
||||||
|
GError *err;
|
||||||
|
|
||||||
|
SCM_ASSERT (scm_is_string (PATH), PATH, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
err = NULL;
|
||||||
|
msg = mu_msg_new_from_file (scm_to_utf8_string (PATH), NULL, &err);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
mu_guile_g_error (FUNC_NAME, err);
|
||||||
|
g_error_free (err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg ? mu_guile_msg_to_scm (msg) : SCM_UNDEFINED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
msg_str_field (SCM msg_smob, MuMsgFieldId mfid)
|
||||||
|
{
|
||||||
|
const char *val, *endptr;
|
||||||
|
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
val = mu_msg_get_field_string(msgwrap->_msg, mfid);
|
||||||
|
|
||||||
|
if (val && !g_utf8_validate (val, -1, &endptr)) {
|
||||||
|
//return scm_from_utf8_string("<invalid>");
|
||||||
|
gchar *part;
|
||||||
|
SCM scm;
|
||||||
|
part = g_strndup (val, (endptr-val));
|
||||||
|
scm = scm_from_utf8_string(part);
|
||||||
|
g_free (part);
|
||||||
|
return scm;
|
||||||
|
} else
|
||||||
|
return val ? scm_from_utf8_string(val) : SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint64
|
||||||
|
msg_num_field (SCM msg_smob, MuMsgFieldId mfid)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
return mu_msg_get_field_numeric(msgwrap->_msg, mfid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_date, "mu:msg:date", 1, 0, 0,
|
||||||
|
(SCM MSG),
|
||||||
|
"Get the date (time in seconds since epoch) for MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_date
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return scm_from_unsigned_integer
|
||||||
|
(msg_num_field (MSG, MU_MSG_FIELD_ID_DATE));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_size, "mu:msg:size", 1, 0, 0,
|
||||||
|
(SCM MSG),
|
||||||
|
"Get the size in bytes for MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_size
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return scm_from_unsigned_integer
|
||||||
|
(msg_num_field (MSG, MU_MSG_FIELD_ID_SIZE));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_prio, "mu:msg:priority", 1, 0, 0,
|
||||||
|
(SCM MSG),
|
||||||
|
"Get the priority of MSG (low, normal or high).\n")
|
||||||
|
#define FUNC_NAME s_msg_prio
|
||||||
|
{
|
||||||
|
MuMsgPrio prio;
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(MSG);
|
||||||
|
|
||||||
|
prio = mu_msg_get_prio (msgwrap->_msg);
|
||||||
|
|
||||||
|
switch (prio) {
|
||||||
|
case MU_MSG_PRIO_LOW: return scm_from_utf8_symbol("low");
|
||||||
|
case MU_MSG_PRIO_NORMAL: return scm_from_utf8_symbol("normal");
|
||||||
|
case MU_MSG_PRIO_HIGH: return scm_from_utf8_symbol("high");
|
||||||
|
default:
|
||||||
|
g_return_val_if_reached (SCM_UNDEFINED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
struct _FlagData {
|
||||||
|
MuMsgFlags flags;
|
||||||
|
SCM lst;
|
||||||
|
};
|
||||||
|
typedef struct _FlagData FlagData;
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_flag (MuMsgFlags flag, FlagData *fdata)
|
||||||
|
{
|
||||||
|
if (fdata->flags & flag) {
|
||||||
|
SCM item;
|
||||||
|
item = scm_list_1 (scm_from_utf8_symbol(mu_msg_flag_name(flag)));
|
||||||
|
fdata->lst = scm_append_x (scm_list_2(fdata->lst, item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_flags, "mu:msg:flags", 1, 0, 0,
|
||||||
|
(SCM MSG),
|
||||||
|
"Get the flags for MSG (one or or more of new, passed, replied, "
|
||||||
|
"seen, trashed, draft, flagged, unread, signed, encrypted, "
|
||||||
|
"has-attach).\n")
|
||||||
|
#define FUNC_NAME s_msg_flags
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
FlagData fdata;
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(MSG);
|
||||||
|
|
||||||
|
fdata.flags = mu_msg_get_flags (msgwrap->_msg);
|
||||||
|
fdata.lst = SCM_EOL;
|
||||||
|
mu_msg_flags_foreach ((MuMsgFlagsForeachFunc)check_flag,
|
||||||
|
&fdata);
|
||||||
|
|
||||||
|
return fdata.lst;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_subject, "mu:msg:subject", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the subject of MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_subject
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_str_field (MSG, MU_MSG_FIELD_ID_SUBJECT);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_from, "mu:msg:from", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the sender of MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_from
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_str_field (MSG, MU_MSG_FIELD_ID_FROM);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
struct _EachContactData {
|
||||||
|
SCM lst;
|
||||||
|
MuMsgContactType ctype;
|
||||||
|
};
|
||||||
|
typedef struct _EachContactData EachContactData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
contacts_to_list (MuMsgContact *contact, EachContactData *ecdata)
|
||||||
|
{
|
||||||
|
if (mu_msg_contact_type (contact) == ecdata->ctype) {
|
||||||
|
SCM item;
|
||||||
|
const char *addr, *name;
|
||||||
|
|
||||||
|
addr = mu_msg_contact_address(contact);
|
||||||
|
name = mu_msg_contact_name(contact);
|
||||||
|
|
||||||
|
item = scm_list_1
|
||||||
|
(scm_list_2 (
|
||||||
|
name ? scm_from_utf8_string(name) : SCM_UNSPECIFIED,
|
||||||
|
addr ? scm_from_utf8_string(addr) : SCM_UNSPECIFIED));
|
||||||
|
|
||||||
|
ecdata->lst = scm_append_x (scm_list_2(ecdata->lst, item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
contact_list_field (SCM msg_smob, MuMsgFieldId mfid)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
EachContactData ecdata;
|
||||||
|
|
||||||
|
ecdata.lst = SCM_EOL;
|
||||||
|
|
||||||
|
switch (mfid) {
|
||||||
|
case MU_MSG_FIELD_ID_TO: ecdata.ctype = MU_MSG_CONTACT_TYPE_TO; break;
|
||||||
|
case MU_MSG_FIELD_ID_CC: ecdata.ctype = MU_MSG_CONTACT_TYPE_CC; break;
|
||||||
|
case MU_MSG_FIELD_ID_BCC: ecdata.ctype = MU_MSG_CONTACT_TYPE_BCC; break;
|
||||||
|
default: g_return_val_if_reached (SCM_UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
mu_msg_contact_foreach (msgwrap->_msg,
|
||||||
|
(MuMsgContactForeachFunc)contacts_to_list,
|
||||||
|
&ecdata);
|
||||||
|
return ecdata.lst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_to, "mu:msg:to", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the list of To:-recipients of MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_to
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return contact_list_field (MSG, MU_MSG_FIELD_ID_TO);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_cc, "mu:msg:cc", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the list of Cc:-recipients of MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_cc
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return contact_list_field (MSG, MU_MSG_FIELD_ID_CC);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_bcc, "mu:msg:bcc", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the list of Bcc:-recipients of MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_bcc
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return contact_list_field (MSG, MU_MSG_FIELD_ID_BCC);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_path, "mu:msg:path", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the filesystem path for MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_path
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_str_field (MSG, MU_MSG_FIELD_ID_PATH);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_maildir, "mu:msg:maildir", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the maildir where MSG lives.\n")
|
||||||
|
#define FUNC_NAME s_msg_maildir
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_str_field (MSG, MU_MSG_FIELD_ID_MAILDIR);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_msgid, "mu:msg:message-id", 1, 0, 0,
|
||||||
|
(SCM MSG), "Get the MSG's message-id.\n")
|
||||||
|
#define FUNC_NAME s_msg_msgid
|
||||||
|
{
|
||||||
|
return msg_str_field (MSG, MU_MSG_FIELD_ID_MSGID);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_body, "mu:msg:body", 1, 1, 0,
|
||||||
|
(SCM MSG, SCM HTML), "Get the MSG's body. If HTML is #t, "
|
||||||
|
"prefer the html-version, otherwise prefer plain text.\n")
|
||||||
|
#define FUNC_NAME s_msg_body
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
gboolean html;
|
||||||
|
const char *val;
|
||||||
|
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(MSG);
|
||||||
|
html = SCM_UNBNDP(HTML) ? FALSE : HTML == SCM_BOOL_T;
|
||||||
|
|
||||||
|
if (html)
|
||||||
|
val = mu_msg_get_body_html(msgwrap->_msg);
|
||||||
|
else
|
||||||
|
val = mu_msg_get_body_text(msgwrap->_msg);
|
||||||
|
|
||||||
|
return val ? scm_from_utf8_string (val) : SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_header, "mu:msg:header", 1, 1, 0,
|
||||||
|
(SCM MSG, SCM HEADER), "Get an arbitary HEADER from MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_header
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
const char *header;
|
||||||
|
const char *val;
|
||||||
|
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
SCM_ASSERT (scm_is_string (HEADER), HEADER, SCM_ARG2, FUNC_NAME);
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(MSG);
|
||||||
|
header = scm_to_utf8_string (HEADER);
|
||||||
|
val = mu_msg_get_header(msgwrap->_msg, header);
|
||||||
|
|
||||||
|
return val ? scm_from_utf8_string(val) : SCM_UNDEFINED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
msg_string_list_field (SCM msg_smob, MuMsgFieldId mfid)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
SCM scmlst;
|
||||||
|
const GSList *lst;
|
||||||
|
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
lst = mu_msg_get_field_string_list (msgwrap->_msg, mfid);
|
||||||
|
|
||||||
|
for (scmlst = SCM_EOL; lst;
|
||||||
|
lst = g_slist_next(lst)) {
|
||||||
|
SCM item;
|
||||||
|
item = scm_list_1 (scm_from_utf8_string((const char*)lst->data));
|
||||||
|
scmlst = scm_append_x (scm_list_2(scmlst, item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return scmlst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_tags, "mu:msg:tags", 1, 1, 0,
|
||||||
|
(SCM MSG), "Get the list of tags (contents of the "
|
||||||
|
"X-Label:-header) for MSG.\n")
|
||||||
|
#define FUNC_NAME s_msg_tags
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_string_list_field (MSG, MU_MSG_FIELD_ID_TAGS);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (msg_refs, "mu:msg:references", 1, 1, 0,
|
||||||
|
(SCM MSG), "Get the list of referenced message-ids "
|
||||||
|
"(contents of the References: and Reply-To: headers).\n")
|
||||||
|
#define FUNC_NAME s_msg_refs
|
||||||
|
{
|
||||||
|
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
return msg_string_list_field (MSG, MU_MSG_FIELD_ID_REFS);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
msg_mark (SCM msg_smob)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
msgwrap->_unrefme = TRUE;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scm_sizet
|
||||||
|
msg_free (SCM msg_smob)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
if (msgwrap->_unrefme)
|
||||||
|
mu_msg_unref (msgwrap->_msg);
|
||||||
|
|
||||||
|
return sizeof (MuMsgWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
msg_print (SCM msg_smob, SCM port, scm_print_state * pstate)
|
||||||
|
{
|
||||||
|
MuMsgWrapper *msgwrap;
|
||||||
|
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
|
||||||
|
|
||||||
|
scm_puts ("#<msg ", port);
|
||||||
|
|
||||||
|
if (msg_smob == SCM_BOOL_F)
|
||||||
|
scm_puts ("#f", port);
|
||||||
|
else
|
||||||
|
scm_puts (mu_msg_get_path(msgwrap->_msg),
|
||||||
|
port);
|
||||||
|
|
||||||
|
scm_puts (">", port);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void*
|
||||||
|
mu_guile_msg_init (void *data)
|
||||||
|
{
|
||||||
|
MSG_TAG = scm_make_smob_type ("msg", sizeof(MuMsgWrapper));
|
||||||
|
|
||||||
|
scm_set_smob_mark (MSG_TAG, msg_mark);
|
||||||
|
scm_set_smob_free (MSG_TAG, msg_free);
|
||||||
|
scm_set_smob_print (MSG_TAG, msg_print);
|
||||||
|
|
||||||
|
define_symbols ();
|
||||||
|
|
||||||
|
#include "mu-guile-msg.x"
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
52
libmuguile/mu-guile-msg.h
Normal file
52
libmuguile/mu-guile-msg.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
** 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_MSG_H__
|
||||||
|
#define __MU_GUILE_MSG_H__
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <mu-msg.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* register MuMsg-related functions/smobs with guile; use with
|
||||||
|
* scm_with_guile
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void *mu_guile_msg_init (void *data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create an SCM for the MuMsg*
|
||||||
|
*
|
||||||
|
* @param msg a MuMsg instance
|
||||||
|
*
|
||||||
|
* @return an SCM for the msg
|
||||||
|
*/
|
||||||
|
SCM mu_guile_msg_to_scm (MuMsg *msg);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*__MU_GUILE_MSG_H__*/
|
||||||
125
libmuguile/mu-guile-store.c
Normal file
125
libmuguile/mu-guile-store.c
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
** 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-query.h>
|
||||||
|
#include <mu-store.h>
|
||||||
|
#include <mu-runtime.h>
|
||||||
|
|
||||||
|
#include "mu-guile-msg.h"
|
||||||
|
#include "mu-guile-store.h"
|
||||||
|
#include "mu-guile-common.h"
|
||||||
|
|
||||||
|
static MuQuery*
|
||||||
|
get_query (void)
|
||||||
|
{
|
||||||
|
MuQuery *query;
|
||||||
|
GError *err;
|
||||||
|
|
||||||
|
err = NULL;
|
||||||
|
query = mu_query_new (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB), &err);
|
||||||
|
if (err) {
|
||||||
|
mu_guile_g_error ("<internal>", err);
|
||||||
|
g_error_free (err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static MuMsgIter*
|
||||||
|
get_query_iter (MuQuery *query, const char* expr)
|
||||||
|
{
|
||||||
|
MuMsgIter *iter;
|
||||||
|
GError *err;
|
||||||
|
|
||||||
|
err = NULL;
|
||||||
|
iter = mu_query_run (query, expr,
|
||||||
|
FALSE, MU_MSG_FIELD_ID_NONE, TRUE, &err);
|
||||||
|
if (err) {
|
||||||
|
mu_guile_g_error ("<internal>", err);
|
||||||
|
g_error_free (err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (store_foreach, "mu:store:foreach", 1, 1, 0,
|
||||||
|
(SCM FUNC, SCM EXPR),
|
||||||
|
"Call FUNC for each message in the store, or, if EXPR is specified, "
|
||||||
|
"for each message matching EXPR.\n")
|
||||||
|
#define FUNC_NAME s_store_foreach
|
||||||
|
{
|
||||||
|
MuQuery *query;
|
||||||
|
MuMsgIter *iter;
|
||||||
|
int count;
|
||||||
|
const char* expr;
|
||||||
|
|
||||||
|
SCM_ASSERT (scm_procedure_p (FUNC), FUNC, SCM_ARG1, FUNC_NAME);
|
||||||
|
SCM_ASSERT (scm_is_string (EXPR) || EXPR == SCM_UNSPECIFIED,
|
||||||
|
EXPR, SCM_ARG2, FUNC_NAME);
|
||||||
|
|
||||||
|
query = get_query ();
|
||||||
|
if (!query)
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
|
||||||
|
expr = SCM_UNBNDP(EXPR) ? NULL : scm_to_utf8_string(EXPR);
|
||||||
|
|
||||||
|
iter = get_query_iter (query, expr);
|
||||||
|
if (!iter)
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
|
||||||
|
for (count = 0; !mu_msg_iter_is_done(iter); mu_msg_iter_next (iter)) {
|
||||||
|
|
||||||
|
SCM msgsmob;
|
||||||
|
MuMsg *msg;
|
||||||
|
GError *err;
|
||||||
|
|
||||||
|
err = NULL;
|
||||||
|
msg = mu_msg_iter_get_msg (iter, &err);
|
||||||
|
if (err) {
|
||||||
|
mu_guile_g_error (FUNC_NAME, err);
|
||||||
|
g_error_free (err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgsmob = mu_guile_msg_to_scm (mu_msg_ref(msg));
|
||||||
|
scm_call_1 (FUNC, msgsmob);
|
||||||
|
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
mu_query_destroy (query);
|
||||||
|
|
||||||
|
return scm_from_int (count);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
void*
|
||||||
|
mu_guile_store_init (void *data)
|
||||||
|
{
|
||||||
|
#include "mu-guile-store.x"
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
39
libmuguile/mu-guile-store.h
Normal file
39
libmuguile/mu-guile-store.h
Normal 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_STORE_H__
|
||||||
|
#define __MU_GUILE_STORE_H__
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize mu:store functions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void *mu_guile_store_init (void *data);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
#endif /*__MU_GUILE_STORE_H__*/
|
||||||
@ -99,7 +99,6 @@ GSList* mu_msg_file_get_str_list_field (MuMsgFile *self,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get a numeric value for this message -- the return value should be
|
* get a numeric value for this message -- the return value should be
|
||||||
* cast into the actual type, e.g., time_t, MuMsgPrio etc.
|
* cast into the actual type, e.g., time_t, MuMsgPrio etc.
|
||||||
|
|||||||
80
src/mu-msg.c
80
src/mu-msg.c
@ -521,25 +521,19 @@ fill_contact (MuMsgContact *self, InternetAddress *addr,
|
|||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
address_list_foreach (InternetAddressList *addrlist,
|
address_list_foreach (InternetAddressList *addrlist, MuMsgContactType ctype,
|
||||||
MuMsgContactType ctype,
|
MuMsgContactForeachFunc func, gpointer user_data)
|
||||||
MuMsgContactForeachFunc func,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!addrlist)
|
for (i = 0; addrlist && i != internet_address_list_length(addrlist);
|
||||||
return;
|
++i) {
|
||||||
|
|
||||||
for (i = 0; i != internet_address_list_length(addrlist); ++i) {
|
|
||||||
|
|
||||||
MuMsgContact contact;
|
MuMsgContact contact;
|
||||||
if (!fill_contact(&contact,
|
if (!fill_contact(&contact,
|
||||||
internet_address_list_get_address (addrlist, i),
|
internet_address_list_get_address (addrlist, i),
|
||||||
ctype)) {
|
ctype))
|
||||||
MU_WRITE_LOG ("ignoring contact");
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (!(func)(&contact, user_data))
|
if (!(func)(&contact, user_data))
|
||||||
break;
|
break;
|
||||||
@ -547,30 +541,25 @@ address_list_foreach (InternetAddressList *addrlist,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_contacts_from (MuMsg *msg, MuMsgContactForeachFunc func,
|
addresses_foreach (const char* addrs, MuMsgContactType ctype,
|
||||||
gpointer user_data)
|
MuMsgContactForeachFunc func, gpointer user_data)
|
||||||
{
|
{
|
||||||
InternetAddressList *lst;
|
InternetAddressList *addrlist;
|
||||||
|
|
||||||
/* we go through this whole excercise of trying to get a *list*
|
if (!addrs)
|
||||||
* of 'From:' address (usually there is only one...), because
|
return;
|
||||||
* internet_address_parse_string has the nice side-effect of
|
|
||||||
* splitting in names and addresses for us */
|
|
||||||
lst = internet_address_list_parse_string (
|
|
||||||
g_mime_message_get_sender (msg->_file->_mime_msg));
|
|
||||||
|
|
||||||
if (lst) {
|
addrlist = internet_address_list_parse_string (addrs);
|
||||||
address_list_foreach (lst, MU_MSG_CONTACT_TYPE_FROM,
|
if (addrlist) {
|
||||||
func, user_data);
|
address_list_foreach (addrlist, ctype, func, user_data);
|
||||||
g_object_unref (G_OBJECT(lst));
|
g_object_unref (addrlist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func,
|
msg_contact_foreach_file (MuMsg *msg, MuMsgContactForeachFunc func,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -583,10 +572,9 @@ mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func,
|
|||||||
{GMIME_RECIPIENT_TYPE_BCC, MU_MSG_CONTACT_TYPE_BCC},
|
{GMIME_RECIPIENT_TYPE_BCC, MU_MSG_CONTACT_TYPE_BCC},
|
||||||
};
|
};
|
||||||
|
|
||||||
g_return_if_fail (func && msg);
|
/* sender */
|
||||||
|
addresses_foreach (g_mime_message_get_sender (msg->_file->_mime_msg),
|
||||||
/* first, get the from address(es) */
|
MU_MSG_CONTACT_TYPE_FROM, func, user_data);
|
||||||
get_contacts_from (msg, func, user_data);
|
|
||||||
|
|
||||||
/* get to, cc, bcc */
|
/* get to, cc, bcc */
|
||||||
for (i = 0; i != G_N_ELEMENTS(ctypes); ++i) {
|
for (i = 0; i != G_N_ELEMENTS(ctypes); ++i) {
|
||||||
@ -598,6 +586,38 @@ mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
msg_contact_foreach_doc (MuMsg *msg, MuMsgContactForeachFunc func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
addresses_foreach (mu_msg_get_from (msg),
|
||||||
|
MU_MSG_CONTACT_TYPE_FROM, func, user_data);
|
||||||
|
addresses_foreach (mu_msg_get_to (msg),
|
||||||
|
MU_MSG_CONTACT_TYPE_TO, func, user_data);
|
||||||
|
addresses_foreach (mu_msg_get_cc (msg),
|
||||||
|
MU_MSG_CONTACT_TYPE_CC, func, user_data);
|
||||||
|
addresses_foreach (mu_msg_get_bcc (msg),
|
||||||
|
MU_MSG_CONTACT_TYPE_BCC, func, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_return_if_fail (msg);
|
||||||
|
g_return_if_fail (func);
|
||||||
|
|
||||||
|
if (msg->_doc)
|
||||||
|
msg_contact_foreach_doc (msg, func, user_data);
|
||||||
|
else if (msg->_file)
|
||||||
|
msg_contact_foreach_file (msg, func, user_data);
|
||||||
|
else
|
||||||
|
g_return_if_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cmp_str (const char* s1, const char *s2)
|
cmp_str (const char* s1, const char *s2)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -77,7 +77,7 @@ MuMsg *mu_msg_new_from_doc (XapianDocument* doc, GError **err)
|
|||||||
* @return the message with its reference count increased, or NULL in
|
* @return the message with its reference count increased, or NULL in
|
||||||
* case of error.
|
* case of error.
|
||||||
*/
|
*/
|
||||||
MuMsg * mu_msg_ref (MuMsg *msg);
|
MuMsg *mu_msg_ref (MuMsg *msg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* decrease the reference count for this message. if the reference
|
* decrease the reference count for this message. if the reference
|
||||||
@ -305,7 +305,7 @@ MuMsgPrio mu_msg_get_prio (MuMsg *msg);
|
|||||||
*
|
*
|
||||||
* @return the timestamp or 0 in case of error
|
* @return the timestamp or 0 in case of error
|
||||||
*/
|
*/
|
||||||
time_t mu_msg_get_timestamp (MuMsg *msg);
|
time_t mu_msg_get_timestamp (MuMsg *msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -334,10 +334,6 @@ const char* mu_msg_get_header (MuMsg *self, const char *header);
|
|||||||
*/
|
*/
|
||||||
const GSList* mu_msg_get_references (MuMsg *msg);
|
const GSList* mu_msg_get_references (MuMsg *msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the list of tags (ie., X-Label)
|
* get the list of tags (ie., X-Label)
|
||||||
*
|
*
|
||||||
|
|||||||
@ -222,29 +222,36 @@ struct _MuQuery {
|
|||||||
MuSizeRangeProcessor _size_range_processor;
|
MuSizeRangeProcessor _size_range_processor;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
static const Xapian::Query
|
||||||
set_query (MuQuery *mqx, Xapian::Query& q, const char* searchexpr,
|
get_query (MuQuery *mqx, const char* searchexpr, GError **err)
|
||||||
GError **err) {
|
{
|
||||||
|
Xapian::Query query;
|
||||||
|
char *preprocessed;
|
||||||
|
|
||||||
|
preprocessed = mu_query_preprocess (searchexpr);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
q = mqx->_qparser.parse_query
|
query = mqx->_qparser.parse_query
|
||||||
(searchexpr,
|
(preprocessed,
|
||||||
Xapian::QueryParser::FLAG_BOOLEAN |
|
Xapian::QueryParser::FLAG_BOOLEAN |
|
||||||
Xapian::QueryParser::FLAG_PURE_NOT |
|
Xapian::QueryParser::FLAG_PURE_NOT |
|
||||||
Xapian::QueryParser::FLAG_WILDCARD |
|
Xapian::QueryParser::FLAG_WILDCARD |
|
||||||
Xapian::QueryParser::FLAG_AUTO_SYNONYMS |
|
Xapian::QueryParser::FLAG_AUTO_SYNONYMS |
|
||||||
Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE);
|
Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE);
|
||||||
|
g_free (preprocessed);
|
||||||
|
return query;
|
||||||
|
|
||||||
return true;
|
} catch (...) {
|
||||||
|
/* some error occured */
|
||||||
} MU_XAPIAN_CATCH_BLOCK;
|
g_set_error (err, 0, MU_ERROR_QUERY, "parse error in query '%s'",
|
||||||
|
searchexpr);
|
||||||
/* some error occured */
|
g_free (preprocessed);
|
||||||
g_set_error (err, 0, MU_ERROR_QUERY, "parse error in query '%s'",
|
throw;
|
||||||
searchexpr);
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_prefix (MuMsgFieldId mfid, Xapian::QueryParser* qparser)
|
add_prefix (MuMsgFieldId mfid, Xapian::QueryParser* qparser)
|
||||||
{
|
{
|
||||||
@ -343,17 +350,6 @@ mu_query_run (MuQuery *self, const char* searchexpr, gboolean threads,
|
|||||||
sortfieldid == MU_MSG_FIELD_ID_NONE,
|
sortfieldid == MU_MSG_FIELD_ID_NONE,
|
||||||
NULL);
|
NULL);
|
||||||
try {
|
try {
|
||||||
Xapian::Query query;
|
|
||||||
char *preprocessed;
|
|
||||||
|
|
||||||
preprocessed = mu_query_preprocess (searchexpr);
|
|
||||||
|
|
||||||
if (!set_query(self, query, preprocessed, err)) {
|
|
||||||
g_free (preprocessed);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
g_free (preprocessed);
|
|
||||||
|
|
||||||
Xapian::Enquire enq (self->_db);
|
Xapian::Enquire enq (self->_db);
|
||||||
|
|
||||||
/* note, when our result will be *threaded*, we sort
|
/* note, when our result will be *threaded*, we sort
|
||||||
@ -361,7 +357,13 @@ mu_query_run (MuQuery *self, const char* searchexpr, gboolean threads,
|
|||||||
if (!threads && sortfieldid != MU_MSG_FIELD_ID_NONE)
|
if (!threads && sortfieldid != MU_MSG_FIELD_ID_NONE)
|
||||||
enq.set_sort_by_value ((Xapian::valueno)sortfieldid,
|
enq.set_sort_by_value ((Xapian::valueno)sortfieldid,
|
||||||
ascending ? true : false);
|
ascending ? true : false);
|
||||||
enq.set_query(query);
|
|
||||||
|
if (!mu_str_is_empty(searchexpr)) /* NULL or "" */
|
||||||
|
enq.set_query(get_query (self, searchexpr, err));
|
||||||
|
else
|
||||||
|
enq.set_query(Xapian::Query::MatchAll);
|
||||||
|
|
||||||
|
|
||||||
enq.set_cutoff(0,0);
|
enq.set_cutoff(0,0);
|
||||||
|
|
||||||
return mu_msg_iter_new (
|
return mu_msg_iter_new (
|
||||||
@ -379,17 +381,7 @@ mu_query_as_string (MuQuery *self, const char *searchexpr, GError **err)
|
|||||||
g_return_val_if_fail (searchexpr, NULL);
|
g_return_val_if_fail (searchexpr, NULL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Xapian::Query query;
|
Xapian::Query query (get_query(self, searchexpr, err));
|
||||||
char *preprocessed;
|
|
||||||
|
|
||||||
preprocessed = mu_query_preprocess (searchexpr);
|
|
||||||
|
|
||||||
if (!set_query(self, query, preprocessed, err)) {
|
|
||||||
g_free (preprocessed);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
g_free (preprocessed);
|
|
||||||
|
|
||||||
return g_strdup(query.get_description().c_str());
|
return g_strdup(query.get_description().c_str());
|
||||||
|
|
||||||
} MU_XAPIAN_CATCH_BLOCK_RETURN(NULL);
|
} MU_XAPIAN_CATCH_BLOCK_RETURN(NULL);
|
||||||
|
|||||||
@ -68,7 +68,7 @@ char* mu_query_version (MuQuery *store)
|
|||||||
* manpage, or http://xapian.org/docs/queryparser.html
|
* manpage, or http://xapian.org/docs/queryparser.html
|
||||||
*
|
*
|
||||||
* @param self a valid MuQuery instance
|
* @param self a valid MuQuery instance
|
||||||
* @param expr the search expression
|
* @param expr the search expression or "" to match all messages
|
||||||
* @param threads calculate message-threads
|
* @param threads calculate message-threads
|
||||||
* @param sortfield the field id to sort by or MU_MSG_FIELD_ID_NONE if
|
* @param sortfield the field id to sort by or MU_MSG_FIELD_ID_NONE if
|
||||||
* sorting is not desired
|
* sorting is not desired
|
||||||
|
|||||||
@ -78,45 +78,6 @@ typedef enum _MuRuntimePath MuRuntimePath;
|
|||||||
const char* mu_runtime_path (MuRuntimePath path);
|
const char* mu_runtime_path (MuRuntimePath path);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the mu home directory (typically, ~/.mu); this can only be
|
|
||||||
* called after mu_runtime_init and before mu_runtime_uninit
|
|
||||||
*
|
|
||||||
* @return mu home directory as a string which should be not be
|
|
||||||
* modified, or NULL in case of error.
|
|
||||||
*/
|
|
||||||
const char* mu_runtime_mu_home_dir (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the xapian directory (typically, ~/.mu/xapian/); this can only
|
|
||||||
* be called after mu_runtime_init and before mu_runtime_uninit
|
|
||||||
*
|
|
||||||
* @return the xapian directory as a string which should be not be
|
|
||||||
* modified, or NULL in case of error.
|
|
||||||
*/
|
|
||||||
const char* mu_runtime_xapian_dir (void);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the mu bookmarks file (typically, ~/.mu/bookmarks); this can
|
|
||||||
* only be called after mu_runtime_init and before mu_runtime_uninit
|
|
||||||
*
|
|
||||||
* @return the bookmarks file as a string which should be not be
|
|
||||||
* modified, or NULL in case of error.
|
|
||||||
*/
|
|
||||||
const char* mu_runtime_bookmarks_file (void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the mu contacts cache file name (typically,
|
|
||||||
* ~/.mu/contacts.cache); this can only be called after
|
|
||||||
* mu_runtime_init and before mu_runtime_uninit
|
|
||||||
*
|
|
||||||
* @return the contacts cache file name as a string which should be not be
|
|
||||||
* modified, or NULL in case of error.
|
|
||||||
*/
|
|
||||||
const char* mu_runtime_contacts_cache_file (void);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the mu configuration options (ie., the parsed command line
|
* get the mu configuration options (ie., the parsed command line
|
||||||
* parameters)
|
* parameters)
|
||||||
|
|||||||
@ -443,7 +443,6 @@ mu_str_subject_normalize (const gchar* str)
|
|||||||
gchar *last_colon;
|
gchar *last_colon;
|
||||||
g_return_val_if_fail (str, NULL);
|
g_return_val_if_fail (str, NULL);
|
||||||
|
|
||||||
/* FIXME: improve this */
|
|
||||||
last_colon = g_strrstr (str, ":");
|
last_colon = g_strrstr (str, ":");
|
||||||
if (!last_colon)
|
if (!last_colon)
|
||||||
return str;
|
return str;
|
||||||
|
|||||||
@ -150,6 +150,7 @@ test_mu_query_01 (void)
|
|||||||
{ "foo:pepernoot", 0 },
|
{ "foo:pepernoot", 0 },
|
||||||
{ "funky", 1 },
|
{ "funky", 1 },
|
||||||
{ "fünkÿ", 1 },
|
{ "fünkÿ", 1 },
|
||||||
|
{ "", 13 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,3 +28,7 @@ if BUILD_WIDGETS
|
|||||||
SUBDIRS += mug2
|
SUBDIRS += mug2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# for muile, we need guile
|
||||||
|
if HAVE_GUILE
|
||||||
|
SUBDIRS += muile
|
||||||
|
endif
|
||||||
|
|||||||
35
toys/muile/Makefile.am
Normal file
35
toys/muile/Makefile.am
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
## 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
|
||||||
|
|
||||||
|
# enforce compiling this dir first before decending into tests/
|
||||||
|
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= \
|
||||||
|
muile
|
||||||
|
|
||||||
|
muile_SOURCES= \
|
||||||
|
muile.cc
|
||||||
|
|
||||||
|
muile_LDFLAGS= \
|
||||||
|
${top_builddir}/libmuguile/libmuguile.la
|
||||||
84
toys/muile/README
Normal file
84
toys/muile/README
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
* README
|
||||||
|
|
||||||
|
** What is muile?
|
||||||
|
|
||||||
|
`muile' is a little experiment/toy using the equally mu guile bindings, to be
|
||||||
|
found in libmuguile/ in the top-level source directory.
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
The combination of mu + guile is called `muile', and allows you to write
|
||||||
|
little Scheme-programs to query the mu-database, or inspect individual
|
||||||
|
messages. It is still in an experimental stage, but useful already.
|
||||||
|
|
||||||
|
** How do I get it?
|
||||||
|
|
||||||
|
The git-version and the future 0.9.7 version of mu will automatically build
|
||||||
|
muile if you have guile. I've been using guile 2.x from git, but installing
|
||||||
|
the 'guile-1.8-dev' package (Ubuntu/Debian) should do the trick (I did not
|
||||||
|
test with 1.8 though).
|
||||||
|
|
||||||
|
Then, configure mu. The configure output should tell you about whether guile
|
||||||
|
was found (and where). If it's found, build mu, and toys/muile should be
|
||||||
|
created, as well.
|
||||||
|
|
||||||
|
** What can I do with it?
|
||||||
|
|
||||||
|
Go to toys/muile and start muile. You'll end up with a guile-shell where you
|
||||||
|
can type scheme [1], it looks something like this (for guile 2.x):
|
||||||
|
|
||||||
|
,----
|
||||||
|
| scheme@(guile-user)>
|
||||||
|
`----
|
||||||
|
|
||||||
|
Now, let's load a message (of course, replace with a message on your system):
|
||||||
|
|
||||||
|
,----
|
||||||
|
| scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S"))
|
||||||
|
`----
|
||||||
|
|
||||||
|
This defines a variable 'msg', which holds some message on your file
|
||||||
|
system. It's now easy to inspect this message:
|
||||||
|
|
||||||
|
,----
|
||||||
|
| scheme@(guile-user)> (define msg (mu:msg:make-from-file "/home/djcb/Maildir/cur/12131e7b20a2:2,S"))
|
||||||
|
`----
|
||||||
|
|
||||||
|
Now, we can inspect this message a bit:
|
||||||
|
,----
|
||||||
|
| scheme@(guile-user)> (mu:msg:subject msg)
|
||||||
|
| $1 = "See me in bikini :-)"
|
||||||
|
| scheme@(guile-user)> (mu:msg:flags msg)
|
||||||
|
| $2 = (attach 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)
|
||||||
|
|
||||||
|
in your ~/.guile configuration.
|
||||||
|
|
||||||
|
** Can I do some statistics on my messages?
|
||||||
|
|
||||||
|
Yes you can. It's pretty easy in guile. See the mu:stats functions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[1] http://www.gnu.org/s/guile/
|
||||||
|
[2] http://en.wikipedia.org/wiki/Scheme_(programming_language)
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# mode: org; org-startup-folded: nil
|
||||||
|
# End:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
160
toys/muile/mu-stats.scm
Normal file
160
toys/muile/mu-stats.scm
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
;;
|
||||||
|
;; 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.
|
||||||
|
|
||||||
|
;; some guile/scheme functions to get various statistics of my mu
|
||||||
|
;; message store.
|
||||||
|
|
||||||
|
;; note, this is a rather inefficient way to calculate the number; for
|
||||||
|
;; demonstration purposes only
|
||||||
|
(define* (mu:stats:count #:optional (EXPR ""))
|
||||||
|
"Count the total number of messages. If the optional EXPR is
|
||||||
|
provided, only count the messages that match it.\n"
|
||||||
|
(mu:store:foreach (lambda(msg) #f) EXPR))
|
||||||
|
|
||||||
|
(define* (mu:stats:average FUNC #:optional (EXPR ""))
|
||||||
|
"Count the average of the result of applying FUNC on all
|
||||||
|
messages. If the optional EXPR is provided, only consider the messages
|
||||||
|
that match it.\n"
|
||||||
|
(let* ((sum 0)
|
||||||
|
(n (mu:store:foreach
|
||||||
|
(lambda(msg) (set! sum (+ sum (FUNC msg)))) EXPR)))
|
||||||
|
(if (= n 0) 0 (exact->inexact (/ sum n)))))
|
||||||
|
|
||||||
|
(define* (mu:stats:average-size #:optional (EXPR ""))
|
||||||
|
"Calculate the average message size. If the optional EXPR is
|
||||||
|
provided, only consider the messages that match it.\n"
|
||||||
|
(mu:stats:average (lambda(msg) (mu:msg:size msg)) EXPR))
|
||||||
|
|
||||||
|
(define* (mu:stats:average-recipient-number #:optional (EXPR ""))
|
||||||
|
"Calculate the average number of recipients (To: + CC: + Bcc:). If
|
||||||
|
the optional EXPR is provided, only consider the messages that match
|
||||||
|
it.\n"
|
||||||
|
(mu:stats:average (lambda(msg)
|
||||||
|
(+(length (mu:msg:to msg))
|
||||||
|
(length (mu:msg:cc msg))
|
||||||
|
(length (mu:msg:bcc msg)))) EXPR))
|
||||||
|
|
||||||
|
(define* (mu:stats:frequency FUNC #:optional (EXPR ""))
|
||||||
|
"FUNC is a function that takes a Msg, and returns the frequency of
|
||||||
|
the different values this function returns. If FUNC returns a list,
|
||||||
|
update the frequency table for each element of this list. If the
|
||||||
|
optional EXPR is provided, only consider messages that match it.\n"
|
||||||
|
(let ((table '()))
|
||||||
|
(mu:store:foreach
|
||||||
|
(lambda(msg)
|
||||||
|
;; note, if val is not already a list, turn it into a list
|
||||||
|
;; then, take frequency for each element in the list
|
||||||
|
(let* ((val (FUNC msg)) (vals (if (list? val) val (list val))))
|
||||||
|
(for-each
|
||||||
|
(lambda (val)
|
||||||
|
(let ((freq (assoc-ref table val)))
|
||||||
|
(set! table (assoc-set! table val
|
||||||
|
(+ 1 (if (eq? freq #f) 0 freq)))))) vals))) EXPR)
|
||||||
|
table))
|
||||||
|
|
||||||
|
|
||||||
|
(define* (mu:stats:per-weekday #:optional (EXPR ""))
|
||||||
|
"Count the total number of messages for each weekday (0-6 for
|
||||||
|
Sun..Sat). If the optional EXPR is provided, only count the messages
|
||||||
|
that match it. The result is a list of pairs (weekday . frequency).\n"
|
||||||
|
(let* ((stats (mu:stats:frequency
|
||||||
|
(lambda (msg) (tm:wday (localtime (mu:msg:date msg)))) EXPR)))
|
||||||
|
(sort stats (lambda(a b) (< (car a) (car b)))))) ;; in order of weekday
|
||||||
|
|
||||||
|
(define* (mu:stats:per-month #:optional (EXPR ""))
|
||||||
|
"Count the total number of messages for each month (0-11 for
|
||||||
|
Jan..Dec). If the optional EXPR is provided, only count the messages
|
||||||
|
that match it. The result is a list of pairs (month . frequency).\n"
|
||||||
|
(let* ((stats (mu:stats:frequency
|
||||||
|
(lambda (msg) (tm:mon (localtime (mu:msg:date msg)))) EXPR)))
|
||||||
|
(sort stats (lambda(a b) (< (car a) (car b)))))) ;; in order of month
|
||||||
|
|
||||||
|
(define* (mu:stats:per-hour #:optional (EXPR ""))
|
||||||
|
"Count the total number of messages for each weekday (0-6 for
|
||||||
|
Sun..Sat). If the optional EXPR is provided, only count the messages
|
||||||
|
that match it. The result is a list of pairs (weekday . frequency).\n"
|
||||||
|
(let* ((stats (mu:stats:frequency
|
||||||
|
(lambda (msg) (tm:hour (localtime (mu:msg:date msg)))) EXPR)))
|
||||||
|
(sort stats (lambda(a b) (< (car a) (car b)))))) ;; in order of hour
|
||||||
|
|
||||||
|
|
||||||
|
(define* (mu:stats:per-year #:optional (EXPR ""))
|
||||||
|
"Count the total number of messages for each year since 1970. If the
|
||||||
|
optional EXPR is provided, only count the messages that match it. The
|
||||||
|
result is a list of pairs (year . frequency).\n"
|
||||||
|
(let* ((stats (mu:stats:frequency
|
||||||
|
(lambda (msg) (+ 1900 (tm:year (localtime (mu:msg:date msg)))))
|
||||||
|
EXPR)))
|
||||||
|
(sort stats (lambda(a b) (< (car a) (car b)))))) ;; in order of year
|
||||||
|
|
||||||
|
|
||||||
|
(define* (mu:stats:top-n FUNC N #:optional (EXPR ""))
|
||||||
|
"Get the Top-N frequency of the result of FUNC applied on each
|
||||||
|
message. If the optional EXPR is provided, only consider the messages
|
||||||
|
that match it."
|
||||||
|
(let* ((freq (mu:stats:frequency FUNC EXPR))
|
||||||
|
(top (sort freq (lambda (a b) (< (cdr b) (cdr a) )))))
|
||||||
|
(list-head top (min (length freq) N))))
|
||||||
|
|
||||||
|
(define* (mu:stats:top-n-to #:optional (N 10) (EXPR ""))
|
||||||
|
"Get the Top-N To:-recipients. If the optional N is not provided,
|
||||||
|
use 10. If the optional EXPR is provided, only consider the messages
|
||||||
|
that match it."
|
||||||
|
(mu:stats:top-n
|
||||||
|
(lambda (msg) (mu:msg:to msg)) N EXPR))
|
||||||
|
|
||||||
|
(define* (mu:stats:top-n-from #:optional (N 10) (EXPR ""))
|
||||||
|
"Get the Top-N senders (From:). If the optional N is not provided,
|
||||||
|
use 10. If the optional EXPR is provided, only consider the messages
|
||||||
|
that match it."
|
||||||
|
(mu:stats:top-n
|
||||||
|
(lambda (msg) (mu:msg:from msg)) N EXPR))
|
||||||
|
|
||||||
|
(define* (mu:stats:top-n-subject #:optional (N 10) (EXPR ""))
|
||||||
|
"Get the Top-N subjects. If the optional N is not provided,
|
||||||
|
use 10. If the optional EXPR is provided, only consider the messages
|
||||||
|
that match it."
|
||||||
|
(mu:stats:top-n
|
||||||
|
(lambda (msg) (mu:msg:subject msg)) N EXPR))
|
||||||
|
|
||||||
|
(define (mu:stats:table pairs)
|
||||||
|
"display a list of PAIRS in a table-like fashion"
|
||||||
|
(let ((maxlen 0))
|
||||||
|
(for-each ;; find the widest in the first col
|
||||||
|
(lambda (pair)
|
||||||
|
(set! maxlen
|
||||||
|
(max maxlen (string-length (format #f "~s " (car pair)))))) pairs)
|
||||||
|
(for-each
|
||||||
|
(lambda (pair)
|
||||||
|
(let ((first (format #f "~s" (car pair)))
|
||||||
|
(second (format #f "~s" (cdr pair))))
|
||||||
|
(display (format #f "~A~v_~A\n"
|
||||||
|
first (- maxlen (string-length first)) second))))
|
||||||
|
pairs)))
|
||||||
|
|
||||||
|
(define (mu:stats:plot pairs)
|
||||||
|
"plot a table using gnuplot"
|
||||||
|
;; create a tmpfile with the data...
|
||||||
|
(let* ((datafile (tmpnam))
|
||||||
|
(output (open datafile (logior O_CREAT O_WRONLY) #O0644)))
|
||||||
|
(for-each
|
||||||
|
(lambda (pair) (display (format #f "~A ~A\n" (car pair) (cdr pair)) output))
|
||||||
|
pairs)
|
||||||
|
(close-output-port output)
|
||||||
|
;; now, display it.
|
||||||
|
(system (format #f "gnuplot -p -e 'plot \"~A\" w boxes fs pattern 2'" datafile))))
|
||||||
40
toys/muile/muile.cc
Normal file
40
toys/muile/muile.cc
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
** 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-runtime.h>
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <libmuguile/mu-guile-msg.h>
|
||||||
|
#include <libmuguile/mu-guile-store.h>
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
mu_runtime_init ("/home/djcb/.mu");
|
||||||
|
|
||||||
|
scm_with_guile (&mu_guile_msg_init, NULL);
|
||||||
|
scm_with_guile (&mu_guile_store_init, NULL);
|
||||||
|
|
||||||
|
scm_shell (argc, argv);
|
||||||
|
|
||||||
|
mu_runtime_uninit ();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user