* some code re-arrangement: src/guile-> guile, toys/mm -> emacs

This commit is contained in:
djcb
2011-12-13 08:03:19 +02:00
parent eb7bd05487
commit 8b39c69c89
31 changed files with 10 additions and 22 deletions

77
guile/Makefile.am Normal file
View File

@ -0,0 +1,77 @@
## 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 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
# 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
# note, we need top_builddir for snarfing with 'make distcheck' (ie.,
# with separate builddir)
SUBDIRS= . mu
INCLUDES=-I. -I${top_builddir} -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
lib_LTLIBRARIES= \
libguile-mu.la
libguile_mu_la_SOURCES= \
mu-guile.c \
mu-guile.h \
mu-guile-msg.c \
mu-guile-msg.h \
mu-guile-store.c \
mu-guile-store.h \
mu-guile-log.c \
mu-guile-log.h \
mu-guile-util.c \
mu-guile-util.h
libguile_mu_la_LIBADD= \
${top_builddir}/src/libmu.la \
${GUILE_LIBS}
XFILES= \
mu-guile-msg.x \
mu-guile-store.x \
mu-guile-log.x \
mu-guile.x
moduledir=$(GUILE_SITEDIR)
module_DATA= \
mu.scm
BUILT_SOURCES=$(XFILES)
snarfcppopts= $(DEFS) $(AM_CPPFLAGS) $(CPPFLAGS) $(CFLAGS) $(INCLUDES)
SUFFIXES = .x .doc
.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)
EXTRA_DIST= \
mu.scm

94
guile/mu-guile-log.c Normal file
View File

@ -0,0 +1,94 @@
/*
** 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-util.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_PUBLIC (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_PUBLIC (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_PUBLIC (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
guile/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__*/

579
guile/mu-guile-msg.c Normal file
View File

