summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2019-03-25 12:35:06 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2019-03-25 14:16:08 (GMT)
commitcef8dafaf17b67c1b4836e22ef00c54f38237fe6 (patch) (side-by-side diff)
tree8b4a134c60934f92021a0851d4aa314046a7e69b
parent1a523cb6c788fc46b234019f4198b865c4b5f502 (diff)
downloadmailutils-cef8dafaf17b67c1b4836e22ef00c54f38237fe6.tar.gz
mailutils-cef8dafaf17b67c1b4836e22ef00c54f38237fe6.tar.bz2
Implement test shell framework
* libmailutils/tests/tesh.c: New file. * libmailutils/tests/tesh.h: New file. * libmailutils/tests/Makefile.am: Add new files. * libmailutils/tests/fsfolder.c: Rewrite using test shell framework. * libmailutils/tests/fsfolder00.at: Use semicolons to delimit commands. * libmailutils/tests/fsfolder01.at: Likewise. * libmailutils/tests/linetrack.c: Rewrite using test shell framework. * libmailutils/tests/linetrack.at: Change command initial character to dot. * libmailutils/tests/listop.c: Rewrite using test shell framework. * libmailutils/tests/list.at: Minor changes. * libproto/dotmail/tests/dm_mesg.c: Rewrite using test shell framework. * libproto/dotmail/tests/Makefile.am: Link dm_mesg with libmu_tesh * libproto/dotmail/dotmail.c: Fix typo.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--libmailutils/tests/Makefile.am10
-rw-r--r--libmailutils/tests/fsfolder.c132
-rw-r--r--libmailutils/tests/fsfolder00.at6
-rw-r--r--libmailutils/tests/fsfolder01.at2
-rw-r--r--libmailutils/tests/linetrack.at56
-rw-r--r--libmailutils/tests/linetrack.c150
-rw-r--r--libmailutils/tests/list.at6
-rw-r--r--libmailutils/tests/listop.c701
-rw-r--r--libmailutils/tests/tesh.c385
-rw-r--r--libmailutils/tests/tesh.h176
-rw-r--r--libproto/dotmail/dotmail.c2
-rw-r--r--libproto/dotmail/tests/Makefile.am2
-rw-r--r--libproto/dotmail/tests/dm_mesg.c414
13 files changed, 1234 insertions, 808 deletions
diff --git a/libmailutils/tests/Makefile.am b/libmailutils/tests/Makefile.am
index b0f3b92..ef95577 100644
--- a/libmailutils/tests/Makefile.am
+++ b/libmailutils/tests/Makefile.am
@@ -39,6 +39,10 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
## -------------------------- ##
AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@
+
+noinst_LTLIBRARIES = libmu_tesh.la
+libmu_tesh_la_SOURCES = tesh.c tesh.h
+
noinst_PROGRAMS = \
addr\
cidr\
@@ -83,6 +87,12 @@ noinst_PROGRAMS = \
wsp\
xscript
+fsfolder_LDFLAGS = -lmu_tesh $(LDADD)
+
+linetrack_LDFLAGS = -lmu_tesh $(LDADD)
+
+listop_LDFLAGS = -lmu_tesh $(LDADD)
+
LDADD = ${MU_LIB_MAILUTILS}
EXTRA_DIST += Encode Decode Wicketfile
diff --git a/libmailutils/tests/fsfolder.c b/libmailutils/tests/fsfolder.c
index f6f62d7..789569d 100644
--- a/libmailutils/tests/fsfolder.c
+++ b/libmailutils/tests/fsfolder.c
@@ -31,18 +31,12 @@
#include <mailutils/registrar.h>
#include <mailutils/sys/folder.h>
#include <mailutils/sys/registrar.h>
+#include <mailutils/opt.h>
+#include "tesh.h"
int sort_option;
int prefix_len;
-struct command
-{
- char *verb;
- int nargs;
- char *args;
- void (*handler) (mu_folder_t folder, char **argv);
-};
-
static int
compare_response (void const *a, void const *b)
{
@@ -70,16 +64,17 @@ _print_list_entry (void *item, void *data)
return 0;
}
-static void
-com_list (mu_folder_t folder, char **argv)
+static int
+com_list (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_folder_t folder = env;
int rc;
mu_list_t list;
- mu_printf ("listing '%s' '%s'\n", argv[0], argv[1]);
- rc = mu_folder_list (folder, argv[0], argv[1], 0, &list);
+ mu_printf ("listing '%s' '%s'\n", argv[1], argv[2]);
+ rc = mu_folder_list (folder, argv[1], argv[2], 0, &list);
if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_list", argv[0], rc);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_list", argv[1], rc);
else
{
if (sort_option)
@@ -87,18 +82,20 @@ com_list (mu_folder_t folder, char **argv)
mu_list_foreach (list, _print_list_entry, &prefix_len);
mu_list_destroy (&list);
}
+ return 0;
}
-static void
-com_lsub (mu_folder_t folder, char **argv)
+static int
+com_lsub (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_folder_t folder = env;
int rc;
mu_list_t list;
- mu_printf ("listing subscriptions for '%s' '%s'\n", argv[0], argv[1]);
- rc = mu_folder_lsub (folder, argv[0], argv[1], &list);
+ mu_printf ("listing subscriptions for '%s' '%s'\n", argv[1], argv[2]);
+ rc = mu_folder_lsub (folder, argv[1], argv[2], &list);
if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_lsub", argv[0], rc);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_lsub", argv[1], rc);
else
{
if (sort_option)
@@ -106,78 +103,71 @@ com_lsub (mu_folder_t folder, char **argv)
mu_list_foreach (list, _print_list_entry, NULL);
mu_list_destroy (&list);
}
+ return 0;
}
-static void
-com_rename (mu_folder_t folder, char **argv)
+static int
+com_rename (int argc, char **argv, mu_assoc_t options, void *env)
{
int rc;
-
- mu_printf ("renaming %s to %s\n", argv[0], argv[1]);
- rc = mu_folder_rename (folder, argv[0], argv[1]);
+ mu_folder_t folder = env;
+
+ mu_printf ("renaming %s to %s\n", argv[1], argv[2]);
+ rc = mu_folder_rename (folder, argv[1], argv[2]);
if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_rename", argv[0], rc);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_rename", argv[1], rc);
else
mu_printf ("rename successful\n");
+ return 0;
}
-static void
-com_subscribe (mu_folder_t folder, char **argv)
+static int
+com_subscribe (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_folder_t folder = env;
int rc;
- mu_printf ("subscribing %s\n", argv[0]);
- rc = mu_folder_subscribe (folder, argv[0]);
+ mu_printf ("subscribing %s\n", argv[1]);
+ rc = mu_folder_subscribe (folder, argv[1]);
if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_subscribe", argv[0], rc);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_subscribe", argv[1], rc);
else
mu_printf ("subscribe successful\n");
+ return 0;
}
-static void
-com_unsubscribe (mu_folder_t folder, char **argv)
+static int
+com_unsubscribe (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_folder_t folder = env;
int rc;
- mu_printf ("unsubscribing %s\n", argv[0]);
- rc = mu_folder_unsubscribe (folder, argv[0]);
+ mu_printf ("unsubscribing %s\n", argv[1]);
+ rc = mu_folder_unsubscribe (folder, argv[1]);
if (rc)
- mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_unsubscribe", argv[0], rc);
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_unsubscribe", argv[1], rc);
else
mu_printf ("unsubscribe successful\n");
+ return 0;
}
-static struct command comtab[] = {
- { "list", 2, "REF MBX", com_list },
- { "lsub", 2, "REF MBX", com_lsub },
- { "rename", 2, "OLD NEW", com_rename },
- { "subscribe", 1, "MBX", com_subscribe },
- { "unsubscribe", 1, "MBX", com_unsubscribe },
+static struct mu_tesh_command comtab[] = {
+ { "list", "REF MBX", com_list },
+ { "lsub", "REF MBX", com_lsub },
+ { "rename", "OLD NEW", com_rename },
+ { "subscribe", "MBX", com_subscribe },
+ { "unsubscribe", "MBX", com_unsubscribe },
{ NULL }
};
-static struct command *
-find_command (const char *name)
-{
- struct command *cp;
-
- for (cp = comtab; cp->verb; cp++)
- if (strcmp (cp->verb, name) == 0)
- return cp;
- return NULL;
-}
-
static void
-usage ()
+usage (void)
{
- struct command *cp;
-
mu_printf (
- "usage: %s [-debug=SPEC] -name=URL [-sort] [-glob] OP ARG [ARG...] [OP ARG [ARG...]...]\n",
+ "usage: %s [-debug=SPEC] -name=URL [-sort] [-glob] OP ARG... [\\; OP ARG...]...]\n",
mu_program_name);
mu_printf ("OPerations and corresponding ARGuments are:\n");
- for (cp = comtab; cp->verb; cp++)
- mu_printf (" %s %s\n", cp->verb, cp->args);
+ mu_tesh_help (comtab, NULL);
}
static int
@@ -236,8 +226,8 @@ main (int argc, char **argv)
mu_folder_t folder;
char *fname = NULL;
int glob_option = 0;
-
- mu_set_program_name (argv[0]);
+
+ mu_tesh_init (argv[0]);
mu_registrar_record (&test_record);
if (argc == 1)
@@ -291,30 +281,8 @@ main (int argc, char **argv)
if (glob_option)
mu_folder_set_match (folder, mu_folder_glob_match);
-
- while (i < argc)
- {
- char *comargs[2];
- struct command *cmd;
-
- cmd = find_command (argv[i]);
- if (!cmd)
- {
- mu_error ("unknown command %s\n", argv[i]);
- break;
- }
- i++;
- if (i + cmd->nargs > argc)
- {
- mu_error ("not enough arguments for %s", cmd->verb);
- break;
- }
- memcpy (comargs, argv + i, cmd->nargs * sizeof (comargs[0]));
- i += cmd->nargs;
-
- cmd->handler (folder, comargs);
- }
+ mu_tesh_read_and_eval (argc - i, argv + i, comtab, folder);
mu_folder_close (folder);
mu_folder_destroy (&folder);
diff --git a/libmailutils/tests/fsfolder00.at b/libmailutils/tests/fsfolder00.at
index ed5f1f3..a179441 100644
--- a/libmailutils/tests/fsfolder00.at
+++ b/libmailutils/tests/fsfolder00.at
@@ -27,9 +27,9 @@ mkdir dir/subdir
> dir/subdir/baz.mbox
fsfolder -name=dir -sort dnl
- list "" "*" dnl
- list subdir "*" dnl
- list "" "*.mbox" dnl
+ list "" "*" \; dnl
+ list subdir "*" \; dnl
+ list "" "*.mbox" \; dnl
list "subdir" "*.mbox"
],
[0],
diff --git a/libmailutils/tests/fsfolder01.at b/libmailutils/tests/fsfolder01.at
index 577f3b4..da17c45 100644
--- a/libmailutils/tests/fsfolder01.at
+++ b/libmailutils/tests/fsfolder01.at
@@ -22,7 +22,7 @@ mkdir dir
fsfolder -name=dir -sort lsub "" "*"
fsfolder -name=dir subscribe foo
fsfolder -name=dir -sort lsub "" "*"
-fsfolder -name=dir subscribe baz subscribe foo/baz subscribe foo/bar
+fsfolder -name=dir subscribe baz \; subscribe foo/baz \; subscribe foo/bar
fsfolder -name=dir -sort lsub "" "*"
fsfolder -name=dir -sort lsub foo "*"
fsfolder -name=dir -sort lsub "" 'foo*'
diff --git a/libmailutils/tests/linetrack.at b/libmailutils/tests/linetrack.at
index 3996138..4a4cea3 100644
--- a/libmailutils/tests/linetrack.at
+++ b/libmailutils/tests/linetrack.at
@@ -54,11 +54,11 @@ liber:4.15-18: Naso
TRACKTEST([retreat],[],[3],
[
agnosco
-#retreat 4
+.retreat 4
veteris
vestigia\n
flamme
-#retreat 8
+.retreat 8
Publius
],
[liber:1.1-7: agnosco
@@ -73,7 +73,7 @@ TRACKTEST([retreat over several lines],[],[4],
one\n
two\n
three
-#retreat 11
+.retreat 11
four
],
[liber:1.1-3: one\n
@@ -85,7 +85,7 @@ liber:1.3-6: four
TRACKTEST([retreat to the beginning],[],[4],
[one\n
two\n
-#retreat 8
+.retreat 8
three
],
[liber:1.1-3: one\n
@@ -96,7 +96,7 @@ liber:1.1-5: three
TRACKTEST([too big retreat],[],[2],
[one\n
two\n
-#retreat 10
+.retreat 10
three
],
[liber:1.1-3: one\n
@@ -110,14 +110,14 @@ TRACKTEST([origin 1],[],[10],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#stat
+.stat
],
[liber:1.1-3: one\n
liber:2.1-3: two\n
@@ -136,14 +136,14 @@ TRACKTEST([origin 2],[],[8],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#stat
+.stat
],
[liber:1.1-3: one\n
liber:2.1-3: two\n
@@ -162,14 +162,14 @@ TRACKTEST([origin 3],[],[7],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#stat
+.stat
],
[liber:1.1-3: one\n
liber:2.1-3: two\n
@@ -188,14 +188,14 @@ TRACKTEST([origin 4],[],[6],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#stat
+.stat
],
[liber:1.1-3: one\n
liber:2.1-3: two\n
@@ -214,16 +214,16 @@ TRACKTEST([retreat over origin],[],[9],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#retreat 17
+.retreat 17
nine
-#stat
+.stat
],[liber:1.1-3: one\n
liber:2.1-3: two\n
liber:3.1-5: three\n
@@ -242,16 +242,16 @@ TRACKTEST([retreat over two origins],[],[9],
[one\n
two\n
three\n
-#origin B 5 0
+.origin B 5 0
four\n
five\n
-#origin C 2 0
+.origin C 2 0
six\n
seven\n
eight\n
-#retreat 32
+.retreat 32
nine
-#stat
+.stat
],[liber:1.1-3: one\n
liber:2.1-3: two\n
liber:3.1-5: three\n
@@ -268,7 +268,7 @@ n_chars=12
TRACKTEST([rebase],[],[9],
[one
-#rebase archivum 5 3
+.rebase archivum 5 3
two
],
[liber:1.1-3: one
@@ -279,9 +279,9 @@ TRACKTEST([#line directive],[],[9],
[agnosco
veteris\n
vestigia
-#line 20
+.line 20
flamme\n
-#retreat 8
+.retreat 8
Naso
],
[liber:1.1-7: agnosco
diff --git a/libmailutils/tests/linetrack.c b/libmailutils/tests/linetrack.c
index 4824026..d545227 100644
--- a/libmailutils/tests/linetrack.c
+++ b/libmailutils/tests/linetrack.c
@@ -1,7 +1,8 @@
#include <mailutils/mailutils.h>
#include <mailutils/locus.h>
+#include "tesh.h"
-int
+static int
getnum (char const *arg, unsigned *ret)
{
char *end;
@@ -15,9 +16,10 @@ getnum (char const *arg, unsigned *ret)
return 0;
}
-static void
-com_retreat (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_retreat (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
unsigned x;
if (getnum (argv[1], &x) == 0)
{
@@ -27,56 +29,64 @@ com_retreat (mu_linetrack_t trk, size_t argc, char **argv)
else if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_linetrack_retreat", argv[1], rc);
}
+ return 0;
}
-static void
-com_origin (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_origin (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
int rc;
struct mu_locus_point pt;
pt.mu_file = argv[1];
if (getnum (argv[2], &pt.mu_line))
- return;
+ return 0;
if (getnum (argv[3], &pt.mu_col))
- return;
+ return 0;
rc = mu_linetrack_origin (trk, &pt);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_linetrack_origin", NULL, rc);
+ return 0;
}
-static void
-com_line (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_line (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
int rc;
struct mu_locus_point pt = MU_LOCUS_POINT_INITIALIZER;
if (getnum (argv[1], &pt.mu_line))
- return;
+ return 0;
rc = mu_linetrack_origin (trk, &pt);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_linetrack_origin", NULL, rc);
+ return 0;
}
-static void
-com_rebase (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_rebase (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
int rc;
struct mu_locus_point pt;
pt.mu_file = argv[1];
if (getnum (argv[2], &pt.mu_line))
- return;
+ return 0;
if (getnum (argv[3], &pt.mu_col))
- return;
+ return 0;
rc = mu_linetrack_rebase (trk, &pt);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_linetrack_rebase", NULL, rc);
+ return 0;
}
-static void
-com_point (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_point (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
struct mu_locus_range lr = MU_LOCUS_RANGE_INITIALIZER;
int rc;
@@ -88,17 +98,21 @@ com_point (mu_linetrack_t trk, size_t argc, char **argv)
mu_stream_lprintf (mu_strout, &lr, "%s\n", argv[0]);
mu_locus_range_deinit (&lr);
}
+ return 0;
}
-static void
-com_bol_p (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_bol_p (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
mu_printf ("%d\n", mu_linetrack_at_bol (trk));
+ return 0;
}
-static void
-com_stat (mu_linetrack_t trk, size_t argc, char **argv)
+static int
+com_stat (int argc, char **argv, mu_assoc_t options, void *env)
{
+ mu_linetrack_t trk = env;
int rc;
struct mu_linetrack_stat st;
@@ -111,23 +125,44 @@ com_stat (mu_linetrack_t trk, size_t argc, char **argv)
mu_printf ("n_lines=%zu\n", st.n_lines);
mu_printf ("n_chars=%zu\n", st.n_chars);
}
+ return 0;
}
-
-struct command
+
+static int
+lineproc (int argc, char **argv, mu_assoc_t options, void *env)
{
- char *name;
- size_t argc;
- void (*fun) (mu_linetrack_t trk, size_t argc, char **argv);
-};
+ char *buf = argv[0];
+ mu_linetrack_t trk = env;
+ struct mu_locus_range lr = MU_LOCUS_RANGE_INITIALIZER;
+ char *tok;
-static struct command comtab[] = {
- { "retreat", 2, com_retreat },
- { "origin", 4, com_origin },
- { "line", 2, com_line },
- { "point", 1, com_point },
- { "rebase", 4, com_rebase },
- { "bol", 1, com_bol_p },
- { "stat", 1, com_stat },
+ if (buf[0] == 0)
+ return 0;
+ if (buf[0] == '.')
+ {
+ /* command escape */
+ memmove (buf, buf + 1, strlen (buf));
+ return MU_ERR_USER0;
+ }
+
+ mu_c_str_unescape (buf, "\\\n", "\\n", &tok);
+ mu_linetrack_advance (trk, &lr, tok, strlen (tok));
+ free (tok);
+ mu_stream_lprintf (mu_strout, &lr, "%s\n", buf);
+ mu_locus_range_deinit (&lr);
+ return 0;
+}
+
+
+static struct mu_tesh_command comtab[] = {
+ { "__LINEPROC__", "", lineproc },
+ { "retreat", "COUNT", com_retreat },
+ { "origin", "FILE LINE COL", com_origin },
+ { "line", "NUMBER", com_line },
+ { "point", "NUMBER", com_point },
+ { "rebase", "FILE LINE COL", com_rebase },
+ { "bol", "", com_bol_p },
+ { "stat", "", com_stat },
{ NULL }
};
@@ -137,16 +172,8 @@ main (int argc, char **argv)
unsigned long max_lines;
char *end;
mu_linetrack_t trk;
- int rc;
- char *buf = NULL;
- size_t size, n;
- struct mu_wordsplit ws;
- int wsf = MU_WRDSF_NOVAR | MU_WRDSF_NOCMD
- | MU_WRDSF_SHOWERR | MU_WRDSF_ENOMEMABRT;
-
- mu_set_program_name (argv[0]);
- mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
+ mu_tesh_init (argv[0]);
if (argc != 3)
{
mu_error ("usage: %s FILE LINES", mu_program_name);
@@ -160,40 +187,9 @@ main (int argc, char **argv)
}
MU_ASSERT (mu_linetrack_create (&trk, argv[1], max_lines));
- while ((rc = mu_stream_getline (mu_strin, &buf, &size, &n)) == 0 && n > 0)
- {
- char *tok;
-
- n = mu_rtrim_class (buf, MU_CTYPE_SPACE);
- if (n == 0)
- continue;
- if (buf[0] == '#')
- {
- struct command *com;
-
- mu_wordsplit (buf+1, &ws, wsf);
- wsf |= MU_WRDSF_REUSE;
-
- for (com = comtab; com->name; com++)
- if (strcmp (com->name, ws.ws_wordv[0]) == 0
- && com->argc == ws.ws_wordc)
- break;
- if (com->name)
- com->fun (trk, ws.ws_wordc, ws.ws_wordv);
- else
- mu_error ("unrecognized command");
- }
- else
- {
- struct mu_locus_range lr = MU_LOCUS_RANGE_INITIALIZER;
-
- mu_c_str_unescape (buf, "\\\n", "\\n", &tok);
- mu_linetrack_advance (trk, &lr, tok, strlen (tok));
- free (tok);
- mu_stream_lprintf (mu_strout, &lr, "%s\n", buf);
- mu_locus_range_deinit (&lr);
- }
- }
+
+ mu_tesh_read_and_eval (argc - 3, argv + 3, comtab, trk);
+
mu_linetrack_destroy (&trk);
return 0;
}
diff --git a/libmailutils/tests/list.at b/libmailutils/tests/list.at
index 057fa07..fc2e3da 100644
--- a/libmailutils/tests/list.at
+++ b/libmailutils/tests/list.at
@@ -48,7 +48,7 @@ fem
TESTLIST([insert after],[],
[add en tre fire fem
-ins after en to
+ins -after en to
print],
[# items: 5
en
@@ -60,7 +60,7 @@ fem
TESTLIST([insert before],[],
[add en tre fire fem
-ins before tre to
+ins -before tre to
print],
[# items: 5
en
@@ -118,7 +118,7 @@ en
TESTLIST([pop-null],[],
[pop],
[],
-[mu_list_pop: Requested item not found
+[listop: mu_list_pop() failed: Requested item not found
])
TESTLIST([get],[],
diff --git a/libmailutils/tests/listop.c b/libmailutils/tests/listop.c
index b5b6336..05dd0c4 100644
--- a/libmailutils/tests/listop.c
+++ b/libmailutils/tests/listop.c
@@ -21,18 +21,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <mailutils/mailutils.h>
+#include "tesh.h"
static int interactive;
void
lperror (char *text, int rc)
{
- fprintf (stderr, "%s: %s\n", text, mu_strerror (rc));
+ mu_error ("%s: %s", text, mu_strerror (rc));
exit (1);
}
+#define NITR 4
+
+struct listop_closure
+{
+ mu_list_t lst;
+ mu_iterator_t itr[NITR];
+ int num;
+};
+
+static void
+listop_invalidate_iterators (struct listop_closure *cls)
+{
+ int i;
+
+ for (i = 0; i < NITR; i++)
+ mu_iterator_destroy (&cls->itr[i]);
+}
+
void
-print (mu_list_t list)
+print_list (mu_list_t list)
{
mu_iterator_t itr;
size_t count;
@@ -46,7 +65,7 @@ print (mu_list_t list)
if (rc)
lperror ("mu_iterator_current", rc);
- printf ("# items: %lu\n", (unsigned long) count);
+ mu_printf ("# items: %lu\n", (unsigned long) count);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
mu_iterator_next (itr))
{
@@ -55,90 +74,98 @@ print (mu_list_t list)
rc = mu_iterator_current (itr, (void**) &text);
if (rc)
lperror ("mu_iterator_current", rc);
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
}
mu_iterator_destroy (&itr);
}
-void
-count (mu_list_t list)
+int
+com_print (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ print_list (cls->lst);
+ return 0;
+}
+
+int
+com_count (int argc, char **argv, mu_assoc_t options, void *env)
+{
+ struct listop_closure *cls = env;
size_t n;
int rc;
- rc = mu_list_count (list, &n);
+ rc = mu_list_count (cls->lst, &n);
if (rc)
lperror ("mu_iterator_current", rc);
else
- printf ("%lu\n", (unsigned long) n);
+ mu_printf ("%lu\n", (unsigned long) n);
+ return 0;
}
-void
-next (mu_iterator_t itr, char *arg)
+int
+com_next (int argc, char **argv, mu_assoc_t options, void *env)
{
- int skip = arg ? strtoul (arg, NULL, 0) : 1;
+ struct listop_closure *cls = env;
+ int skip = argc == 2 ? strtoul (argv[1], NULL, 0) : 1;
if (skip == 0)
- fprintf (stderr, "next arg?\n");
- while (skip--)
- mu_iterator_next (itr);
+ {
+ mu_error ("next arg?");
+ }
+ else
+ {
+ while (skip--)
+ mu_iterator_next (cls->itr[cls->num]);
+ }
+ return 0;
}
-void
-delete (mu_list_t list, int argc, char **argv)
+int
+com_delete (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
int rc;
- if (argc == 1)
- {
- fprintf (stderr, "del arg?\n");
- return;
- }
-
while (--argc)
{
rc = mu_list_remove (list, *++argv);
if (rc)
- fprintf (stderr, "mu_list_remove(%s): %s\n", *argv, mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_remove", *argv, rc);
}
+ return 0;
}
-void
-add (mu_list_t list, int argc, char **argv)
+int
+com_add (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
int rc;
- if (argc == 1)
- {
- fprintf (stderr, "add arg?\n");
- return;
- }
-
while (--argc)
{
rc = mu_list_append (list, strdup (*++argv));
if (rc)
- fprintf (stderr, "mu_list_append: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", *argv, rc);
}
+ return 0;
}
-void
-prep (mu_list_t list, int argc, char **argv)
+int
+com_prep (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
int rc;
- if (argc == 1)
- {
- fprintf (stderr, "add arg?\n");
- return;
- }
-
while (--argc)
{
rc = mu_list_prepend (list, strdup (*++argv));
if (rc)
- fprintf (stderr, "mu_list_append: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_prepend", *argv, rc);
}
+ return 0;
}
static mu_list_t
@@ -150,7 +177,7 @@ read_list (int argc, char **argv)
rc = mu_list_create (&list);
if (rc)
{
- fprintf (stderr, "creating temp list: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
return NULL;
}
mu_list_set_destroy_item (list, mu_list_free_item);
@@ -159,71 +186,55 @@ read_list (int argc, char **argv)
rc = mu_list_append (list, strdup (*argv));
if (rc)
{
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", *argv, rc);
mu_list_destroy (&list);
- fprintf (stderr, "adding to temp list: %s\n", mu_strerror (rc));
break;
}
}
return list;
}
-void
-ins (mu_list_t list, int argc, char **argv)
+int
+com_ins (int argc, char **argv, mu_assoc_t options, void *env)
{
- int an;
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
int rc;
char *item;
int insert_before = 0;
-
- if (argc < 3)
- {
- fprintf (stderr, "ins [before] item new_item [new_item*]?\n");
- return;
- }
- an = 1;
- if (strcmp (argv[1], "before") == 0)
- {
- an++;
- insert_before = 1;
- }
- else if (strcmp (argv[1], "after") == 0)
- {
- an++;
- insert_before = 0;
- }
+ if (mu_assoc_lookup (options, "before", NULL) == 0)
+ insert_before = 1;
- item = argv[an++];
+ item = argv[1];
- if (an + 1 == argc)
- rc = mu_list_insert (list, item, strdup (argv[an]), insert_before);
+ if (3 == argc)
+ rc = mu_list_insert (list, item, strdup (argv[2]), insert_before);
else
{
- mu_list_t tmp = read_list (argc - an, argv + an);
+ mu_list_t tmp = read_list (argc - 2, argv + 2);
if (!tmp)
- return;
+ return 0;
rc = mu_list_insert_list (list, item, tmp, insert_before);
mu_list_destroy (&tmp);
}
if (rc)
lperror ("mu_list_insert", rc);
+ return 0;
}
-void
-repl (mu_list_t list, int argc, char **argv)
+int
+com_repl (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
int rc;
- if (argc != 3)
- {
- fprintf (stderr, "repl src dst?\n");
- return;
- }
-
rc = mu_list_replace (list, argv[1], strdup (argv[2]));
if (rc)
- fprintf (stderr, "mu_list_replace: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_replace", NULL, rc);
+ return 0;
}
void
@@ -234,14 +245,14 @@ ictl_tell (mu_iterator_t itr, int argc)
if (argc)
{
- fprintf (stderr, "ictl tell?\n");
+ mu_error ("ictl tell?");
return;
}
rc = mu_iterator_ctl (itr, mu_itrctl_tell, &pos);
if (rc)
lperror ("mu_iterator_ctl", rc);
- printf ("%lu\n", (unsigned long) pos);
+ mu_printf ("%lu\n", (unsigned long) pos);
}
void
@@ -251,7 +262,7 @@ ictl_del (mu_iterator_t itr, int argc)
if (argc)
{
- fprintf (stderr, "ictl del?\n");
+ mu_error ("ictl del?");
return;
}
rc = mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
@@ -259,20 +270,21 @@ ictl_del (mu_iterator_t itr, int argc)
lperror ("mu_iterator_ctl", rc);
}
-void
+int
ictl_repl (mu_iterator_t itr, int argc, char **argv)
{
int rc;
if (argc != 1)
{
- fprintf (stderr, "ictl repl item?\n");
- return;
+ mu_error ("ictl repl item?");
+ return 0;
}
rc = mu_iterator_ctl (itr, mu_itrctl_replace, strdup (argv[0]));
if (rc)
lperror ("mu_iterator_ctl", rc);
+ return 0;
}
void
@@ -283,7 +295,7 @@ ictl_dir (mu_iterator_t itr, int argc, char **argv)
if (argc > 1)
{
- fprintf (stderr, "ictl dir [backwards|forwards]?\n");
+ mu_error ("ictl dir [backwards|forwards]?");
return;
}
if (argc == 1)
@@ -294,7 +306,7 @@ ictl_dir (mu_iterator_t itr, int argc, char **argv)
dir = 0;
else
{
- fprintf (stderr, "ictl dir [backwards|forwards]?\n");
+ mu_error ("ictl dir [backwards|forwards]?");
return;
}
rc = mu_iterator_ctl (itr, mu_itrctl_set_direction, &dir);
@@ -306,8 +318,9 @@ ictl_dir (mu_iterator_t itr, int argc, char **argv)
rc = mu_iterator_ctl (itr, mu_itrctl_qry_direction, &dir);
if (rc)
lperror ("mu_iterator_ctl", rc);
- printf ("%s\n", dir ? "backwards" : "forwards");
+ mu_printf ("%s\n", dir ? "backwards" : "forwards");
}
+ return;
}
void
@@ -317,7 +330,7 @@ ictl_ins (mu_iterator_t itr, int argc, char **argv)
if (argc < 1)
{
- fprintf (stderr, "ictl ins item [item*]?\n");
+ mu_error ("ictl ins item [item*]?");
return;
}
@@ -332,17 +345,15 @@ ictl_ins (mu_iterator_t itr, int argc, char **argv)
mu_list_destroy (&tmp);
}
if (rc)
- printf ("%s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_ctl", NULL, rc);
+ return;
}
-void
-ictl (mu_iterator_t itr, int argc, char **argv)
+int
+com_ictl (int argc, char **argv, mu_assoc_t options, void *env)
{
- if (argc == 1)
- {
- fprintf (stderr, "ictl tell|del|repl|ins?\n");
- return;
- }
+ struct listop_closure *cls = env;
+ mu_iterator_t itr = cls->itr[cls->num];
if (strcmp (argv[1], "tell") == 0)
ictl_tell (itr, argc - 2);
@@ -355,78 +366,88 @@ ictl (mu_iterator_t itr, int argc, char **argv)
else if (strcmp (argv[1], "dir") == 0)
ictl_dir (itr, argc - 2, argv + 2);
else
- fprintf (stderr, "unknown subcommand\n");
+ mu_error ("unknown subcommand");
+ return 0;
}
-#define NITR 4
-
int
-iter (int *pnum, int argc, char **argv)
+com_iter (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
int n;
-
- if (argc != 2)
- {
- fprintf (stderr, "iter num?\n");
- return 1;
- }
n = strtoul (argv[1], NULL, 0);
if (n < 0 || n >= NITR)
{
- fprintf (stderr, "iter [0-3]?\n");
+ mu_error ("iter [0-3]?");
return 1;
}
- *pnum = n;
+
+ if (!cls->itr[n])
+ {
+ int rc = mu_list_get_iterator (cls->lst, &cls->itr[n]);
+ if (rc)
+ lperror ("mu_list_get_iterator", rc);
+ mu_iterator_first (cls->itr[n]);
+ }
+ cls->num = n;
return 0;
}
-void
-find (mu_iterator_t itr, char *arg)
+int
+com_find (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_iterator_t itr = cls->itr[cls->num];
char *text;
- if (!arg)
- {
- fprintf (stderr, "find item?\n");
- return;
- }
-
mu_iterator_current (itr, (void**)&text);
for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
{
char *item;
mu_iterator_current (itr, (void**)&item);
- if (strcmp (arg, item) == 0)
- return;
+ if (strcmp (argv[1], item) == 0)
+ return 0;
}
- fprintf (stderr, "%s not in list\n", arg);
+ mu_error ("%s not in list", argv[1]);
+ return 0;
}
-void
-cur (int num, mu_iterator_t itr)
+int
+com_cur (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
+ mu_iterator_t itr = cls->itr[cls->num];
char *text;
size_t pos;
int rc;
- printf ("%lu:", (unsigned long) num);
+ mu_printf ("%lu:", (unsigned long) cls->num);
rc = mu_iterator_ctl (itr, mu_itrctl_tell, &pos);
if (rc == MU_ERR_NOENT)
{
- printf ("iterator not initialized\n");
- return;
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_iterator_ctl", NULL, rc);
+ return 0;
}
if (rc)
lperror ("mu_iterator_ctl", rc);
- printf ("%lu:", (unsigned long) pos);
+ mu_printf ("%lu:", (unsigned long) pos);
rc = mu_iterator_current (itr, (void**) &text);
if (rc)
lperror ("mu_iterator_current", rc);
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
+ return 0;
+}
+
+int
+com_first (int argc, char **argv, mu_assoc_t options, void *env)
+{
+ struct listop_closure *cls = env;
+ mu_iterator_first (cls->itr[cls->num]);
+ return 0;
}
static int
@@ -514,26 +535,17 @@ map_trim (void **itmv, size_t itmc, void *call_data)
}
int
-map (mu_list_t *plist, int argc, char **argv)
+com_map (int argc, char **argv, mu_assoc_t options, void *env)
{
- mu_list_t list = *plist;
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
mu_list_t result;
int rc;
int replace = 0;
- if (argc > 1 && strcmp (argv[1], "-replace") == 0)
- {
- replace = 1;
- argc--;
- argv++;
- }
+ if (mu_assoc_lookup (options, "replace", NULL) == 0)
+ replace = 1;
- if (argc < 2)
- {
- fprintf (stderr, "map [-replace] even|odd|concat|keep|trim\n");
- return 0;
- }
-
if (strcmp (argv[1], "even") == 0)
{
int n = 0;
@@ -551,7 +563,7 @@ map (mu_list_t *plist, int argc, char **argv)
if (argc < 3 || argc > 4)
{
- fprintf (stderr, "map concat NUM [DELIM]?\n");
+ mu_error ("map concat NUM [DELIM]");
return 0;
}
num = atoi (argv[2]);
@@ -566,7 +578,7 @@ map (mu_list_t *plist, int argc, char **argv)
if (argc < 3 || argc > 4)
{
- fprintf (stderr, "map skip NUM?\n");
+ mu_error ("map skip NUM");
return 0;
}
td.n = 0;
@@ -579,7 +591,7 @@ map (mu_list_t *plist, int argc, char **argv)
if (argc < 3 || argc > 4)
{
- fprintf (stderr, "map trim NUM?\n");
+ mu_error ("map trim NUM");
return 0;
}
td.n = 0;
@@ -588,13 +600,13 @@ map (mu_list_t *plist, int argc, char **argv)
}
else
{
- mu_error ("unknown map name\n");
+ mu_error ("unknown map name");
return 0;
}
if (rc)
{
- mu_error ("map failed: %s", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_map", NULL, rc);
return 0;
}
@@ -606,15 +618,15 @@ map (mu_list_t *plist, int argc, char **argv)
mu_list_count (list, &count[0]);
mu_list_count (result, &count[1]);
- printf ("%lu in, %lu out\n", (unsigned long) count[0],
- (unsigned long) count[1]);
+ mu_printf ("%lu in, %lu out\n", (unsigned long) count[0],
+ (unsigned long) count[1]);
mu_list_destroy (&list);
- *plist = result;
- return 1;
+ cls->lst = result;
+ listop_invalidate_iterators (cls);
}
else
{
- print (result);
+ print_list (result);
mu_list_destroy (&result);
}
return 0;
@@ -628,29 +640,20 @@ dup_string (void **res, void *itm, void *closure)
}
int
-slice (mu_list_t *plist, int argc, char **argv)
+com_slice (int argc, char **argv, mu_assoc_t options, void *env)
{
- mu_list_t list = *plist;
+ struct listop_closure *cls = env;
+ mu_list_t list = cls->lst;
mu_list_t result;
int rc, i;
int replace = 0;
size_t *buf;
+ if (mu_assoc_lookup (options, "replace", NULL) == 0)
+ replace = 1;
+
argc--;
argv++;
-
- if (argc > 0 && strcmp (argv[0], "-replace") == 0)
- {
- replace = 1;
- argc--;
- argv++;
- }
-
- if (argc < 1)
- {
- fprintf (stderr, "slice [-replace] num [num...]\n");
- return 0;
- }
buf = calloc (argc, sizeof (buf[0]));
if (!buf)
@@ -658,11 +661,10 @@ slice (mu_list_t *plist, int argc, char **argv)
for (i = 0; i < argc; i++)
buf[i] = atoi (argv[i]);
- rc = mu_list_slice_dup (&result, list, buf, argc,
- dup_string, NULL);
+ rc = mu_list_slice_dup (&result, list, buf, argc, dup_string, NULL);
if (rc)
{
- mu_error ("slice failed: %s", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_slice_dup", NULL, rc);
return 0;
}
if (replace)
@@ -671,54 +673,48 @@ slice (mu_list_t *plist, int argc, char **argv)
mu_list_count (list, &count[0]);
mu_list_count (result, &count[1]);
- printf ("%lu in, %lu out\n", (unsigned long) count[0],
- (unsigned long) count[1]);
+ mu_printf ("%lu in, %lu out\n", (unsigned long) count[0],
+ (unsigned long) count[1]);
mu_list_destroy (&list);
- *plist = result;
- return 1;
+ cls->lst = result;
+ listop_invalidate_iterators (cls);
}
else
{
- print (result);
+ print_list (result);
mu_list_destroy (&result);
}
return 0;
}
-void
-head (size_t argc, mu_list_t list)
+int
+com_head (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
int rc;
- const char *text;
-
- if (argc != 1)
- {
- fprintf (stderr, "head ?\n");
- return;
- }
- rc = mu_list_head (list, (void**) &text);
+ char *text;
+
+ rc = mu_list_head (cls->lst, (void**) &text);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_head", NULL, rc);
else
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
+ return 0;
}
-void
-tail (size_t argc, mu_list_t list)
+int
+com_tail (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
int rc;
const char *text;
- if (argc != 1)
- {
- fprintf (stderr, "head ?\n");
- return;
- }
- rc = mu_list_tail (list, (void**) &text);
+ rc = mu_list_tail (cls->lst, (void**) &text);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_tail", NULL, rc);
else
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
+ return 0;
}
static int
@@ -739,268 +735,149 @@ fold_concat (void *item, void *data, void *prev, void **ret)
return 0;
}
-void
-fold (mu_list_t list)
+int
+com_fold (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
char *text = NULL;
int rc;
- rc = mu_list_fold (list, fold_concat, NULL, NULL, &text);
+ rc = mu_list_fold (cls->lst, fold_concat, NULL, NULL, &text);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_fold", NULL, rc);
else if (text)
{
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
free (text);
}
else
- printf ("NULL\n");
+ mu_printf ("NULL\n");
+ return 0;
}
-void
-rfold (mu_list_t list)
+int
+com_rfold (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
char *text = NULL;
int rc;
- rc = mu_list_rfold (list, fold_concat, NULL, NULL, &text);
+ rc = mu_list_rfold (cls->lst, fold_concat, NULL, NULL, &text);
if (rc)
mu_diag_funcall (MU_DIAG_ERROR, "mu_list_fold", NULL, rc);
else if (text)
{
- printf ("%s\n", text);
+ mu_printf ("%s\n", text);
free (text);
}
else
- printf ("NULL\n");
+ mu_printf ("NULL\n");
+ return 0;
}
-void
-sort (mu_list_t list)
+int
+com_sort (int argc, char **argv, mu_assoc_t options, void *env)
{
- mu_list_sort (list, NULL);
+ struct listop_closure *cls = env;
+ mu_list_sort (cls->lst, NULL);
+ listop_invalidate_iterators (cls);
+ return 0;
}
-void
-push (mu_list_t list, int argc, char **argv)
+int
+com_push (int argc, char **argv, mu_assoc_t options, void *env)
{
- if (argc < 2)
- {
- fprintf (stderr, "push arg [arg] ?\n");
- return;
- }
-
+ struct listop_closure *cls = env;
while (--argc)
{
- int rc = mu_list_push (list, strdup (*++argv));
+ int rc = mu_list_push (cls->lst, strdup (*++argv));
if (rc)
- fprintf (stderr, "mu_list_push: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_push", *argv, rc);
}
+ return 0;
}
-void
-pop (mu_list_t list, int argc, char **argv)
+int
+com_pop (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct listop_closure *cls = env;
char *text;
int rc;
- if (argc != 1)
- {
- fprintf (stderr, "pop ?\n");
- return;
- }
-
- rc = mu_list_pop (list, (void**) &text);
+ rc = mu_list_pop (cls->lst, (void**) &text);
if (rc)
- fprintf (stderr, "mu_list_pop: %s\n", mu_strerror (rc));
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_pop", NULL, rc);
else
- printf ("%s\n", text);
-}
-
-void
-help ()
-{
- printf ("count\n");
- printf ("cur\n");
- printf ("next [count]\n");
- printf ("first\n");
- printf ("find item\n");
- printf ("del item [item*]\n");
- printf ("add item [item*]\n");
- printf ("prep item [item*]\n");
- printf ("repl old_item new_item\n");
- printf ("ins [before|after] item new_item [new_item*]\n");
- printf ("ictl tell\n");
- printf ("ictl del\n");
- printf ("ictl repl item\n");
- printf ("ictl ins item [item*]\n");
- printf ("ictl dir [backwards|forwards]\n");
- printf ("map [-replace] NAME [ARGS]\n");
- printf ("fold\n");
- printf ("rfold\n");
- printf ("print\n");
- printf ("slice [-replace] num [num...]\n");
- printf ("quit\n");
- printf ("iter num\n");
- printf ("help\n");
- printf ("head\n");
- printf ("tail\n");
- printf ("push item\n");
- printf ("pop\n");
- printf ("sort\n");
- printf ("NUMBER\n");
+ mu_printf ("%s\n", text);
+ return 0;
}
-void
-shell (mu_list_t list)
+int
+envinit (int argc, char **argv, mu_assoc_t options, void *env)
{
- int num = 0;
- mu_iterator_t itr[NITR];
- int rc;
+ struct listop_closure *cls = env;
- memset (&itr, 0, sizeof itr);
- num = 0;
- while (1)
+ if (!cls->itr[cls->num])
{
- char *text = NULL;
- char buf[80];
- struct mu_wordsplit ws;
-
- if (!itr[num])
- {
- rc = mu_list_get_iterator (list, &itr[num]);
- if (rc)
- lperror ("mu_list_get_iterator", rc);
- mu_iterator_first (itr[num]);
- }
-
- rc = mu_iterator_current (itr[num], (void**) &text);
+ int rc = mu_list_get_iterator (cls->lst, &cls->itr[cls->num]);
if (rc)
- lperror ("mu_iterator_current", rc);
-
- if (interactive)
- printf ("%d:(%s)> ", num, text ? text : "NULL");
- if (fgets (buf, sizeof buf, stdin) == NULL)
- return;
+ lperror ("mu_list_get_iterator", rc);
+ mu_iterator_first (cls->itr[cls->num]);
+ }
+ return 0;
+}
- ws.ws_comment = "#";
- if (mu_wordsplit (buf, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT))
- {
- mu_error ("cannot split line `%s': %s", buf,
- mu_wordsplit_strerror (&ws));
- exit (1);
- }
+int
+get (int argc, char **argv, mu_assoc_t options, void *env)
+{
+ struct listop_closure *cls = env;
+ char *p;
+ size_t n;
- if (ws.ws_wordc > 0)
- {
- if (strcmp (ws.ws_wordv[0], "count") == 0)
- count (list);
- else if (strcmp (ws.ws_wordv[0], "next") == 0)
- next (itr[num], ws.ws_wordv[1]);
- else if (strcmp (ws.ws_wordv[0], "first") == 0)
- mu_iterator_first (itr[num]);
- else if (strcmp (ws.ws_wordv[0], "del") == 0)
- delete (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "add") == 0)
- add (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "prep") == 0)
- prep (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "ins") == 0)
- ins (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "repl") == 0)
- repl (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "ictl") == 0)
- ictl (itr[num], ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "print") == 0)
- print (list);
- else if (strcmp (ws.ws_wordv[0], "cur") == 0)
- cur (num, itr[num]);
- else if (strcmp (ws.ws_wordv[0], "fold") == 0)
- fold (list);
- else if (strcmp (ws.ws_wordv[0], "rfold") == 0)
- rfold (list);
- else if (strcmp (ws.ws_wordv[0], "map") == 0)
- {
- int i;
-
- if (map (&list, ws.ws_wordc, ws.ws_wordv))
- for (i = 0; i < NITR; i++)
- mu_iterator_destroy (&itr[i]);
- }
- else if (strcmp (ws.ws_wordv[0], "slice") == 0)
- {
- int i;
-
- if (slice (&list, ws.ws_wordc, ws.ws_wordv))
- for (i = 0; i < NITR; i++)
- mu_iterator_destroy (&itr[i]);
- }
- else if (strcmp (ws.ws_wordv[0], "quit") == 0)
- return;
- else if (strcmp (ws.ws_wordv[0], "iter") == 0)
- {
- int n;
- if (iter (&n, ws.ws_wordc, ws.ws_wordv) == 0 && !itr[n])
- {
- rc = mu_list_get_iterator (list, &itr[n]);
- if (rc)
- lperror ("mu_list_get_iterator", rc);
- mu_iterator_first (itr[n]);
- }
- num = n;
- }
- else if (strcmp (ws.ws_wordv[0], "close") == 0)
- {
- int n;
- if (iter (&n, ws.ws_wordc, ws.ws_wordv) == 0)
- {
- mu_iterator_destroy (&itr[n]);
- if (n == num && ++num == NITR)
- num = 0;
- }
- }
- else if (strcmp (ws.ws_wordv[0], "find") == 0)
- find (itr[num], ws.ws_wordv[1]);
- else if (strcmp (ws.ws_wordv[0], "help") == 0)
- help ();
- else if (strcmp (ws.ws_wordv[0], "head") == 0)
- head (ws.ws_wordc, list);
- else if (strcmp (ws.ws_wordv[0], "tail") == 0)
- tail (ws.ws_wordc, list);
- else if (strcmp (ws.ws_wordv[0], "push") == 0)
- push (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "pop") == 0)
- pop (list, ws.ws_wordc, ws.ws_wordv);
- else if (strcmp (ws.ws_wordv[0], "sort") == 0)
- {
- int i;
- sort (list);
-
- for (i = 0; i < NITR; i++)
- mu_iterator_destroy (&itr[i]);
- }
- else if (ws.ws_wordc == 1)
- {
- char *p;
- size_t n = strtoul (ws.ws_wordv[0], &p, 0);
- if (*p != 0)
- fprintf (stderr, "?\n");
- else
- {
- rc = mu_list_get (list, n, (void**) &text);
- if (rc)
- fprintf (stderr, "mu_list_get: %s\n", mu_strerror (rc));
- else
- printf ("%s\n", text);
- }
- }
- else
- fprintf (stderr, "?\n");
- }
- mu_wordsplit_free (&ws);
+ errno = 0;
+ n = strtoul (argv[0], &p, 0);
+ if (errno || *p != 0)
+ return MU_ERR_PARSE;
+ else
+ {
+ char *text;
+ int rc = mu_list_get (cls->lst, n, (void**) &text);
+ if (rc)
+ mu_diag_funcall (MU_DIAG_ERROR, "mu_list_get", argv[0], rc);
+ else
+ mu_printf ("%s\n", text);
+ return 0;
}
}
+static struct mu_tesh_command comtab[] = {
+ { "__ENVINIT__", "", envinit },
+ { "__NOCMD__", "", get },
+ { "print", "", com_print },
+ { "count", "", com_count },
+ { "next", "[COUNT]", com_next },
+ { "del", "ITEM ...", com_delete },
+ { "add", "ITEM ...", com_add },
+ { "prep", "ITEM ...", com_prep },
+ { "ins", "[-before] [-after] ITEM NEW_ITEM ...", com_ins },
+ { "repl", "OLD_ITEM NEW_ITEM", com_repl },
+ { "ictl", "tell|del|repl|ins|dir [ARG...]", com_ictl },
+ { "iter", "NUM", com_iter },
+ { "find", "ITEM", com_find },
+ { "cur", "", com_cur },
+ { "map", "[-replace] NAME [ARG...]", com_map },
+ { "slice", "[-replace] NUM ...", com_slice },
+ { "first", "", com_first },
+ { "head", "", com_head },
+ { "tail", "", com_tail },
+ { "fold", "", com_fold },
+ { "rfold", "", com_rfold },
+ { "sort", "", com_sort },
+ { "push", "ITEM ...", com_push },
+ { "pop", "", com_pop },
+ { NULL }
+};
+
static int
string_comp (const void *item, const void *value)
{
@@ -1010,27 +887,29 @@ string_comp (const void *item, const void *value)
int
main (int argc, char **argv)
{
- mu_list_t list;
+ struct listop_closure cls;
int rc;
+ mu_tesh_init (argv[0]);
+
interactive = isatty (0);
- rc = mu_list_create (&list);
+
+ memset (&cls, 0, sizeof cls);
+
+ rc = mu_list_create (&cls.lst);
if (rc)
lperror ("mu_list_create", rc);
- mu_list_set_comparator (list, string_comp);
- mu_list_set_destroy_item (list, mu_list_free_item);
+ mu_list_set_comparator (cls.lst, string_comp);
+ mu_list_set_destroy_item (cls.lst, mu_list_free_item);
- argc--;
- argv++;
-
- while (argc--)
+ while (--argc)
{
- rc = mu_list_append (list, strdup (*argv++));
+ rc = mu_list_append (cls.lst, strdup (*++argv));
if (rc)
lperror ("mu_list_append", rc);
}
- shell (list);
+ mu_tesh_read_and_eval (argc, argv, comtab, &cls);
return 0;
}
diff --git a/libmailutils/tests/tesh.c b/libmailutils/tests/tesh.c
new file mode 100644
index 0000000..6fe7000
--- a/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);
+}
diff --git a/libmailutils/tests/tesh.h b/libmailutils/tests/tesh.h
new file mode 100644
index 0000000..dbab97c
--- a/dev/null
+++ b/libmailutils/tests/tesh.h
@@ -0,0 +1,176 @@
+/* Definitions of test shell functions for GNU Mailutils.
+ Copyright (C) 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/>. */
+
+/* Test shell commands are implemented via mu_tesh_function_t functions.
+ Arguments:
+ argc - argument count
+ argv - argument vector
+ options - assoc of options
+ env - evaluation environment (closure)
+*/
+typedef int (*mu_tesh_function_t)(int argc, char **argv, mu_assoc_t options,
+ void *env);
+
+/* Option argument types */
+enum mu_tesh_arg
+ {
+ mu_tesh_noarg, /* No argument allowed */
+ mu_tesh_arg_required, /* Argument is required */
+ mu_tesh_arg_optional /* Argument is optional */
+ };
+
+/* Test shell command definition */
+struct mu_tesh_command
+{
+ /* The following fields must be filled by the caller */
+ char *verb; /* Command verb [1] */
+ char *args; /* Description of command arguments [2] */
+ mu_tesh_function_t func; /* Command handler function */
+
+ /* The fields below are computed and used by the shell itself.
+ The caller MAY NOT initialize them.
+ */
+
+ int param_min; /* Minimum number of parameters */
+ int param_max; /* Maximum number of parameters. -1 for variadic */
+ mu_assoc_t options; /* Assoc array of the allowed options, indexed by
+ option names without the leading dash. Values
+ are of enum mu_tesh_arg type. */
+};
+
+/* Notes:
+
+ [1] The command verb "help" is implicitly defined. It produces a short
+ summary of the available shell commands. The special commands
+
+ The following special command verbs can be used:
+ __LINEPROC__
+ Called before word splitting. The entire command line is passed
+ as argv[0]. The argc is 1, and the options argument is NULL.
+
+ The function returns 0 to indicate that it has processed the
+ input. In this case, no further input handing will be performed
+ and the shell will proceed to next line from the input.
+ Otherwise, the shell will perform word splitting and initiate
+ further processing.
+
+ The function is free to modify or reassign argv[0] if it needs
+ so. The modified value will serve as input for further processing.
+ If the function chooses to reassing argv[0], it may not free the
+ current value, and the caller remains responsible for any memory
+ management needed. For example, if a newly allocated memory is
+ assigned to argv[0], this pointer must be saved somewhere in the
+ 'env' to be subsequently freed by the __ENVFINI__ handler.
+
+ __NOCMD__
+ Called if the first token in the command line does not match
+ any defined command. The result of word splitting is passed in
+ argc and argv. The options argument is NULL. The function must
+ return 0 if it was able to process the input, and any non-zero
+ value otherwise. In the latter case, the shell will issue the
+ "no such command" diagnostics.
+
+ __ENVINIT__
+ Initialize the environment prior to running the command. The
+ argc, argv, and options arguments contain the result of command
+ line parsing.
+
+ The command must return 0 on success.
+
+ __ENVFINI__
+ Do any post-command cleanup of the environment. Arguments are
+ the same as to __ENVINIT__.
+
+ Return value is ignored.
+
+ __HELPINIT__
+ Print initial part of the help output. This function is called
+ by mu_tesh_help prior to printing the command summary.
+ The argc parameter is 0, argv and options are NULL.
+
+ Return value is ignored.
+
+ __HELPFINI__
+ Print final part of the help output. This function is called
+ by mu_tesh_help prior after printing the command summary.
+
+ Arguments are the same as for __HELPINIT__. Return value is
+ ignored.
+
+ Apart from these, no command verbs may begin and end with two
+ underscore characters.
+
+ [2] The args field in the above structure serves two purposes. First,
+ it describes the options and arguments to the shell command in
+ unambiguous way and is used during command line parsing. Secondly,
+ it is output by mu_tesh_help as a human-readable command line
+ syntax summary.
+
+ The syntax of this field is described by the BNF below:
+
+ <args> ::= <arglist> | <arglist> <ws> "..." |
+ <arglist> "..."
+ <arglist> ::= <argdef> | <arglist> <ws> <argdef>
+ <argdef> ::= <optdef> | <optarg> | STRING
+ <optdef> ::= "[-" STRING "]" |
+ "[-" STRING "=" STRING "]" |
+ "[-" STRING "[=" STRING "]]"
+ <optarg> ::= "[" string-list "]"
+ <string-list> ::= STRING | <string-list> <ws> STRING
+ <ws> ::= 1*WS
+
+ STRING is any contiguous sequence of printable characters, other
+ than whitespace, '[', or ']'. WS is horizontal space or tab
+ character.
+
+ Ellipis can appear immediately before the closing "]" of an <optdef>
+ or <optarg>.
+
+ The <optdef> token defines options that can be supplied to the
+ command. The <optdef>s can be intermixed with other tokens in an
+ <args> string. However, in actual command invocation, options must
+ precede positional arguments. The "--" argument can be given to
+ explicitly stop option processing.
+
+ Both trailing ellipsis and <optarg> indicate variable number of
+ arguments to the command.
+*/
+
+
+/* Initialize the shell. */
+void mu_tesh_init (char const *argv0);
+
+/* Main command evaluation loop.
+
+ If argc > 0, commands are read from argv. They must be delimited by
+ semicolons (obviously, if argc,argv are obtained from the shell command
+ line, the user must take care to escape each ';' to prevent it from
+ being processed by the shell). A semicolon may appear either as a separate
+ argument or as a last character of command's argument, e.g. supposing that
+ 't' is a command line utility using tesh, both invocations below are valid:
+
+ t foo bar \; baz qux
+ t foo 'bar;' baz qux
+
+ If argc == 0, commands are read from the standard input. The '#' character
+ begins an inline comment.
+ */
+void mu_tesh_read_and_eval (int argc, char **argv,
+ struct mu_tesh_command *cmd,
+ void *env);
+
+/* Print a short summary of shell commands. */
+void mu_tesh_help (struct mu_tesh_command *cmd, void *env);
diff --git a/libproto/dotmail/dotmail.c b/libproto/dotmail/dotmail.c
index 41d7e92..48ca756 100644
--- a/libproto/dotmail/dotmail.c
+++ b/libproto/dotmail/dotmail.c
@@ -930,7 +930,7 @@ dotmail_append_message (mu_mailbox_t mailbox, mu_message_t msg)
{
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("%s(%s):%s: %s",
- __func__, dmp->name, "mu_docker_lock",
+ __func__, dmp->name, "mu_locker_lock",
mu_strerror (rc)));
return rc;
}
diff --git a/libproto/dotmail/tests/Makefile.am b/libproto/dotmail/tests/Makefile.am
index 46330db..592b4df 100644
--- a/libproto/dotmail/tests/Makefile.am
+++ b/libproto/dotmail/tests/Makefile.am
@@ -43,6 +43,8 @@ noinst_PROGRAMS = \
dm_mesg\
dm_qget\
dm_detect
+dm_mesg_LDFLAGS = -L$(top_builddir)/libmailutils/tests -lmu_tesh $(LDADD)
+dm_mesg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/libmailutils/tests
LDADD = ${MU_LIB_DOTMAIL} ${MU_LIB_MAILUTILS}
diff --git a/libproto/dotmail/tests/dm_mesg.c b/libproto/dotmail/tests/dm_mesg.c
index 40f1eff..9e6b26b 100644
--- a/libproto/dotmail/tests/dm_mesg.c
+++ b/libproto/dotmail/tests/dm_mesg.c
@@ -15,6 +15,7 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include <mailutils/mailutils.h>
+#include "tesh.h"
unsigned long
get_num (char const *s)
@@ -38,85 +39,105 @@ struct interp_env
size_t msgno;
};
-void
-dm_env_date (struct interp_env *ienv, char **argv)
+int
+dm_env_date (int argc, char **argv, mu_assoc_t options, void *env)
{
- mu_envelope_t env;
+ struct interp_env *ienv = env;
+ mu_envelope_t msg_env;
char *str;
- MU_ASSERT (mu_message_get_envelope (ienv->msg, &env));
- MU_ASSERT (mu_envelope_aget_date (env, &str));
+ MU_ASSERT (mu_message_get_envelope (ienv->msg, &msg_env));
+ MU_ASSERT (mu_envelope_aget_date (msg_env, &str));
mu_printf ("%s", str);
free (str);
+ return 0;
}
-void
-dm_env_sender (struct interp_env *ienv, char **argv)
+int
+dm_env_sender (int argc, char **argv, mu_assoc_t options, void *env)
{
- mu_envelope_t env;
+ struct interp_env *ienv = env;
+ mu_envelope_t msg_env;
char *str;
- MU_ASSERT (mu_message_get_envelope (ienv->msg, &env));
- MU_ASSERT (mu_envelope_aget_sender (env, &str));
+ MU_ASSERT (mu_message_get_envelope (ienv->msg, &msg_env));
+ MU_ASSERT (mu_envelope_aget_sender (msg_env, &str));
mu_printf ("%s", str);
free (str);
+ return 0;
}
-void
-dm_header_lines (struct interp_env *ienv, char **argv)
+int
+dm_header_lines (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
size_t lines;
+
MU_ASSERT (mu_message_get_header (ienv->msg, &hdr));
MU_ASSERT (mu_header_lines (hdr, &lines));
mu_printf ("%lu", (unsigned long) lines);
+ return 0;
}
-void
-dm_header_count (struct interp_env *ienv, char **argv)
+int
+dm_header_count (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
size_t n;
+
MU_ASSERT (mu_message_get_header (ienv->msg, &hdr));
MU_ASSERT (mu_header_get_field_count (hdr, &n));
mu_printf ("%lu", (unsigned long) n);
+ return 0;
}
-void
-dm_header_size (struct interp_env *ienv, char **argv)
+int
+dm_header_size (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
size_t s;
+
MU_ASSERT (mu_message_get_header (ienv->msg, &hdr));
MU_ASSERT (mu_header_size (hdr, &s));
mu_printf ("%lu", (unsigned long) s);
+ return 0;
}
-void
-dm_header_field (struct interp_env *ienv, char **argv)
+int
+dm_header_field (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
- size_t n = get_num (argv[0]);
+ size_t n = get_num (argv[1]);
char const *s;
+
MU_ASSERT (mu_message_get_header (ienv->msg, &hdr));
MU_ASSERT (mu_header_sget_field_name (hdr, n, &s));
mu_printf ("%s", s);
+ return 0;
}
-void
-dm_header_value (struct interp_env *ienv, char **argv)
+int
+dm_header_value (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
- size_t n = get_num (argv[0]);
+ size_t n = get_num (argv[1]);
char const *s;
+
MU_ASSERT (mu_message_get_header (ienv->msg, &hdr));
MU_ASSERT (mu_header_sget_field_value (hdr, n, &s));
mu_printf ("%s", s);
+ return 0;
}
-void
-dm_headers (struct interp_env *ienv, char **argv)
+int
+dm_headers (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_header_t hdr;
char const *name;
char *val;
@@ -131,68 +152,86 @@ dm_headers (struct interp_env *ienv, char **argv)
MU_ASSERT (mu_header_aget_field_value_unfold (hdr, i, &val));
mu_printf ("%s\n", val);
}
+ return 0;
}
-void
-dm_body_lines (struct interp_env *ienv, char **argv)
+int
+dm_body_lines (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_body_t body;
size_t lines;
+
MU_ASSERT (mu_message_get_body (ienv->msg, &body));
MU_ASSERT (mu_body_lines (body, &lines));
mu_printf ("%lu", (unsigned long) lines);
+ return 0;
}
-void
-dm_body_size (struct interp_env *ienv, char **argv)
+int
+dm_body_size (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_body_t body;
size_t s;
+
MU_ASSERT (mu_message_get_body (ienv->msg, &body));
MU_ASSERT (mu_body_size (body, &s));
mu_printf ("%lu", (unsigned long) s);
+ return 0;
}
-void
-dm_body_text (struct interp_env *ienv, char **argv)
+int
+dm_body_text (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_body_t body;
mu_stream_t str;
+
MU_ASSERT (mu_message_get_body (ienv->msg, &body));
MU_ASSERT (mu_body_get_streamref (body, &str));
MU_ASSERT (mu_stream_copy (mu_strout, str, 0, NULL));
mu_stream_unref (str);
+ return 0;
}
-void
-dm_attr (struct interp_env *ienv, char **argv)
+int
+dm_attr (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_attribute_t attr;
char abuf[MU_STATUS_BUF_SIZE];
+
MU_ASSERT (mu_message_get_attribute (ienv->msg, &attr));
MU_ASSERT (mu_attribute_to_string (attr, abuf, sizeof (abuf), NULL));
mu_printf ("%s", abuf[0] ? abuf : "-");
+ return 0;
}
-void
-dm_uid (struct interp_env *ienv, char **argv)
+int
+dm_uid (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
size_t uid;
+
MU_ASSERT (mu_message_get_uid (ienv->msg, &uid));
mu_printf ("%lu", (unsigned long) uid);
+ return 0;
}
#define __cat2__(a,b) a ## b
#define __cat3__(a,b,c) a ## b ## c
#define __cat4__(a,b,c,d) a ## b ## c ## d
-#define ATTR_FUN(op,attr) \
-static void \
-__cat4__(dm_,op,_,attr) (struct interp_env *ienv, char **argv) \
-{ \
- mu_attribute_t a; \
+#define ATTR_FUN(op,attr) \
+static int \
+__cat4__(dm_,op,_,attr) (int argc, char **argv, mu_assoc_t options, void *env) \
+{ \
+ struct interp_env *ienv = env; \
+ mu_attribute_t a; \
MU_ASSERT (mu_message_get_attribute (ienv->msg, &a)); \
- MU_ASSERT (__cat4__(mu_attribute_,op,_,attr)(a)); \
- mu_printf ("OK"); \
+ MU_ASSERT (__cat4__(mu_attribute_,op,_,attr)(a)); \
+ mu_printf ("OK"); \
+ return 0; \
}
ATTR_FUN(set,seen)
@@ -211,181 +250,205 @@ ATTR_FUN(unset,draft)
ATTR_FUN(unset,recent)
ATTR_FUN(unset,read)
-void
-dm_append (struct interp_env *ienv, char **argv)
+int
+dm_append (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
mu_stream_t str;
mu_message_t newmsg;
- MU_ASSERT (mu_file_stream_create (&str, argv[0], MU_STREAM_READ));
+
+ MU_ASSERT (mu_file_stream_create (&str, argv[1], MU_STREAM_READ));
MU_ASSERT (mu_stream_to_message (str, &newmsg));
MU_ASSERT (mu_mailbox_append_message (ienv->mbx, newmsg));
mu_stream_destroy (&str);
mu_printf ("OK");
+ return 0;
}
-void
-dm_expunge (struct interp_env *ienv, char **argv)
+int
+dm_expunge (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
+
MU_ASSERT (mu_mailbox_expunge (ienv->mbx));
mu_printf ("OK");
+ return 0;
}
-void
-dm_sync (struct interp_env *ienv, char **argv)
+int
+dm_sync (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
+
MU_ASSERT (mu_mailbox_sync (ienv->mbx));
mu_printf ("OK");
+ return 0;
}
-void
-dm_count (struct interp_env *ienv, char **argv)
+int
+dm_count (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
size_t n;
+
MU_ASSERT (mu_mailbox_messages_count (ienv->mbx, &n));
mu_printf ("%lu", (unsigned long) n);
+ return 0;
}
-void
-dm_uidvalidity (struct interp_env *ienv, char **argv)
+int
+dm_uidvalidity (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
unsigned long v;
+
MU_ASSERT (mu_mailbox_uidvalidity (ienv->mbx, &v));
mu_printf ("%lu", v);
+ return 0;
}
-void
-dm_uidnext (struct interp_env *ienv, char **argv)
+int
+dm_uidnext (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
size_t n;
+
MU_ASSERT (mu_mailbox_uidnext (ienv->mbx, &n));
mu_printf ("%lu", (unsigned long) n);
+ return 0;
}
-void
-dm_recent (struct interp_env *ienv, char **argv)
+int
+dm_recent (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
size_t n;
+
MU_ASSERT (mu_mailbox_messages_recent (ienv->mbx, &n));
mu_printf ("%lu", (unsigned long) n);
+ return 0;
}
-void
-dm_unseen (struct interp_env *ienv, char **argv)
+int
+dm_unseen (int argc, char **argv, mu_assoc_t options, void *env)
{
+ struct interp_env *ienv = env;
size_t n;
+
MU_ASSERT (mu_mailbox_message_unseen (ienv->mbx, &n));
mu_printf ("%lu", (unsigned long) n);
+ return 0;
}
-typedef void (*dm_action_fn) (struct interp_env *, char **);
-
-struct dm_action
-{
- char *name;
- dm_action_fn fn;
- int needs_message;
- int narg;
+static char const *mbox_actions[] = {
+ "expunge",
+ "sync",
+ "append",
+ "uidvalidity",
+ "uidnext",
+ "count",
+ "recent",
+ "unseen",
+ NULL
};
-struct dm_action actions[] = {
- { "env_date", dm_env_date, 1, 0 },
- { "env_sender", dm_env_sender, 1, 0 },
- { "header_lines", dm_header_lines, 1, 0 },
- { "header_size", dm_header_size, 1, 0 },
- { "header_count", dm_header_count, 1, 0 },
- { "header_field", dm_header_field, 1, 1 },
- { "header_value", dm_header_value, 1, 1 },
- { "headers", dm_headers, 1, 0 },
- { "body_lines", dm_body_lines, 1, 0 },
- { "body_size", dm_body_size, 1, 0 },
- { "body_text", dm_body_text, 1, 0 },
- { "attr", dm_attr, 1, 0 },
- { "uid", dm_uid, 1, 0 },
- { "set_seen", dm_set_seen, 1, 0 },
- { "set_answered", dm_set_answered, 1, 0 },
- { "set_flagged", dm_set_flagged, 1, 0 },
- { "set_deleted", dm_set_deleted, 1, 0 },
- { "set_draft", dm_set_draft, 1, 0 },
- { "set_recent", dm_set_recent, 1, 0 },
- { "set_read", dm_set_read, 1, 0 },
- { "unset_seen", dm_unset_seen, 1, 0 },
- { "unset_answered", dm_unset_answered, 1, 0 },
- { "unset_flagged", dm_unset_flagged, 1, 0 },
- { "unset_deleted", dm_unset_deleted, 1, 0 },
- { "unset_draft", dm_unset_draft, 1, 0 },
- { "unset_recent", dm_unset_recent, 1, 0 },
- { "unset_read", dm_unset_read, 1, 0 },
- { "expunge", dm_expunge, 0, 0 },
- { "sync", dm_sync, 0, 0 },
- { "append", dm_append, 0, 1 },
- { "uidvalidity", dm_uidvalidity, 0, 0 },
- { "uidnext", dm_uidnext, 0, 0 },
- { "count", dm_count, 0, 0 },
- { "recent", dm_recent, 0, 0 },
- { "unseen", dm_unseen, 0, 0 },
- { NULL }
-};
-
-static struct dm_action *
-get_action (char const *s)
+int
+needs_message (char const *name)
{
- size_t i;
-
- for (i = 0; actions[i].name; i++)
- if (strcmp (actions[i].name, s) == 0)
- return &actions[i];
-
- return NULL;
+ int i;
+ for (i = 0; mbox_actions[i]; i++)
+ if (strcmp (mbox_actions[i], name) == 0)
+ return 0;
+ return 1;
}
-
-void
-interpret (struct interp_env *env, int argc, char **argv)
+
+int
+dm_envinit (int argc, char **argv, mu_assoc_t options, void *env)
{
- struct dm_action *act;
-
- if (mu_isdigit (*argv[0]))
+ struct interp_env *ienv = env;
+ if (needs_message (argv[0]))
{
- env->msgno = get_num (argv[0]);
- MU_ASSERT (mu_mailbox_get_message (env->mbx, env->msgno, &env->msg));
- mu_printf ("%lu current message\n", (unsigned long) env->msgno);
- return;
- }
-
- act = get_action (argv[0]);
- if (!act)
- {
- mu_error ("%s: unrecognized action", argv[0]);
- exit (1);
+ if (!ienv->msg)
+ {
+ mu_error ("no message selected");
+ exit (1);
+ }
+ mu_printf ("%lu ", (unsigned long) ienv->msgno);
}
+ mu_printf ("%s: ", argv[0]);
+ return 0;
+}
- if (act->needs_message && !env->msg)
- {
- mu_error ("no message selected");
- exit (1);
- }
+int
+dm_envfini (int argc, char **argv, mu_assoc_t options, void *env)
+{
+ mu_printf ("\n");
+ return 0;
+}
- if (act->narg + 1 != argc)
+int
+dm_nocmd (int argc, char **argv, mu_assoc_t options, void *env)
+{
+ struct interp_env *ienv = env;
+ if (mu_isdigit (*argv[0]))
{
- mu_error ("bad number of arguments for %s", argv[0]);
- exit (1);
+ ienv->msgno = get_num (argv[0]);
+ MU_ASSERT (mu_mailbox_get_message (ienv->mbx, ienv->msgno, &ienv->msg));
+ mu_printf ("%lu current message\n", (unsigned long) ienv->msgno);
+ return 0;
}
-
- if (act->needs_message)
- mu_printf ("%lu ", (unsigned long) env->msgno);
- mu_printf ("%s: ", argv[0]);
- act->fn (env, argv + 1);
- mu_printf ("\n");
+ return MU_ERR_PARSE;
}
+
+struct mu_tesh_command commands[] = {
+ { "__ENVINIT__", "", dm_envinit },
+ { "__ENVFINI__", "", dm_envfini },
+ { "__NOCMD__", "", dm_nocmd },
+ { "env_date", "", dm_env_date },
+ { "env_sender", "", dm_env_sender },
+ { "header_lines", "", dm_header_lines },
+ { "header_size", "", dm_header_size },
+ { "header_count", "", dm_header_count },
+ { "header_field", "NUMBER", dm_header_field },
+ { "header_value", "NAME", dm_header_value },
+ { "headers", "", dm_headers },
+ { "body_lines", "", dm_body_lines },
+ { "body_size", "", dm_body_size },
+ { "body_text", "", dm_body_text },
+ { "attr", "", dm_attr },
+ { "uid", "", dm_uid },
+ { "set_seen", "", dm_set_seen },
+ { "set_answered", "", dm_set_answered },
+ { "set_flagged", "", dm_set_flagged },
+ { "set_deleted", "", dm_set_deleted },
+ { "set_draft", "", dm_set_draft },
+ { "set_recent", "", dm_set_recent },
+ { "set_read", "", dm_set_read },
+ { "unset_seen", "", dm_unset_seen },
+ { "unset_answered", "", dm_unset_answered },
+ { "unset_flagged", "", dm_unset_flagged },
+ { "unset_deleted", "", dm_unset_deleted },
+ { "unset_draft", "", dm_unset_draft },
+ { "unset_recent", "", dm_unset_recent },
+ { "unset_read", "", dm_unset_read },
+ { "expunge", "", dm_expunge },
+ { "sync", "", dm_sync },
+ { "append", "FILE", dm_append },
+ { "uidvalidity", "", dm_uidvalidity },
+ { "uidnext", "", dm_uidnext },
+ { "count", "", dm_count },
+ { "recent", "", dm_recent },
+ { "unseen", "", dm_unseen },
+ { NULL }
+};
int
main (int argc, char **argv)
{
struct interp_env env = { NULL, NULL, 0 };
char *mailbox_name = getenv ("MAIL");
- int rc;
- mu_set_program_name (argv[0]);
- mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
+ mu_tesh_init (argv[0]);
mu_registrar_record (mu_dotmail_record);
argc--;
@@ -402,59 +465,6 @@ main (int argc, char **argv)
MU_ASSERT (mu_mailbox_create_default (&env.mbx, mailbox_name));
MU_ASSERT (mu_mailbox_open (env.mbx, MU_STREAM_RDWR));
- 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 (&env, i-n, argv);
- argc -= i;
- argv += i;
- }
- }
- else
- {
- char *buf = NULL;
- size_t size = 0, n;
- struct mu_wordsplit ws;
- int wsflags;
-
- 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)
- {
- mu_ltrim_class (buf, MU_CTYPE_SPACE);
- mu_rtrim_class (buf, MU_CTYPE_SPACE);
-
- MU_ASSERT (mu_wordsplit (buf, &ws, wsflags));
- wsflags |= MU_WRDSF_REUSE;
-
- if (ws.ws_wordc == 0)
- continue;
- interpret (&env, ws.ws_wordc, ws.ws_wordv);
- }
- if (wsflags & MU_WRDSF_REUSE)
- mu_wordsplit_free (&ws);
- }
- mu_mailbox_close (env.mbx);
- mu_mailbox_destroy (&env.mbx);
+ mu_tesh_read_and_eval (argc, argv, commands, &env);
return 0;
}

Return to:

Send suggestions and report system problems to the System administrator.