diff --git a/man/Makefile.am b/man/Makefile.am index 40ea1944..10b39334 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1,4 +1,4 @@ -## Copyright (C) 2008-2010 Dirk-Jan C. Binnema +## Copyright (C) 2008-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 @@ -17,7 +17,6 @@ include $(top_srcdir)/gtest.mk dist_man_MANS = \ - mu-cleanup.1 \ mu-bookmarks.5 \ mu-cfind.1 \ mu-easy.1 \ @@ -29,5 +28,6 @@ dist_man_MANS = \ mu-mv.1 \ mu-add.1 \ mu-remove.1 \ + mu-server.1 \ mu.1 \ mug.1 diff --git a/man/mu-server.1 b/man/mu-server.1 new file mode 100644 index 00000000..19df7683 --- /dev/null +++ b/man/mu-server.1 @@ -0,0 +1,42 @@ +.TH MU SERVER 1 "September 2011" "User Manuals" + +.SH NAME + +mu server\- start a mu process which accepts commands + +.SH SYNOPSIS + +.B mu server + +.SH DESCRIPTION + +\fBmu server\fR is the \fBmu\fR sub-command for starting a mu server process +which accepts various commands (see below). \fBmu server\fR is specifically +designed for communication with the emacs-text editor, and as such, the output +is in the form of s-expressions. + +\fBmu server\fR could be use as a very rudimentary interactive shell, but we +would recommend the `muile' instead, which is much more powerful and suitable +for humans. + +.SH OPTIONS + +.SH RETURN VALUE + +\fBmu server\fR returns 0 upon success, and some other value when an error +occurs. See \fBmu(1)\fR for a list. + +.SH BUGS + +Please report bugs if you find them: +.BR http://code.google.com/p/mu0/issues/list + +.SH AUTHOR + +Dirk-Jan C. Binnema + +.SH "SEE ALSO" +.BR mu(1) +.BR mu-index(1) +.BR mu-add(1) +.BR mu-remove(1) diff --git a/src/mu-cmd-server.c b/src/mu-cmd-server.c new file mode 100644 index 00000000..d9a5ebdc --- /dev/null +++ b/src/mu-cmd-server.c @@ -0,0 +1,354 @@ +/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ +/* +** Copyright (C) 2010-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. +** +*/ + +#if HAVE_CONFIG_H +#include "config.h" +#endif /*HAVE_CONFIG_H*/ + +#include +#include +#include +#include +#include + +#ifdef HAVE_LIBREADLINE +#include +#include +#endif /*HAVE_LIBREADLINE*/ + +#include "mu-msg.h" +#include "mu-msg-part.h" +#include "mu-cmd.h" +#include "mu-util.h" +#include "mu-str.h" +#include "mu-date.h" +#include "mu-maildir.h" +#include "mu-contacts.h" +#include "mu-runtime.h" +#include "mu-flags.h" +#include "mu-store.h" +#include "mu-query.h" + +#define EOX ";; eox\n" + + +static void +print_expr (const char* str) +{ + fputs (str, stdout); + fputs (EOX, stdout); +} + + +static gboolean +server_error (MuError err, const char* str) +{ + gchar *errexp; + + errexp = g_strdup_printf ("(:error %u :error-message \"%s\") ", + err, str); + print_expr (errexp); + g_free (errexp); + + return FALSE; +} + +#define MU_PROMPT "mu> " + +static gchar* +my_readline (const char *prompt) +{ +#ifdef HAVE_LIBREADLINE + return readline (MU_PROMPT); +#else + GString *gstr; + int kar; + + gstr = g_string_sized_new (512); + + g_print ("%s", prompt); + + do { + kar = fgetc (stdin); + if (kar == '\n' || kar == EOF) + break; + else + gstr = g_string_append_c (gstr, (char)kar); + + } while (1); + + return g_string_free (gstr, FALSE); +#endif /*!HAVE_LIBREADLINE*/ +} + +enum _Cmd { + CMD_ADD, + CMD_FIND, + CMD_HELP, + CMD_QUIT, + CMD_REMOVE, + CMD_RENAME, + CMD_RM, + CMD_VIEW, + CMD_VERSION, + CMD_IGNORE, + CMD_NUM, +}; +typedef enum _Cmd Cmd; + +static const Cmd CMD_INVALID = (Cmd)-1; + +struct _CmdInfo { + Cmd cmd; + const char *cmdstr; + const char *help; +}; +typedef struct _CmdInfo CmdInfo; + +static const CmdInfo CMD_INFO[] = { + { CMD_ADD, "add", + "add [] ;; add a message to the database" }, + { CMD_FIND, "find", + "find \"\";; find messages matching " }, + { CMD_HELP, "help", + "help [];; get some help information about " }, + { CMD_QUIT, "quit", + "quit ;; quit mu server" }, + { CMD_REMOVE, "remove", + "remove | [|]" + ";; remove path (with "") or docid from the database" }, + { CMD_RENAME, "rename", + "rename | " + ";; rename (move) path (with "") or docid to a new " }, + { CMD_VERSION, "version", "version ;; get the mu version" }, + { CMD_VIEW, "view", "view ;; get the contents of message at " } +}; + +static Cmd +cmd_from_string (const char *str) +{ + unsigned u; + + for (u = 0; u != G_N_ELEMENTS(CMD_INFO); ++u) + if (g_strcmp0 (str, CMD_INFO[u].cmdstr) == 0) + return CMD_INFO[u].cmd; + + /* handle all-blank strings */ + if (g_regex_match_simple ("^ *$", str, 0, 0)) + return CMD_IGNORE; + + return CMD_INVALID; +} + + +static Cmd +parse_line (const gchar *line, GSList **args) +{ + Cmd cmd; + GSList *lst; + + lst = mu_str_esc_to_list (line); + if (!lst) + return CMD_INVALID; + + cmd = cmd_from_string ((const char*)lst->data); + + if (cmd == CMD_INVALID) { + mu_str_free_list (lst); + *args = NULL; + } else { + *args = g_slist_next (lst); + lst->next = NULL; + mu_str_free_list (lst); + } + + return cmd; +} + + +static gboolean +cmd_version (GSList *lst, GError **err) +{ + if (lst) { + g_set_error (err, 0,MU_ERROR_IN_PARAMETERS, + "`version' does not take any parameters"); + return FALSE; + } + + print_expr ("(:version \"" VERSION "\")\n"); + return TRUE; +} + + +static MuConfig* +get_opts (MuConfigCmd cmd) +{ + static MuConfig opts; + + memset (&opts, 0, sizeof(opts)); + opts.cmd = cmd; + opts.format = MU_CONFIG_FORMAT_SEXP; + + return &opts; +} + +static MuError +cmd_find (MuStore *store, GSList *lst, GError **err) +{ + MuConfig *opts; + const gchar *params[3] = { NULL, NULL, NULL }; + + if (!lst) + return server_error (MU_ERROR_IN_PARAMETERS, + "find takes 1 parameter"); + + opts = get_opts (MU_CONFIG_CMD_FIND); + params[1] = (const char*)lst->data; + opts->params = (char**)params; + + return mu_cmd_find (store, opts, err); +} + + +static MuError +cmd_view (GSList *args, GError **err) +{ + MuConfig *opts; + const gchar *params[3] = { NULL, NULL, NULL }; + + if (g_slist_length(args) != 1) + return server_error (MU_ERROR_IN_PARAMETERS, + "view takes exactly 1 parameter"); + + opts = get_opts (MU_CONFIG_CMD_VIEW); + params[1] = (const char*)args->data; + opts->params = (char**)params; + + return mu_cmd_view (opts, err); +} + + + +static gboolean +cmd_help (GSList *lst, GError **err) +{ + unsigned u; + + if (g_slist_length (lst) > 1) { + g_set_error (err, 0, MU_ERROR_IN_PARAMETERS, + "`help' takes at most one parameter"); + return FALSE; + } + + if (!lst) { + g_print (";; use 'help to get info about a " + "particular command\n"); + g_print (";; commands are: "); + for (u = 0; u != G_N_ELEMENTS(CMD_INFO); ++u) + g_print ("%s ", CMD_INFO[u].cmdstr); + g_print ("\n"); + + } else { + Cmd c = cmd_from_string ((const char*)lst->data); + if (c == CMD_INVALID) { + g_set_error (err, 0, MU_ERROR_IN_PARAMETERS, + "no such command"); + return FALSE; + } + + for (u = 0; u != G_N_ELEMENTS(CMD_INFO); ++u) + if (CMD_INFO[u].cmd == c) + g_print (";; usage: %s\n", CMD_INFO[u].help); + } + + return TRUE; +} + + +static gboolean +cmd_quit (GSList *lst, GError **err) +{ + if (lst) { + g_set_error (err, 0, MU_ERROR_IN_PARAMETERS, + "`quit' does not take any parameters"); + return FALSE; + } + + print_expr (";; quiting"); + return TRUE; +} + + +static gboolean +handle_command (MuConfigCmd cmd, MuStore *store, GSList *args, GError **err) +{ + MuError rv; + + switch (cmd) { + case CMD_VERSION: rv = cmd_version (args, err); break; + case CMD_QUIT: rv = cmd_quit (args, err); break; + case CMD_HELP: rv = cmd_help (args, err); break; + + case CMD_FIND: rv = cmd_find (store, args, err); break; + case CMD_VIEW: rv = cmd_view (args, err); break; + + case CMD_IGNORE: return TRUE; + default: + g_set_error (err, 0, MU_ERROR_IN_PARAMETERS, + "unknown command"); + return FALSE; + } + + return rv == MU_OK ? TRUE : FALSE; +} + + +MuError +mu_cmd_server (MuStore *store, MuConfig *opts, GError **err/*unused*/) +{ + g_return_val_if_fail (store, MU_ERROR_INTERNAL); + + g_print (";; welcome to mu\n"); + g_print (";; type your commands, and press Enter to execute them\n"); + + while (1) { + char *line; + Cmd cmd; + GSList *args; + GError *err; + + line = my_readline (MU_PROMPT); + cmd = parse_line (line, &args); + g_free (line); + + err = NULL; + if (!handle_command (cmd, store, args, &err)) { + server_error (err->code, err->message); + g_clear_error (&err); + } + + mu_str_free_list (args); + + if (cmd == CMD_QUIT) + break; + } + + return MU_OK; +} diff --git a/src/mu-config.c b/src/mu-config.c index acf1b508..f15facd8 100644 --- a/src/mu-config.c +++ b/src/mu-config.c @@ -80,7 +80,6 @@ set_group_mu_defaults (MuConfig *opts) opts->muhome = exp; } - /* check for the MU_COLORS env var; but in any case don't use * colors unless we're writing to a tty */ @@ -124,10 +123,8 @@ config_options_group_mu (MuConfig *opts) static void set_group_index_defaults (MuConfig * opts) { - /* note: opts->maildir is handled in mu-cmd, as we need to - * have a MuIndex entry for mu_index_last_used_maildir () - */ - opts->xbatchsize = 0; + if (!opts->maildir) + opts->maildir = mu_util_guess_maildir (); } static GOptionGroup* @@ -401,7 +398,6 @@ parse_cmd (MuConfig *opts, int *argcp, char ***argvp) MuConfigCmd _cmd; } cmd_map[] = { { "cfind", MU_CONFIG_CMD_CFIND }, - { "cleanup", MU_CONFIG_CMD_CLEANUP }, { "extract", MU_CONFIG_CMD_EXTRACT }, { "find", MU_CONFIG_CMD_FIND }, { "index", MU_CONFIG_CMD_INDEX }, @@ -440,8 +436,6 @@ add_context_group (GOptionContext *context, MuConfig *opts) { GOptionGroup *group; - group = NULL; - switch (opts->cmd) { case MU_CONFIG_CMD_INDEX: group = config_options_group_index (opts); break; @@ -458,13 +452,12 @@ add_context_group (GOptionContext *context, MuConfig *opts) case MU_CONFIG_CMD_VIEW: group = config_options_group_view (opts); break; case MU_CONFIG_CMD_SERVER: - group = config_options_group_server (opts); break; - - default: break; + group = config_options_group_server (opts); break; + default: + return; /* no group to add */ } - if (group) - g_option_context_add_group(context, group); + g_option_context_add_group(context, group); } @@ -528,6 +521,7 @@ mu_config_destroy (MuConfig *opts) g_free (opts->maildir); g_free (opts->linksdir); g_free (opts->targetdir); + g_strfreev (opts->params); g_free (opts); } @@ -539,7 +533,6 @@ mu_config_param_num (MuConfig *conf) guint u; g_return_val_if_fail (conf, 0); - for (u = 0; conf->params[u]; ++u); return u; diff --git a/src/mu-config.h b/src/mu-config.h index 6fbbfdc1..d2e8484d 100644 --- a/src/mu-config.h +++ b/src/mu-config.h @@ -63,7 +63,6 @@ enum _MuConfigCmd { MU_CONFIG_CMD_INDEX, MU_CONFIG_CMD_FIND, - MU_CONFIG_CMD_CLEANUP, MU_CONFIG_CMD_MKDIR, MU_CONFIG_CMD_VIEW, MU_CONFIG_CMD_EXTRACT,