@ -0,0 +1,579 @@
/*
** 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-util.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_PUBLIC (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_util_g_error (FUNC_NAME, err);
g_error_free (err);
}
return msg ? mu_guile_msg_to_scm (msg) : SCM_UNDEFINED;
}
#undef FUNC_NAME
SCM_DEFINE_PUBLIC (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;
MuFlags flags;
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;
flags = mu_msg_get_flags (msgwrap->_msg);
rv = mu_msg_move_to_maildir (msgwrap->_msg,
scm_to_utf8_string (TARGETMDIR), flags,
FALSE, &err);
if (!rv && err) {
mu_guile_util_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)
{
return str ? scm_from_utf8_string (str) : SCM_UNSPECIFIED;
}
static SCM
msg_str_field (SCM msg_smob, MuMsgFieldId mfid)
{
MuMsgWrapper *msgwrap;
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
return scm_from_string_or_null (
mu_msg_get_field_string(msgwrap->_msg, mfid));
}
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_PUBLIC (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_PUBLIC (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_PUBLIC (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_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);
}
}
#undef FUNC_NAME
struct _FlagData {
MuFlags flags;
SCM lst;
};
typedef struct _FlagData FlagData;
static void
check_flag (MuFlags flag, FlagData *fdata)
{
if (fdata->flags & flag) {
SCM item;
char *flagsym;
flagsym = g_strconcat ("mu:", mu_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));
}
}
SCM_DEFINE_PUBLIC (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_flags_foreach ((MuFlagsForeachFunc)check_flag,
&fdata);
return fdata.lst;
}
#undef FUNC_NAME
SCM_DEFINE_PUBLIC (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
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 (
scm_from_string_or_null(name),
scm_from_string_or_null(addr)));
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_PUBLIC (msg_from, "mu:msg:from", 1, 0, 0,
(SCM MSG), "Get the list of senders of MSG.\n")
#define FUNC_NAME s_msg_from
{
SCM_ASSERT (mu_guile_scm_is_msg(MSG), MSG, SCM_ARG1, FUNC_NAME);
return contact_list_field (MSG, MU_MSG_FIELD_ID_FROM);
}
#undef FUNC_NAME
SCM_DEFINE_PUBLIC (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_PUBLIC (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_PUBLIC (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_PUBLIC (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_PUBLIC (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_PUBLIC (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_PUBLIC (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 scm_from_string_or_null (val);
}
#undef FUNC_NAME
SCM_DEFINE_PUBLIC (msg_header, "mu:msg:header", 2, 0, 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_UNDEFINED,
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_string_or_null(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_string_or_null((const char*)lst->data));
scmlst = scm_append_x (scm_list_2(scmlst, item));
}
return scmlst;
}
SCM_DEFINE_PUBLIC (msg_tags, "mu:msg:tags", 1, 0, 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_PUBLIC (msg_refs, "mu:msg:references", 1, 0, 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 size_t
msg_free (SCM msg_smob)
{
MuMsgWrapper *msgwrap;
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
if (msgwrap->_unrefme)
mu_msg_unref (msgwrap->_msg);
return sizeof (MuMsgWrapper);
}
static int
msg_print (SCM msg_smob, SCM port, scm_print_state * pstate)
{
MuMsgWrapper *msgwrap;
msgwrap = (MuMsgWrapper*) SCM_CDR(msg_smob);
scm_puts ("#<msg ", port);
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 ("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 ("mu:new", scm_from_int(MU_FLAG_NEW));
scm_c_define ("mu:passed", scm_from_int(MU_FLAG_PASSED));
scm_c_define ("mu:replied", scm_from_int(MU_FLAG_REPLIED));
scm_c_define ("mu:seen", scm_from_int(MU_FLAG_SEEN));
scm_c_define ("mu:trashed", scm_from_int(MU_FLAG_TRASHED));
scm_c_define ("mu:draft", scm_from_int(MU_FLAG_DRAFT));
scm_c_define ("mu:flagged", scm_from_int(MU_FLAG_FLAGGED));
scm_c_define ("mu:signed", scm_from_int(MU_FLAG_SIGNED));
scm_c_define ("mu:encrypted", scm_from_int(MU_FLAG_ENCRYPTED));
scm_c_define ("mu:has-attach", scm_from_int(MU_FLAG_HAS_ATTACH));
scm_c_define ("mu:unread", scm_from_int(MU_FLAG_UNREAD));
}
gboolean
mu_guile_msg_load_current (const char *path)
{
MuMsg *msg;
GError *err;
SCM msgsmob;
err = NULL;
msg = mu_msg_new_from_file (path, NULL, &err);
if (!msg) {
g_printerr ("error creating message for '%s'", path);
if (err) {
g_printerr (": %s", err->message);
g_error_free (err);
}
g_printerr ("\n");
return FALSE;
}
msgsmob = mu_guile_msg_to_scm (msg);
scm_c_define ("mu:current-msg", msgsmob);
return TRUE;
}
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;
}

65
guile/mu-guile-msg.h Normal file
View File

@ -0,0 +1,65 @@
/*
** 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*/
typedef void* MuGuileFunc (void*);
/**
* register MuMsg-related functions/smobs with guile; use with
* scm_with_guile
*
* @param data
*/
void *mu_guile_msg_init (void *data);
/**
* set 'mu:msg:current in the guile env
*
* @param path path to a message
*
* @return TRUE if it worked, FALSE otherwise
*/
gboolean mu_guile_msg_load_current (const char *path);
/**
* 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__*/

127
guile/mu-guile-store.c Normal file
View File

