summaryrefslogtreecommitdiff
path: root/libmailutils/tests/tesh.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/tests/tesh.c')
-rw-r--r--libmailutils/tests/tesh.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/libmailutils/tests/tesh.c b/libmailutils/tests/tesh.c
new file mode 100644
index 000000000..6fe7000ee
--- /dev/null
+++ b/libmailutils/tests/tesh.c
@@ -0,0 +1,385 @@
+/* Test shell framework for GNU Mailutils.
+ Copyright (C) 2003-2019 Free Software Foundation, Inc.
+
+ GNU Mailutils 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.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include "mailutils/mailutils.h"
+#include "tesh.h"
+
+static struct mu_tesh_command *
+find_command (struct mu_tesh_command *cmd, char const *name)
+{
+ while (cmd->verb)
+ {
+ if (strcmp (cmd->verb, name) == 0)
+ return cmd;
+ cmd++;
+ }
+ return NULL;
+}
+
+static int
+cmdspecial (char const *special, struct mu_tesh_command *cmdtab,
+ int argc, char **argv, mu_assoc_t opt, void *env, int defval)
+{
+ struct mu_tesh_command *cmd = find_command (cmdtab, special);
+ if (cmd)
+ return cmd->func (argc, argv, opt, env);
+ return defval;
+}
+
+static int
+is_reserved (char const *str)
+{
+ return mu_string_prefix (str, "__") && mu_string_suffix (str, "__");
+}
+
+#define VARIADIC (-1)
+
+static int
+interpret (int argc, char **xargv, struct mu_tesh_command *cmdtab, void *env)
+{
+ int rc;
+ struct mu_tesh_command *cmd;
+ mu_assoc_t options = NULL;
+ char **argv = xargv;
+
+ if (strcmp (argv[0], "help") == 0)
+ {
+ mu_tesh_help (cmdtab, env);
+ return 0;
+ }
+
+ if (is_reserved (argv[0]))
+ cmd = NULL;
+ else
+ cmd = find_command (cmdtab, argv[0]);
+ if (!cmd)
+ {
+ if (cmdspecial("__NOCMD__", cmdtab, argc, argv, NULL, env, MU_ERR_NOENT))
+ {
+ mu_error ("%s: no such command", argv[0]);
+ return MU_ERR_NOENT;
+ }
+ return 0;
+ }
+ if (cmd->param_min == 0)
+ {
+ struct mu_wordsplit ws;
+
+ /* See the description of the args field in tesh.h (Note [2]) */
+ if (mu_wordsplit (cmd->args, &ws,
+ MU_WRDSF_NOVAR
+ | MU_WRDSF_NOCMD
+ | MU_WRDSF_ALLOC_DIE
+ | MU_WRDSF_SHOWERR))
+ return MU_ERR_PARSE;
+
+ if (ws.ws_wordc == 0)
+ {
+ cmd->param_min = cmd->param_max = 1;
+ cmd->options = NULL;
+ }
+ else
+ {
+ int i;
+ int variadic = 0;
+
+ cmd->param_min = cmd->param_max = 1;
+ for (i = 0; i < ws.ws_wordc; i++)
+ {
+ char *argdef = ws.ws_wordv[i];
+
+ if (mu_string_suffix (argdef, "..."))
+ {
+ variadic = 1;
+ argdef[strlen (argdef) - 3] = 0;
+ if (argdef[0] == 0)
+ {
+ if (i != ws.ws_wordc - 1)
+ {
+ mu_error ("%s: ellipsis must be last (found at #%d)",
+ cmd->args, i);
+ abort ();
+ }
+ break;
+ }
+ }
+
+ if (mu_string_prefix (argdef, "[-")
+ && mu_string_suffix (argdef, "]"))
+ {
+ int *type;
+ char *p;
+
+ type = mu_alloc (sizeof (*type));
+
+ argdef += 2;
+ argdef[strlen (argdef) - 1] = 0;
+
+ if (!cmd->options)
+ {
+ MU_ASSERT (mu_assoc_create (&cmd->options, 0));
+ }
+
+ p = strchr (argdef, '=');
+ if (p)
+ {
+ *p = 0;
+ if (p[-1] == '[' && mu_string_suffix (argdef, "]"))
+ {
+ *type = mu_tesh_arg_optional;
+ p[-1] = 0;
+ }
+ else
+ *type = mu_tesh_arg_required;
+ }
+ else
+ *type = mu_tesh_noarg;
+ MU_ASSERT (mu_assoc_install (cmd->options, argdef, type));
+ }
+ else if (mu_string_prefix (argdef, "["))
+ {
+ int j;
+ int lev = 0;
+ for (j = i; j < ws.ws_wordc; )
+ {
+ if (mu_string_prefix (ws.ws_wordv[j], "["))
+ lev++;
+ if (mu_string_suffix (ws.ws_wordv[j], "]"))
+ lev--;
+ j++;
+ if (lev == 0)
+ break;
+ }
+ if (lev == 0)
+ {
+ cmd->param_max += j - i;
+ i = j;
+ }
+ }
+ else
+ {
+ cmd->param_min++;
+ cmd->param_max++;
+ }
+ }
+
+ if (!variadic
+ && mu_string_prefix (ws.ws_wordv[ws.ws_wordc-1], "[")
+ && mu_string_suffix (ws.ws_wordv[ws.ws_wordc-1], "...]"))
+ {
+ variadic = 1;
+ }
+
+ if (variadic)
+ cmd->param_max = VARIADIC;
+ mu_wordsplit_free (&ws);
+ }
+ }
+
+ if (cmd->options)
+ {
+ int i;
+
+ /* Extract options */
+ MU_ASSERT (mu_assoc_create (&options, 0));
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp (argv[i], "--") == 0)
+ {
+ i++;
+ break;
+ }
+
+ if (argv[i][0] == '-')
+ {
+ int *type;
+ char *opt = argv[i];
+ char *arg;
+
+ arg = strchr (opt, '=');
+ if (arg)
+ *arg++ = 0;
+
+ rc = mu_assoc_lookup (cmd->options, opt + 1, &type);
+ if (rc == MU_ERR_NOENT)
+ {
+ mu_error ("%s: no such option %s", argv[0], opt);
+ mu_assoc_destroy (&options);
+ return MU_ERR_NOENT;
+ }
+
+ if (arg)
+ {
+ if (*type == mu_tesh_noarg)
+ {
+ mu_error ("%s: option %s doesn't take argument",
+ argv[0], opt);
+ mu_assoc_destroy (&options);
+ return MU_ERR_PARSE;
+ }
+ }
+ else if (*type == mu_tesh_arg_required)
+ {
+ if (i + 1 < argc)
+ arg = argv[++i];
+ else
+ {
+ mu_error ("%s: option %s requires argument",
+ argv[0], opt);
+ mu_assoc_destroy (&options);
+ return MU_ERR_PARSE;
+ }
+ }
+
+ if (arg)
+ arg = mu_strdup (arg);
+ MU_ASSERT (mu_assoc_install (options, opt + 1, arg));
+ }
+ else
+ break;
+ }
+
+ if (i > 1)
+ {
+ char *t;
+
+ --i;
+ t = argv[i];
+ argv[i] = argv[0];
+ argv[0] = t;
+ argc -= i;
+ argv += i;
+ }
+ }
+
+ if (argc < cmd->param_min)
+ {
+ mu_error ("%s: not enough arguments", argv[0]);
+ return MU_ERR_PARSE;
+ }
+
+ if (cmd->param_max != VARIADIC && argc > cmd->param_max)
+ {
+ mu_error ("%s: too many arguments", argv[0]);
+ return MU_ERR_PARSE;
+ }
+
+ rc = cmdspecial ("__ENVINIT__", cmdtab, argc, argv, options, env, 0);
+ if (rc == 0)
+ {
+ rc = cmd->func (argc, argv, options, env);
+ cmdspecial ("__ENVFINI__", cmdtab, argc, argv, options, env, 0);
+ }
+ mu_assoc_destroy (&options);
+ return rc;
+}
+
+static void
+cleanup (struct mu_tesh_command *cmd)
+{
+ while (cmd->verb)
+ {
+ mu_assoc_destroy (&cmd->options);
+ cmd++;
+ }
+}
+
+void
+mu_tesh_read_and_eval (int argc, char **argv,
+ struct mu_tesh_command *cmd,
+ void *env)
+{
+ if (argc)
+ {
+ while (argc)
+ {
+ int i, n = 0;
+ for (i = 0; i < argc; i++)
+ {
+ size_t len = strlen (argv[i]);
+ if (argv[i][len - 1] == ';')
+ {
+ if (len == 1)
+ n = 1;
+ else
+ argv[i][len - 1] = 0;
+ i++;
+ break;
+ }
+ }
+
+ interpret (i - n, argv, cmd, env);
+ argc -= i;
+ argv += i;
+ }
+ }
+ else
+ {
+ char *buf = NULL;
+ size_t size = 0, n;
+ struct mu_wordsplit ws;
+ int wsflags;
+ int rc;
+
+ wsflags = MU_WRDSF_DEFFLAGS
+ | MU_WRDSF_COMMENT
+ | MU_WRDSF_ALLOC_DIE
+ | MU_WRDSF_SHOWERR;
+ ws.ws_comment = "#";
+
+ while ((rc = mu_stream_getline (mu_strin, &buf, &size, &n)) == 0 && n > 0)
+ {
+ char *larg[2];
+
+ mu_ltrim_class (buf, MU_CTYPE_SPACE);
+ mu_rtrim_class (buf, MU_CTYPE_SPACE);
+
+ larg[0] = buf;
+ larg[1] = NULL;
+ if (!cmdspecial("__LINEPROC__", cmd, 1, larg, NULL, env, MU_ERR_NOENT))
+ continue;
+
+ MU_ASSERT (mu_wordsplit (larg[0], &ws, wsflags));
+ wsflags |= MU_WRDSF_REUSE;
+
+ if (ws.ws_wordc == 0)
+ continue;
+ interpret (ws.ws_wordc, ws.ws_wordv, cmd, env);
+ }
+ if (wsflags & MU_WRDSF_REUSE)
+ mu_wordsplit_free (&ws);
+ }
+
+ cleanup (cmd);
+}
+
+void
+mu_tesh_init (char const *argv0)
+{
+ mu_set_program_name (argv0);
+ mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
+}
+
+void
+mu_tesh_help (struct mu_tesh_command *cmd, void *env)
+{
+ cmdspecial("__HELPINIT__", cmd, 0, NULL, NULL, env, 0);
+ for (; cmd->verb; cmd++)
+ if (!is_reserved (cmd->verb))
+ mu_printf (" %s %s\n", cmd->verb, cmd->args);
+ cmdspecial("__HELPFINI__", cmd, 0, NULL, NULL, env, 0);
+}

Return to:

Send suggestions and report system problems to the System administrator.