mirror of
https://github.com/djcb/mu.git
synced 2024-06-30 08:01:07 +02:00
296 lines
5.9 KiB
C
296 lines
5.9 KiB
C
/*
|
|
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify it
|
|
** under the terms of the GNU General Public License as published by the
|
|
** Free Software Foundation; either version 3, or (at your option) any
|
|
** later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program; if not, write to the Free Software Foundation,
|
|
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
**
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <gio/gio.h>
|
|
#include <libmuguile/mu-guile-common.h>
|
|
#include <libmuguile/mu-guile-msg.h>
|
|
|
|
#include "mu-runtime.h"
|
|
#include "mu-util.h"
|
|
|
|
struct _ChildData {
|
|
char **shell_argv;
|
|
int shell_argc;
|
|
};
|
|
typedef struct _ChildData ChildData;
|
|
|
|
static ChildData *
|
|
child_data_new (const char *muhome)
|
|
{
|
|
ChildData *data;
|
|
|
|
data = g_new0 (ChildData, 1);
|
|
|
|
data->shell_argv = g_new0 (char*,3);
|
|
data->shell_argc = 0;
|
|
|
|
data->shell_argv[data->shell_argc++] = g_strdup ("procmule");
|
|
data->shell_argv[data->shell_argc++] = g_strdup ("-s");
|
|
data->shell_argv[data->shell_argc++] =
|
|
g_strdup_printf ("%s%cprocmule.scm", muhome, G_DIR_SEPARATOR);
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
child_data_destroy (ChildData *data)
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
g_strfreev (data->shell_argv);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
on_dir_change (GFileMonitor *mon, GFile *file, GFile *other_file,
|
|
GFileMonitorEvent event_type, ChildData *data)
|
|
{
|
|
gchar *path;
|
|
|
|
path = g_file_get_path (file);
|
|
|
|
/* ignore all except create events */
|
|
if (event_type != G_FILE_MONITOR_EVENT_CREATED)
|
|
return;
|
|
|
|
if (fork() == 0) { /* run guile in child */
|
|
|
|
mu_guile_init (); /* initialize mu guile modules */
|
|
|
|
if (!(gboolean)scm_with_guile
|
|
((MuGuileFunc*)&mu_guile_msg_load_current, path)) {
|
|
g_warning ("failed to set message in guile env");
|
|
return;
|
|
}
|
|
scm_shell (data->shell_argc, data->shell_argv); /* never returns */
|
|
}
|
|
|
|
g_free (path);
|
|
}
|
|
|
|
static GFileMonitor*
|
|
create_monitor (const char *path, ChildData *data)
|
|
{
|
|
GFile *dir;
|
|
GFileMonitor *dirmon;
|
|
GError *err;
|
|
|
|
if (!mu_util_check_dir (path, TRUE, FALSE)) {
|
|
g_warning ("must be a readable dir: '%s'", path);
|
|
return NULL;
|
|
}
|
|
|
|
dir = g_file_new_for_path (path);
|
|
|
|
err = NULL;
|
|
dirmon = g_file_monitor_directory (dir, G_FILE_MONITOR_NONE,
|
|
NULL, &err);
|
|
if (!dirmon) {
|
|
g_warning ("error adding monitor: %s", err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
g_object_unref (dir);
|
|
|
|
if (dirmon)
|
|
g_signal_connect (dirmon, "changed",
|
|
G_CALLBACK(on_dir_change), data);
|
|
|
|
return dirmon;
|
|
}
|
|
|
|
static void
|
|
destroy_watchlist (GSList *lst)
|
|
{
|
|
g_slist_foreach (lst, (GFunc)g_object_unref, NULL);
|
|
g_slist_free (lst);
|
|
}
|
|
|
|
|
|
GSList*
|
|
create_watchlist (char **dirs, ChildData *data)
|
|
{
|
|
GSList *watchlist;
|
|
char **cur;
|
|
|
|
/* TODO: check for dups */
|
|
for (watchlist = NULL, cur = dirs; cur && *cur; ++cur) {
|
|
GFileMonitor *dirmon;
|
|
dirmon = create_monitor (*cur, data);
|
|
if (!dirmon) {
|
|
destroy_watchlist (watchlist);
|
|
return NULL;
|
|
}
|
|
watchlist = g_slist_prepend (watchlist, dirmon);
|
|
}
|
|
|
|
return watchlist;
|
|
}
|
|
|
|
|
|
struct _PMConfig {
|
|
char *muhome;
|
|
char **watchdirs;
|
|
|
|
};
|
|
typedef struct _PMConfig PMConfig;
|
|
|
|
|
|
static void
|
|
expand_paths (PMConfig *opts)
|
|
{
|
|
char **cur;
|
|
|
|
for (cur = opts->watchdirs; cur && *cur; ++cur)
|
|
*cur = mu_util_dir_expand (*cur);
|
|
|
|
if (opts->muhome)
|
|
opts->muhome = mu_util_dir_expand (opts->muhome);
|
|
}
|
|
|
|
|
|
static PMConfig *
|
|
pm_config_new (int *argcp, char ***argvp)
|
|
{
|
|
GOptionContext *octx;
|
|
|
|
PMConfig *opts = g_new0 (PMConfig, 1);
|
|
GOptionEntry entries[] = {
|
|
{"muhome", 0, 0, G_OPTION_ARG_FILENAME, &opts->muhome,
|
|
"specify an alternative mu directory", NULL},
|
|
{"watch", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opts->watchdirs,
|
|
"directory to watch (may be specified multiple times)", NULL},
|
|
{NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL}/* sentinel */
|
|
};
|
|
|
|
octx = g_option_context_new ("- procmule options");
|
|
g_option_context_add_main_entries (octx, entries, "Procmule");
|
|
|
|
if (!g_option_context_parse (octx, argcp, argvp, NULL)) {
|
|
g_printerr ("error in options\n");
|
|
goto error;
|
|
}
|
|
|
|
if (!opts->watchdirs) {
|
|
g_printerr ("specify at least one --watch=<dir>\n");
|
|
goto error;
|
|
}
|
|
|
|
expand_paths (opts);
|
|
|
|
g_option_context_free (octx);
|
|
return opts;
|
|
|
|
error:
|
|
if (octx)
|
|
g_option_context_free (octx);
|
|
g_free (opts);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
pm_config_destroy (PMConfig *conf)
|
|
{
|
|
if (!conf)
|
|
return;
|
|
|
|
g_free (conf->muhome);
|
|
g_strfreev (conf->watchdirs);
|
|
|
|
g_free (conf);
|
|
}
|
|
|
|
|
|
static void
|
|
usage (void)
|
|
{
|
|
g_print ("usage: procmule [--muhome=<dir>] [--watch=<dir1>]\n");
|
|
g_print ("also, see toys/procmule/README\n");
|
|
}
|
|
|
|
|
|
static gboolean
|
|
watch_dirs (char **watchdirs)
|
|
{
|
|
ChildData *child_data;
|
|
GSList *watchlist;
|
|
GMainLoop *loop;
|
|
|
|
child_data = child_data_new
|
|
(mu_runtime_path(MU_RUNTIME_PATH_MUHOME));
|
|
|
|
watchlist = create_watchlist (watchdirs, child_data);
|
|
if (!watchlist)
|
|
goto error;
|
|
|
|
loop = g_main_loop_new (NULL, TRUE);
|
|
|
|
g_main_loop_run (loop);
|
|
g_main_loop_unref (loop);
|
|
|
|
destroy_watchlist (watchlist);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
child_data_destroy (child_data);
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
PMConfig *opts;
|
|
|
|
g_type_init ();
|
|
g_thread_init (NULL);
|
|
|
|
#ifdef HAVE_PRE2_GUILE
|
|
g_warning ("Note: pre-2.x version of guile: procmule will not function "
|
|
"correctly unless you're using UTF-8 locale.");
|
|
#endif /* HAVE_PRE2_GUILE */
|
|
|
|
opts = pm_config_new (&argc, &argv);
|
|
if (!opts) {
|
|
usage ();
|
|
goto error;
|
|
}
|
|
|
|
if (!mu_runtime_init (opts->muhome, "procmule")) {
|
|
usage ();
|
|
goto error;
|
|
}
|
|
|
|
watch_dirs (opts->watchdirs); /* do it! */
|
|
mu_runtime_uninit ();
|
|
|
|
pm_config_destroy (opts);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
pm_config_destroy (opts);
|
|
return 1;
|
|
|
|
}
|