@ -0,0 +1,127 @@
/*
** 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-util.h"
static MuQuery*
get_query (void)
{
MuQuery *query;
MuStore *store;
GError *err;
err = NULL;
store = mu_store_new_read_only (mu_runtime_path(MU_RUNTIME_PATH_XAPIANDB),
&err);
query = store ? mu_query_new (store, &err) : NULL;
if (store)
mu_store_unref (store);
if (!query) {
mu_guile_util_g_error ("<internal error>", err);
g_clear_error (&err);
}
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, -1, &err);
if (!iter) {
mu_guile_util_g_error ("<internal error>", err);
g_clear_error (&err);
}
return iter;
}
static void
call_func (SCM FUNC, MuMsgIter *iter, const char* func_name)
{
SCM msgsmob;
MuMsg *msg;
msg = mu_msg_iter_get_msg_floating (iter); /* don't unref */
msgsmob = mu_guile_msg_to_scm (mu_msg_ref(msg));
scm_call_1 (FUNC, msgsmob);
}
SCM_DEFINE_PUBLIC (store_foreach, "mu:store:for-each", 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_UNBNDP(EXPR) || scm_is_string (EXPR),
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)) {
call_func (FUNC, iter, FUNC_NAME);
++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
guile/mu-guile-store.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_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__*/

155
guile/mu-guile-util.c Normal file
View File

@ -0,0 +1,155 @@
/*
** 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-util.h"
SCM
mu_guile_util_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)));
return SCM_UNSPECIFIED;
}
SCM
mu_guile_util_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 ? err->message : "error"),
SCM_UNDEFINED, SCM_UNDEFINED);
return SCM_UNSPECIFIED;
}
#include <mu-runtime.h>
#include <glib-object.h>
#include "mu-guile-util.h"
#include "mu-guile-msg.h"
struct _GuileConfig {
const char *muhome;
char *msgpath;
};
typedef struct _GuileConfig GuileConfig;
static GuileConfig*
guile_config_new (int *argcp, char ***argvp)
{
GOptionContext *octx;
GuileConfig *opts = g_new0 (GuileConfig, 1);
GOptionEntry entries[] = {
{"muhome", 0, 0, G_OPTION_ARG_FILENAME, &opts->muhome,
"specify an alternative mu directory", NULL},
{"msg", 0, 0, G_OPTION_ARG_FILENAME, &opts->msgpath,
"specify path to a message to load as mu:current-msg)", NULL},
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}/* sentinel */
};
octx = g_option_context_new ("- mu guile options");
g_option_context_add_main_entries (octx, entries, "mu guile");
if (!g_option_context_parse (octx, argcp, argvp, NULL)) {
g_option_context_free (octx);
g_printerr ("mu guile: error in options\n");
return NULL;
}
if (opts->msgpath)
opts->msgpath = mu_util_dir_expand (opts->msgpath);
g_option_context_free (octx);
return opts;
}
static void
guile_config_destroy (GuileConfig *conf)
{
g_free (conf->msgpath);
g_free (conf);
}
gboolean
mu_guile_util_run (int *argcp, char **argvp[])
{
GuileConfig *opts;
#ifdef HAVE_PRE2_GUILE
g_warning ("Note: mu guile will not function properly unless you are using a"
"UTF-8 locale.");
#endif /* HAVE_PRE2_GUILE */
opts = guile_config_new (argcp, argvp);
if (!opts)
goto error;
if (!mu_runtime_init (opts->muhome /* NULL is okay */,
"mu-guile"))
goto error;
/* FIXME: mu_guile_init (); /\* initialize mu guile modules *\/ */
if (opts->msgpath) {
if (!(gboolean)scm_with_guile
((MuGuileFunc*)&mu_guile_msg_load_current,
opts->msgpath))
goto error;
}
scm_shell (*argcp, *argvp);
mu_runtime_uninit ();
guile_config_destroy (opts);
return TRUE;
error:
guile_config_destroy (opts);
return FALSE;
}
/*
* backward compat for pre-2.x guile - note, this will fail miserably
* if you don't use a UTF8 locale
*/
#if HAVE_PRE2_GUILE
SCM
scm_from_utf8_string (const char* str)
{
return scm_from_locale_string (str);
}
char*
scm_to_utf8_string (SCM scm)
{
return scm_to_locale_string (scm);
}
#endif /*HAVE_PRE2_GUILE*/

73
guile/mu-guile-util.h Normal file
View File

@ -0,0 +1,73 @@
/*
** 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_UTIL_H__
#define __MU_GUILE_UTIL_H__
#include <libguile.h>
#if HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <glib.h>
G_BEGIN_DECLS
/**
* start a guile shell with the mu modules loaded. function does not return
*
* @param argcp pointer to argc
* @param argvp pointer to argv
*
* @return FALSE in case of error, otherwise, the function will not return
*/
gboolean mu_guile_util_run (int *argcp, char **argvp[]);
/**
* output an error
*
* @param func_name
* @param status
* @param fmt
* @param args
*/
SCM mu_guile_util_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
*/
SCM mu_guile_util_g_error (const char *func_name, GError *err);
/* compatibility functions for guile 1.8 */
#if HAVE_PRE2_GUILE
SCM scm_from_utf8_string (const char* str);
char* scm_to_utf8_string (SCM scm);
#endif /*HAVE_PRE2_GUILE*/
G_END_DECLS
#endif /*__MU_GUILE_UTIL_H__*/

