diff --git a/lib/Makefile.am b/lib/Makefile.am index 888029c6..046e28d0 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -26,7 +26,7 @@ else crypto= endif -INCLUDES=$(XAPIAN_CXXFLAGS) $(GMIME_CFLAGS) $(GLIB_CFLAGS) +INCLUDES=$(XAPIAN_CXXFLAGS) $(GMIME_CFLAGS) $(GLIB_CFLAGS) $(GUILE_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not @@ -76,6 +76,8 @@ libmu_la_SOURCES= \ mu-query.h \ mu-runtime.c \ mu-runtime.h \ + mu-script.c \ + mu-script.h \ mu-store.cc \ mu-store.h \ mu-store-read.cc \ diff --git a/lib/mu-script.c b/lib/mu-script.c new file mode 100644 index 00000000..d22bf256 --- /dev/null +++ b/lib/mu-script.c @@ -0,0 +1,219 @@ +/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ + +/* +** Copyright (C) 2012 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif /*HAVE_CONFIG_H*/ + +#ifdef BUILD_GUILE +#include +#endif /*BUILD_GUILE*/ + +#include +#include +#include +#include +#include +#include + +#include "mu-script.h" +#include "mu-util.h" + +/** + * Structure with information about a certain script. + * the values will be *freed* when MuScriptInfo is freed + */ +struct _MuScriptInfo { + char *_name; /* filename-sans-extension */ + char *_path; /* full path to script */ + char *_descr; /* one-line description */ +}; + + +/* create a new MuScriptInfo* object*/ +static MuScriptInfo* +script_info_new (void) +{ + return g_slice_new0 (MuScriptInfo); +} + +/* destroy a MuScriptInfo* object */ +static void +script_info_destroy (MuScriptInfo *msi) +{ + if (!msi) + return; + + g_slice_free (MuScriptInfo, msi); +} + +/* compare two MuScripInfo* objects (for sorting) */ +static int +script_info_cmp (MuScriptInfo *msi1, MuScriptInfo *msi2) +{ + return strcmp (msi1->_name, msi2->_name); + +} + +const char* +mu_script_info_name (MuScriptInfo *msi) +{ + g_return_val_if_fail (msi, NULL); + return msi->_name; +} + +const char* +mu_script_info_path (MuScriptInfo *msi) +{ + g_return_val_if_fail (msi, NULL); + return msi->_path; +} + +const char* +mu_script_info_description (MuScriptInfo *msi) +{ + g_return_val_if_fail (msi, NULL); + return msi->_descr; +} + +void +mu_script_info_list_destroy (GSList *lst) +{ + g_slist_foreach (lst, (GFunc)script_info_destroy, NULL); + g_slist_free (lst); +} + + +GSList* +mu_script_get_script_info_list (const char *path, const char *ext, + GError **err) +{ + DIR *dir; + GSList *lst; + struct dirent *dentry; + + g_return_val_if_fail (path, NULL); + + dir = opendir (path); + if (!dir) { + mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_OPEN, + "failed to open '%s': %s", + path, strerror(errno)); + return NULL; + } + + /* create a list of names, paths */ + lst = NULL; + while ((dentry = readdir (dir))) { + MuScriptInfo *msi; + + /* only consider files with certain extensions, + * if ext != NULL */ + if (ext && !g_str_has_suffix (dentry->d_name, ext)) + continue; + msi = script_info_new (); + msi->_name = g_strdup (dentry->d_name); + if (ext) /* strip the extension */ + msi->_name[strlen(msi->_name) - strlen(ext)] = '\0'; + + msi->_path = g_strdup_printf ("%s%c%s", path, G_DIR_SEPARATOR, + dentry->d_name); + lst = g_slist_prepend (lst, msi); + } + + closedir (dir); /* ignore error checking... */ + + return g_slist_sort (lst, (GCompareFunc)script_info_cmp); +} + + +MuScriptInfo* +mu_script_find_script_with_name (GSList *lst, const char *name) +{ + GSList *cur; + + g_return_val_if_fail (name, NULL); + + for (cur = lst; cur; cur = g_slist_next (cur)) { + + MuScriptInfo *msi; + msi = (MuScriptInfo*)cur->data; + + if (g_strcmp0 (name, mu_script_info_name (msi)) == 0) + return msi; + } + + return NULL; +} + +#ifdef BUILD_GUILE + +static void +guile_shell (void *closure, int argc, char **argv) +{ + scm_shell (argc, argv); +} + + +gboolean +mu_script_guile_run (MuScriptInfo *msi, const char *muhome, + const char *query, gboolean textonly, GError **err) +{ + char *expr; + char *argv[] = { + "guile", "-l", NULL, "-c", NULL, NULL + }; + + g_return_val_if_fail (msi, FALSE); + g_return_val_if_fail (muhome, FALSE); + + if (access (mu_script_info_path (msi), R_OK) != 0) { + mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_READ, + strerror(errno)); + return FALSE; + } + argv[2] = (char*)mu_script_info_path (msi); + + expr = g_strdup_printf ( + "(main '(\"%s\" \"--muhome=%s\" %s %s))", + mu_script_info_name (msi), + muhome, + textonly ? "\"--textonly\"" : "", + query ? query : ""); + argv[4] = expr; + + scm_boot_guile (5, argv, guile_shell, NULL); + + /* never reached but let's be correct(TM)*/ + g_free (expr); + return TRUE; +} + +#else +gboolean +mu_script_guile_run (MuScriptInfo *msi, const char *muhome, + const char *query, gboolean textonly, GError **err) +{ + mu_util_g_set_error (err, MU_ERROR_INTERNAL, + "this mu does not have guile support"); + return FALSE; +} +#endif /*!BUILD_GUILE*/ diff --git a/lib/mu-script.h b/lib/mu-script.h new file mode 100644 index 00000000..9cd85a10 --- /dev/null +++ b/lib/mu-script.h @@ -0,0 +1,111 @@ +/* +** Copyright (C) 2012 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef __MU_SCRIPT_H__ +#define __MU_SCRIPT_H__ + +#include + +G_BEGIN_DECLS + +/* Opaque structure with information about a script */ +struct _MuScriptInfo; +typedef struct _MuScriptInfo MuScriptInfo; + +/** + * get the name of the script (sans-extension, if some extension was + * provided to mu_script_get_scripts) + * + * @param msi a MuScriptInfo structure + * + * @return the name + */ +const char* mu_script_info_name (MuScriptInfo *msi); + + +/** + * get the full filesystem path of the script + * + * @param msi a MuScriptInfo structure + * + * @return the path + */ +const char* mu_script_info_path (MuScriptInfo *msi); + +/** + * get a one-line description for the script + * + * @param msi a MuScriptInfo structure + * + * @return the description, or NULL if there was none + */ +const char* mu_script_info_description (MuScriptInfo *msi); + + +/** + * Get the list of all scripts in path with extension ext + * + * @param path a file system path + * @param ext an extension (e.g., ".scm"), or NULL + * @param err receives error information, if any + * + * @return a list of Mu + */ +GSList *mu_script_get_script_info_list (const char *path, const char *ext, + GError **err); + +/** + * destroy a list of MuScriptInfo* objects + * + * @param scriptslst a list of MuScriptInfo* objects + */ +void mu_script_info_list_destroy (GSList *lst); + + +/** + * find the MuScriptInfo object for the first script with a certain + * name, or return NULL if not found. + * + * @param lst a list of MuScriptInfo* objects + * @param name the name to search for + * + * @return a MuScriptInfo* object, or NULL if not found. + */ +MuScriptInfo* mu_script_find_script_with_name (GSList *lst, const char *name); + + +/** + * run the guile script at path + * + * @param msi MuScriptInfo object for the script + * @param muhome mu home directory + * @param query query for this script (or NULL) + * @param textonly whether to not use a graphical display + * @param err receives error information + * + * @return FALSE in case of error -- otherwise, this function will + * _not return_ + */ +gboolean mu_script_guile_run (MuScriptInfo *msi, const char *muhome, + const char* query, gboolean textonly, + GError **err); + +G_END_DECLS + +#endif /*__MU_SCRIPT_H__*/ diff --git a/mu/Makefile.am b/mu/Makefile.am index cfb65de9..75e191b9 100644 --- a/mu/Makefile.am +++ b/mu/Makefile.am @@ -20,7 +20,7 @@ include $(top_srcdir)/gtest.mk # before decending into tests/ SUBDIRS= . tests -INCLUDES=-I${top_srcdir}/lib $(GLIB_CFLAGS) $(GUILE_CFLAGS) +INCLUDES=-I${top_srcdir}/lib $(GLIB_CFLAGS) # don't use -Werror, as it might break on other compilers # use -Wno-unused-parameters, because some callbacks may not @@ -42,9 +42,9 @@ mu_SOURCES= \ mu-config.h \ mu-cmd-extract.c \ mu-cmd-find.c \ - mu-cmd-guile.c \ mu-cmd-index.c \ mu-cmd-server.c \ + mu-cmd-script.c \ mu-cmd.c \ mu-cmd.h diff --git a/mu/mu-cmd-guile.c b/mu/mu-cmd-guile.c deleted file mode 100644 index 3421aaeb..00000000 --- a/mu/mu-cmd-guile.c +++ /dev/null @@ -1,259 +0,0 @@ -/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ - -/* -** Copyright (C) 2012 Dirk-Jan C. Binnema -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 3, or (at your option) any -** later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software Foundation, -** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -*/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif /*HAVE_CONFIG_H*/ - -#ifdef BUILD_GUILE -#include -#endif /*BUILD_GUILE*/ - -#include -#include -#include -#include -#include - -#include "mu-cmd.h" -#include "mu-util.h" -#include "mu-str.h" - -struct _NamePath { - char *name; /* name of the script (ie., basename with ext)*/ - char *path; /* full path of script */ -}; -typedef struct _NamePath NamePath; - -static NamePath* -namepath_new (char *name, const char *path) -{ - NamePath *np; - - np = g_new0 (NamePath, 1); - - np->name = g_strdup (name); - np->path = g_strdup (path); - - return np; -} - -static void -namepath_destroy (NamePath *np) -{ - if (!np) - return; - - g_free (np->name); - g_free (np->path); - g_free (np); -} - -static int -namepath_cmp (NamePath *np1, NamePath *np2) -{ - return strcmp (np1->name, np2->name); -} - -static void -script_namepaths_destroy (GSList *namepaths) -{ - g_slist_foreach (namepaths, (GFunc)namepath_destroy, NULL); - g_slist_free (namepaths); -} - - -static GSList* -get_script_namepaths (const char *path, GError **err) -{ - DIR *dir; - GSList *lst; - struct dirent *dentry; - - dir = opendir (path); - if (!dir) { - mu_util_g_set_error (err, MU_ERROR_FILE_CANNOT_OPEN, - "failed to open '%s': %s", - path, strerror(errno)); - return NULL; - } - - /* create a list of names, paths */ - lst = NULL; - while ((dentry = readdir (dir))) { - const char* scmext = ".scm"; - char *name, *fullpath; - - /* only consider scm files */ - if (!g_str_has_suffix (dentry->d_name, scmext)) - continue; - - name = g_strndup (dentry->d_name, - strlen(dentry->d_name) - strlen(scmext)); - fullpath = g_strdup_printf ("%s%c%s", path, G_DIR_SEPARATOR, - dentry->d_name); - - lst = g_slist_prepend (lst, namepath_new (name, fullpath)); - - g_free (name); - g_free (fullpath); - } - - closedir (dir); - - return g_slist_sort (lst, (GCompareFunc)namepath_cmp); -} - - -static char* -find_script_path (const char *script, GError **err) -{ - GSList *scripts, *cur; - char *path; - - scripts = get_script_namepaths (MU_STATSDIR, err); - if (err && *err) - return NULL; - - for (cur = scripts, path = NULL; cur ; cur = g_slist_next (cur)) - if (g_strcmp0(((NamePath*)cur->data)->name, script) == 0) { - path = g_strdup (((NamePath*)cur->data)->path); - break; - } - - script_namepaths_destroy (scripts); - - if (!path) - mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, - "statistic '%s' not found", - script); - return path; -} - -#ifdef BUILD_GUILE - -static void -do_it (void *closure, int argc, char **argv) -{ - scm_shell (argc, argv); -} - - -static void -run_guile_script (MuConfig *opts, GError **err) -{ - char *expr, *query, *scriptpath; - char *argv[] = { - "guile", "-l", NULL, "-c", NULL, NULL - }; - - scriptpath = find_script_path (opts->stat, err); - if (!scriptpath) - return; - else - argv[2] = scriptpath; - - if (opts->params[1]) - query = mu_str_quoted_from_strv - ((const gchar**)&opts->params[1]); - else - query = NULL; - - expr = g_strdup_printf ( - "(main '(\"dummy\" \"--muhome=%s\" %s %s))", - opts->muhome, - opts->textonly ? "\"--textonly\"" : "", - query ? query : ""); - - g_free (query); - argv[4] = expr; - scm_boot_guile (5, argv, do_it, 0); - - /* never reached but let's be correct(TM)*/ - g_free (expr); - g_free (scriptpath); -} - -#else -static void -run_guile_script (MuConfig *opts, GError **err) -{ - g_return_if_reached (); -} -#endif /*!BUILD_GUILE*/ - - -static MuError -list_stats (GError **err) -{ - GSList *scripts; - - scripts = get_script_namepaths (MU_STATSDIR, err); - if (err && *err) - return MU_ERROR; - - if (!scripts) - g_print ("No statistics available\n"); - else { - GSList *cur; - g_print ("Available statistics " - "(use with --stat=data)->name); - } - - script_namepaths_destroy (scripts); - - return MU_OK; - -} - - -static gboolean -check_params (MuConfig *opts, GError **err) -{ - if (!mu_util_supports (MU_FEATURE_GUILE | MU_FEATURE_GNUPLOT)) { - mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, - "the 'stats' command is not supported"); - return FALSE; - } - - return TRUE; -} - - -MuError -mu_cmd_stats (MuConfig *opts, GError **err) -{ - g_return_val_if_fail (opts, MU_ERROR_INTERNAL); - g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_STATS, - MU_ERROR_INTERNAL); - - if (!check_params (opts, err)) - return MU_ERROR; - - if (!opts->stat) - return list_stats (err); - - run_guile_script (opts, err); - - return MU_OK; -} diff --git a/mu/mu-cmd-script.c b/mu/mu-cmd-script.c new file mode 100644 index 00000000..53133d83 --- /dev/null +++ b/mu/mu-cmd-script.c @@ -0,0 +1,123 @@ +/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ + +/* +** Copyright (C) 2012 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif /*HAVE_CONFIG_H*/ + +#include +#include +#include +#include +#include + +#include "mu-cmd.h" +#include "mu-util.h" +#include "mu-str.h" +#include "mu-script.h" + +static MuError +list_stats (GError **err) +{ + GSList *scripts; + + scripts = mu_script_get_script_info_list (MU_STATSDIR, ".scm", err); + if (err && *err) + return MU_ERROR; + + if (!scripts) + g_print ("No statistics available\n"); + else { + GSList *cur; + g_print ("Available statistics " + "(use with --stat=):\n"); + for (cur = scripts; cur; cur = g_slist_next (cur)) + g_print ("\t%s\n", + mu_script_info_name + ((MuScriptInfo*)cur->data)); + } + + mu_script_info_list_destroy (scripts); + + return MU_OK; + +} + + +static gboolean +check_params (MuConfig *opts, GError **err) +{ + if (!mu_util_supports (MU_FEATURE_GUILE | MU_FEATURE_GNUPLOT)) { + mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, + "the 'stats' command is not supported"); + return FALSE; + } + + return TRUE; +} + + +MuError +mu_cmd_stats (MuConfig *opts, GError **err) +{ + MuScriptInfo *msi; + GSList *scripts; + gchar *query; + + g_return_val_if_fail (opts, MU_ERROR_INTERNAL); + g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_STATS, + MU_ERROR_INTERNAL); + + if (!check_params (opts, err)) + return MU_ERROR; + + if (!opts->stat) + return list_stats (err); + + scripts = mu_script_get_script_info_list (MU_STATSDIR, ".scm", + err); + if (err && *err) + return MU_ERROR; + + msi = mu_script_find_script_with_name (scripts, opts->stat); + if (!msi) { + mu_script_info_list_destroy (scripts); + mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, + "script not found"); + return MU_ERROR_IN_PARAMETERS; + } + + if (opts->params[1]) + query = mu_str_quoted_from_strv + ((const gchar**)&opts->params[1]); + else + query = NULL; + + /* do it! */ + mu_script_guile_run (msi, opts->muhome, query ? query : "", + opts->textonly, err); + + /* this won't be reached, unless there is some error */ + mu_script_info_list_destroy (scripts); + g_free (query); + + return MU_ERROR; +}