/* ** Copyright (C) 2011 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. ** */ #include #include #include #include #include "mu-runtime.h" #include "mu-util.h" struct _ChildData { char **shell_argv; int shell_argc; }; typedef struct _ChildData ChildData; static ChildData * child_data_new (const char *muhome) { ChildData *data; data = g_new0 (ChildData, 1); data->shell_argv = g_new0 (char*,3); data->shell_argc = 0; data->shell_argv[data->shell_argc++] = g_strdup ("procmule"); data->shell_argv[data->shell_argc++] = g_strdup ("-s"); data->shell_argv[data->shell_argc++] = g_strdup_printf ("%s%cprocmule.scm", muhome, G_DIR_SEPARATOR); return data; } static void child_data_destroy (ChildData *data) { if (!data) return; g_strfreev (data->shell_argv); g_free (data); } static void on_dir_change (GFileMonitor *mon, GFile *file, GFile *other_file, GFileMonitorEvent event_type, ChildData *data) { gchar *path; path = g_file_get_path (file); /* ignore all except create events */ if (event_type != G_FILE_MONITOR_EVENT_CREATED) return; if (fork() == 0) { /* run guile in child */ mu_guile_init (); /* initialize mu guile modules */ if (!(gboolean)scm_with_guile ((MuGuileFunc*)&mu_guile_msg_load_current, path)) { g_warning ("failed to set message in guile env"); return; } scm_shell (data->shell_argc, data->shell_argv); /* never returns */ } g_free (path); } static GFileMonitor* create_monitor (const char *path, ChildData *data) { GFile *dir; GFileMonitor *dirmon; GError *err; if (!mu_util_check_dir (path, TRUE, FALSE)) { g_warning ("must be a readable dir: '%s'", path); return NULL; } dir = g_file_new_for_path (path); err = NULL; dirmon = g_file_monitor_directory (dir, G_FILE_MONITOR_NONE, NULL, &err); if (!dirmon) { g_warning ("error adding monitor: %s", err->message); g_error_free (err); } g_object_unref (dir); if (dirmon) g_signal_connect (dirmon, "changed", G_CALLBACK(on_dir_change), data); return dirmon; } static void destroy_watchlist (GSList *lst) { g_slist_foreach (lst, (GFunc)g_object_unref, NULL); g_slist_free (lst); } GSList* create_watchlist (char **dirs, ChildData *data) { GSList *watchlist; char **cur; /* TODO: check for dups */ for (watchlist = NULL, cur = dirs; cur && *cur; ++cur) { GFileMonitor *dirmon; dirmon = create_monitor (*cur, data); if (!dirmon) { destroy_watchlist (watchlist); return NULL; } watchlist = g_slist_prepend (watchlist, dirmon); } return watchlist; } struct _PMConfig { char *muhome; char **watchdirs; }; typedef struct _PMConfig PMConfig; static void expand_paths (PMConfig *opts) { char **cur; for (cur = opts->watchdirs; cur && *cur; ++cur) *cur = mu_util_dir_expand (*cur); if (opts->muhome) opts->muhome = mu_util_dir_expand (opts->muhome); } static PMConfig * pm_config_new (int *argcp, char ***argvp) { GOptionContext *octx; PMConfig *opts = g_new0 (PMConfig, 1); GOptionEntry entries[] = { {"muhome", 0, 0, G_OPTION_ARG_FILENAME, &opts->muhome, "specify an alternative mu directory", NULL}, {"watch", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opts->watchdirs, "directory to watch (may be specified multiple times)", NULL}, {NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}/* sentinel */ }; octx = g_option_context_new ("- procmule options"); g_option_context_add_main_entries (octx, entries, "Procmule"); if (!g_option_context_parse (octx, argcp, argvp, NULL)) { g_printerr ("error in options\n"); goto error; } if (!opts->watchdirs) { g_printerr ("specify at least one --watch=\n"); goto error; } expand_paths (opts); g_option_context_free (octx); return opts; error: if (octx) g_option_context_free (octx); g_free (opts); return NULL; } static void pm_config_destroy (PMConfig *conf) { if (!conf) return; g_free (conf->muhome); g_strfreev (conf->watchdirs); g_free (conf); } static void usage (void) { g_print ("usage: procmule [--muhome=] [--watch=]\n"); g_print ("also, see toys/procmule/README\n"); } static gboolean watch_dirs (char **watchdirs) { ChildData *child_data; GSList *watchlist; GMainLoop *loop; child_data = child_data_new (mu_runtime_path(MU_RUNTIME_PATH_MUHOME)); watchlist = create_watchlist (watchdirs, child_data); if (!watchlist) goto error; loop = g_main_loop_new (NULL, TRUE); g_main_loop_run (loop); g_main_loop_unref (loop); destroy_watchlist (watchlist); return TRUE; error: child_data_destroy (child_data); return FALSE; } int main (int argc, char *argv[]) { PMConfig *opts; g_type_init (); g_thread_init (NULL); #ifdef HAVE_PRE2_GUILE g_warning ("Note: pre-2.x version of guile: procmule will not function " "correctly unless you're using UTF-8 locale."); #endif /* HAVE_PRE2_GUILE */ opts = pm_config_new (&argc, &argv); if (!opts) { usage (); goto error; } if (!mu_runtime_init (opts->muhome, "procmule")) { usage (); goto error; } watch_dirs (opts->watchdirs); /* do it! */ mu_runtime_uninit (); pm_config_destroy (opts); return 0; error: pm_config_destroy (opts); return 1; }