83
guile/mu-guile.c Normal file
View File

@ -0,0 +1,83 @@
/*
** 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.
**
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <mu-runtime.h>
#include "mu-guile-util.h"
static gboolean initialized = FALSE;
SCM_DEFINE_PUBLIC (init_mu, "mu:init", 0, 1, 0,
(SCM MUHOME),
"Initialize mu - needed before you call any of the other "
"functions. Optionally, you can provide MUHOME which "
"should be an absolute path to your mu home directory "
"(typically, the default, ~/.mu, should be just fine).\n")
#define FUNC_NAME s_init_mu
{
const char *muhome;
static gboolean initialized = FALSE;
SCM_ASSERT (scm_is_string (MUHOME) || SCM_UNBNDP(MUHOME),
MUHOME, SCM_ARG1, FUNC_NAME);
if (initialized)
return mu_guile_util_error (FUNC_NAME, 0, "Already initialized",
SCM_UNSPECIFIED);
muhome = SCM_UNBNDP(MUHOME) ? NULL : scm_to_utf8_string (MUHOME);
if (!mu_runtime_init (muhome, "mu-guile"))
return mu_guile_util_error (FUNC_NAME, 0, "Failed to initialize mu",
SCM_UNSPECIFIED);
initialized = TRUE;
return SCM_UNSPECIFIED;
}
#undef FUNC_NAME
SCM_DEFINE_PUBLIC (init_p, "mu:init?", 0, 0, 0,
(void), "Whether mu is initialized or not.\n")
#define FUNC_NAME s_init_p
{
return initialized ? SCM_BOOL_T : SCM_BOOL_F;
}
#undef FUNC_NAME
/* C function so we can cheaply check from other C-based code */
gboolean
mu_guile_initialized (void)
{
return initialized;
}
void*
mu_guile_init (void *data)
{
#include "mu-guile.x"
return NULL;
}

48
guile/mu-guile.h Normal file
View File

@ -0,0 +1,48 @@
/*
** 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_H__
#define __MU_GUILE_H__
#include <glib.h>
G_BEGIN_DECLS
/**
* Whether or not mu/guile has been initialized
*
*
* @return TRUE if it has been initialized, FALSE otherwise
*/
gboolean mu_guile_initialized (void);
/**
* Initialize this mu guile module.
*
* @param data
*
* @return
*/
void* mu_guile_init (void *data);
G_END_DECLS
#endif /*__MU_GUILE_H__*/

25
guile/mu.scm Normal file
View File

@ -0,0 +1,25 @@
;;
;; 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.
(define-module (mu)
:use-module (mu log)
:use-module (mu msg)
:use-module (mu store))
;; mu_guile_init will actually initialize the msg/store/log as well
(load-extension "libguile-mu" "mu_guile_init")

28
guile/mu/Makefile.am Normal file
View File

@ -0,0 +1,28 @@
## Copyright (C) 2008-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 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
moduledir=$(GUILE_SITEDIR)/mu
module_DATA= \
msg.scm \
log.scm \
store.scm \
stats.scm
EXTRA_DIST= \
README

205
guile/mu/README Normal file
View File

@ -0,0 +1,205 @@
* README
** What is muile?
`muile' is a little experiment/toy using the equally experimental 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. 'muile' requires guile 2.x to get the full
support.
Older versions may not support e.g. the 'mu-stats.scm' things discussed below.
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 only did
very minimal testing with guile 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 = (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)
`----
in your ~/.guile configuration.
** does this tool have some parameters?
Yes, there is --muhome to set a non-default place for the message database
(see the documentation on --muhome in the mu-find manpage).
And there is --msg=<path> where you specify some particular message file;
it will be available as 'mu:current-msg' in the guile (muile) environment. For
example:
,----
| ./muile --msg=~/Maildir/inbox/cur/1311310172_1234:2,S
| [...]
| scheme@(guile-user)> mu:current-msg
| $1 = #<msg /home/djcb/Maildir/inbox/cur/1311310172_1234:2,S>
| scheme@(guile-user)> (mu:msg:size mu:current-msg)
| $2 = 7206
`----
** What about searching messages in the database?
That's easy, too - it does require a little more scheme knowledge. For
searching messages there is the mu:store:for-each function, which takes two
arguments; the first argument is a function that will be called for each
message found. The optional second argument is the search expression (following
'mu find' syntax); if don't provide the argument, all messages match.
So how does this work in practice? Let's see I want to see the subject and
sender for messages about milk:
,----
| (mu:store:for-each (lambda(msg) (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg))) "milk")
`----
or slightly more readable:
,----
| (mu:store:for-each
| (lambda(msg)
| (format #t "~s ~s\n" (mu:msg:from msg) (mu:msg:subject msg)))
| "milk")
`----
As you can see, I provide an anonymous ('lambda') function which will be
called for each message matching 'milk'. Admittedly, this requires a bit of
Scheme-knowledge... but this time is good as any to learn this nice
language.
** Can I do some statistics on my messages?
Yes you can. In fact, it's pretty easy. If you load (in the muile/ directory)
the file 'mu-stats.scm':
,----
| (load "mu-stats.scm")
`----
you'll get a bunch of functions (with names starting with 'mu:stats') to make
this very easy. Let's see, suppose I want to see how many messages I get per
weekday:
,----
| scheme@(guile-user)> (mu:stats:per-weekday)
| $1 = ((0 . 2255) (1 . 2788) (2 . 2868) (3 . 2599) (4 . 2629) (5 . 2287) (6 . 1851))
`----
Note, Sunday=0, Monday=1 and so on. Apparently, I get/send most of e-mail on
Tuesdays, and least on Saturday.
And note that mu:stats:per-weekdays takes an optional search expression
argument, to limit the results to messages matching that, e.g., to only
consider messages related to emacs during this year:
,----
| scheme@(guile-user)> (mu:stats:per-weekday "emacs date:2011..now")
| $8 = ((0 . 54) (1 . 22) (2 . 46) (3 . 47) (4 . 39) (5 . 54) (6 . 50))
`----
There's also 'mu:stats:per-month', 'mu:stats:per-year', 'mu:stats:per-hour'.
I learnt that during 3-4am I sent/receive only about a third of what I sent
during 11-12pm.
** What about getting the top-10 people in the To:-field?
Easy.
,----
| scheme@(guile-user)> (mu:stats:top-n-to)
| $1 = ((("Abc" "myself@example.com") . 4465) (("Def" "somebodyelse@example.com") . 2114)
| (and so on)
`----
I've changed the names a bit to protect the innocent, but what the function
does is return a list of pairs of
(<name> <email>) . <frequency>
descending in order of frequency. Note, 'mu:stats:top-n-to' takes two
optional arguments - first the 'n' in top-n (default is 10), and seconds as
search expression to limit the messages considered.
There are also the functions 'mu:stats:top-n-subject' and
'mu:stats:top-n-from' which do the same, mutatis mutandis, and it's quite
easy to add your own (see the mu-stats.scm for examples)
** What about showing the results in a table?
Even easier. Try:
,----
| (mu:stats:table (mu:stats:top-n-to))
`----
or
,----
| (mu:stats:table (mu:stats:per-weekday))
`----
You can also export the table:
,----
| (mu:stats:export (mu:stats:per-weekday))
`----
which will create a temporary file with the results, for further processing
in e.g. 'R' or 'gnuplot'.
[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:

20
guile/mu/log.scm Normal file
View File

@ -0,0 +1,20 @@
;;
;; 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.
(define-module (mu log))
(load-extension "libguile-mu" "mu_guile_log_init")

22
guile/mu/msg.scm Normal file
View File

@ -0,0 +1,22 @@
;;
;; 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.
(define-module (mu msg)
:use-module (mu log))
(load-extension "libguile-mu" "mu_guile_msg_init")

241
guile/mu/stats.scm Normal file
View File

@ -0,0 +1,241 @@
;;
;; 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.
(use-modules (ice-9 optargs) (ice-9 popen))
(define-module (mu stats)
:use-module (mu log)
:use-module (mu store)
:use-module (mu msg)
:export
(
mu:stats:count
mu:stats:average
mu:stats:average-size
mu:stats:average-recipient-number
mu:stats:frequency
mu:stats:per-weekday
mu:plot:per-weekday
mu:stats:per-month
mu:plot:per-month
mu:stats:per-hour
mu:stats:per-year
mu:stats:top-n
mu:stats:top-n-to
mu:stats:top-n-from
mu:stats:top-n-subject
mu:stats:table
mu:stats:histogram
mu:stats:export))
;; 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:for-each (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:for-each
(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 mMsg, 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:for-each
(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:plot:per-weekday #:optional (EXPR ""))
(let* ((datafile (mu:stats:export (mu:stats:per-weekday EXPR)))
(gnuplot (open-pipe "gnuplot -p" OPEN_WRITE)))
;; note, we cannot use the weekday "%a" support in gnuplot because
;; demands the field to be a date field ('set xdata time' etc.)
;; for that to work, but we cannot use that since gnuplot does not
;; support weekdays ('%w') as a date field in its input
(display (string-append
"reset\n"
"set xtics (\"Sun\" 0, \"Mon\" 1, \"Tue\" 2, \"Wed\" 3,"
"\"Thu\" 4, \"Fri\" 5, \"Sat\" 6);\n"
"set xlabel \"Weekday\"\n"
"set ylabel \"# of messages\"\n"
"set boxwidth 0.9\n") gnuplot)
(display (string-append "plot \"" datafile "\" using 1:2 with boxes fs solid\n")
gnuplot)
(close-pipe gnuplot)))
(define* (mu:stats:per-month #:optional (EXPR ""))
"Count the total number of messages for each month (1-12 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) ;; note the 1+
(1+ (tm:mon (localtime (mu:msg:date msg))))) EXPR)))
(sort stats
(lambda(a b)
(< (car a) (car b)))))) ;; in order ofmonth
;; (define* (mu:plot:per-month #:optional (EXPR ""))
;; (let* ((data
;; (map ;; add 1 to the month numbers, since gnuplot counts
;; ;; months from 1, not 0
;; (lambda (cell)
;; (cons (1+ (car cell)) (cdr cell)))
;; (mu:stats:per-month EXPR)))
;; (datafile (mu:stats:export data))
;; (gnuplot (open-pipe "gnuplot -p" OPEN_WRITE)))
;; ;; note, we cannot use the weekday "%a" support in gnuplot because
;; ;; demands the field to be a date field ('set xdata time' etc.)
;; ;; for that to work, but we cannot use that since gnuplot does not
;; ;; support weekdays ('%w') as a date field in its input
;; (display (string-append
;; "reset\n"
;; "set xtics (\"Sun\" 0, \"Mon\" 1, \"Tue\" 2, \"Wed\" 3,"
;; "\"Thu\" 4, \"Fri\" 5, \"Sat\" 6);\n"
;; "set xlabel \"Weekday\"\n"
;; "set ylabel \"# of messages\"\n"
;; "set boxwidth 0.9\n") gnuplot)
;; (display (string-append "plot \"" datafile "\" using 1:2 with boxes fs solid\n")
;; gnuplot)
;; (close-pipe gnuplot)))
(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 #:optional (port (current-output-port)))
"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) port)))
pairs)))
;; (define* (mu:stats:histogram pairs #:optional (port (current-output-port)))
;; "Display a histogram of the list of cons pairs; the car of each pair
;; is used for the x-asxis, while the cdr represents the y value."
;; (let ((pairs ;; pairs may be unsorted, so let's sort first
;; (sort (pairs) (lambda(x1 x2) (< x1 x2)))))
(define (mu:stats:export pairs)
"Export PAIRS to a temporary file, return its name. The data can
then be used in, e.g., R and gnuplot."
(let* ((datafile (tmpnam))
(output (open datafile (logior O_CREAT O_WRONLY) #O0644)))
(mu:stats:table pairs output)
(close output)
datafile))

25
guile/mu/store.scm Normal file
View File

@ -0,0 +1,25 @@
;;
;; 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.
(use-modules (mu log))
(use-modules (mu msg))
(define-module (mu store)
:use-module (mu log)
:use-module (mu msg))
(load-extension "libguile-mu" "mu_guile_store_init")