summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--doc/texinfo/mu-mh.texi63
-rw-r--r--mh/Makefile.am16
-rw-r--r--mh/TODO2
-rw-r--r--mh/prompter-rl.c68
-rw-r--r--mh/prompter-tty.c197
-rw-r--r--mh/prompter.c313
-rw-r--r--mh/prompter.h27
-rw-r--r--po/POTFILES.in2
9 files changed, 673 insertions, 21 deletions
diff --git a/configure.ac b/configure.ac
index 67ddf6b1e..7c9df0a6a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -909,7 +909,7 @@ AC_SUBST(CURSES_LIBS)
dnl Check for GNU Readline
AC_SUBST(READLINE_LIBS)
-if test x"$usereadline" = x"yes"; then
+if test "$usereadline" = "yes"; then
dnl FIXME This should only link in the curses libraries if it's
dnl really needed!
@@ -919,7 +919,7 @@ if test x"$usereadline" = x"yes"; then
AC_CHECK_LIB(readline, readline, mu_have_readline=yes)
LIBS=$saved_LIBS
- if test x"$mu_have_readline" = x"yes"; then
+ if test "$mu_have_readline" = "yes"; then
AC_CHECK_HEADERS(readline/readline.h,
AC_DEFINE(WITH_READLINE,1,[Enable use of readline]))
READLINE_LIBS="-lreadline $CURSES_LIBS"
@@ -933,6 +933,8 @@ if test x"$usereadline" = x"yes"; then
fi
+AM_CONDITIONAL([MU_COND_READLINE], [test "$usereadline" = "yes"])
+
AH_BOTTOM([
/* Newer versions of readline have rl_completion_matches */
#ifndef HAVE_RL_COMPLETION_MATCHES
diff --git a/doc/texinfo/mu-mh.texi b/doc/texinfo/mu-mh.texi
index b04a34c0f..99ebaebf6 100644
--- a/doc/texinfo/mu-mh.texi
+++ b/doc/texinfo/mu-mh.texi
@@ -311,25 +311,6 @@ behaviour is disabled when @option{--quiet} option was given.
The @option{--all} mode does not display commented out entries.
-@item repl
-
-Understands @option{--use} option. Disposition shell provides
-@code{use} command.
-
-@item rmm
-
-@enumerate 1
-@item
-Different behaviour if one of the messages in the list does not exist:
-
-Mailutils @command{rmm} does not delete any messages. Standard
-@command{rmm} in this case deletes all messages preceding the
-non-existent one.
-
-@item
-The @code{rmmproc} profile component is not used.
-@end enumerate
-
@item pick
The command line syntax @option{--@var{component} @var{string}}) is
@@ -388,6 +369,31 @@ pick --before '1 year ago'
etc...
@end smallexample
+@item prompter
+@enumerate
+@item
+Prompter attempts to use GNU Readline library, if it is installed.
+Consequently, arguments to @option{-erase} and @option{-kill} option
+must follow GNU style key sequence notation (@pxref{Readline Init File
+Syntax, keyseq,,readline,GNU Readline Library}).
+
+If @command{prompter} is built without @command{readline}, it accepts
+the following character notations:
+
+@table @asis
+@item \@var{nnnn}
+Here, @var{n} stands for a single octal digit.
+
+@item ^@var{chr}
+This notation is translated to the ASCII code @samp{@var{chr} + 0100}.
+@end table
+
+@item
+Component continuation lines are not required to begin with a
+whitespace. If leading whitespace is not present, @command{prompter}
+will add it automatically.
+@end enumerate
+
@item refile
@enumerate
@@ -405,6 +411,25 @@ compatibility only.
Message specs and folder names may be interspersed.
@end enumerate
+@item repl
+
+Understands @option{--use} option. Disposition shell provides
+@code{use} command.
+
+@item rmm
+
+@enumerate 1
+@item
+Different behaviour if one of the messages in the list does not exist:
+
+Mailutils @command{rmm} does not delete any messages. Standard
+@command{rmm} in this case deletes all messages preceding the
+non-existent one.
+
+@item
+The @code{rmmproc} profile component is not used.
+@end enumerate
+
@item sortm
New option @option{--numfield} specifies numeric comparison for the
diff --git a/mh/Makefile.am b/mh/Makefile.am
index 2c52f554e..08218b255 100644
--- a/mh/Makefile.am
+++ b/mh/Makefile.am
@@ -34,6 +34,7 @@ bin_PROGRAMS = \
mhparam\
mhpath\
pick\
+ prompter\
refile\
repl\
rmf\
@@ -44,6 +45,21 @@ bin_PROGRAMS = \
whatnow\
whom
+prompter_LDADD = $(mh_LIBS)
+
+if MU_COND_READLINE
+ PROMPTER_FUN=prompter-rl.c
+ prompter_LDADD += @READLINE_LIBS@
+else
+ PROMPTER_FUN=prompter-tty.c
+ prompter_LDADD += @CURSES_LIBS@
+endif
+
+prompter_SOURCES = \
+ prompter.c\
+ prompter.h\
+ $(PROMPTER_FUN)
+
noinst_LIBRARIES = libmh.a
libmh_a_SOURCES= \
diff --git a/mh/TODO b/mh/TODO
index 8dfe2c609..ba6b4108f 100644
--- a/mh/TODO
+++ b/mh/TODO
@@ -40,6 +40,7 @@ State Nice Utility Comments
+ 10 mhpath
+ 10 whatnow
+ 20 sortm
++ 20 prompter
Utilities In Alphabetical Order
===============================
@@ -55,6 +56,7 @@ mhl
mhn
mhpath
pick
+prompter
refile
repl
rmf
diff --git a/mh/prompter-rl.c b/mh/prompter-rl.c
new file mode 100644
index 000000000..731c9cbf2
--- /dev/null
+++ b/mh/prompter-rl.c
@@ -0,0 +1,68 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 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/>. */
+
+/* MH prompter - readline support */
+
+#include <mh.h>
+#include "prompter.h"
+#include <readline/readline.h>
+
+void
+prompter_init ()
+{
+}
+
+void
+prompter_done ()
+{
+}
+
+void
+prompter_set_erase (const char *keyseq)
+{
+ rl_bind_keyseq (keyseq, rl_delete);
+}
+
+void
+prompter_set_kill (const char *keyseq)
+{
+ rl_bind_keyseq (keyseq, rl_kill_full_line);
+}
+
+char *
+prompter_get_value (const char *name)
+{
+ char *prompt = NULL;
+ char *val;
+
+ if (name)
+ {
+ prompt = xmalloc (strlen (name) + 3);
+ strcpy (prompt, name);
+ strcat (prompt, ": ");
+ }
+ val = readline (prompt);
+ free (prompt);
+ return val;
+}
+
+char *
+prompter_get_line ()
+{
+ return doteof_filter (readline (NULL));
+}
+
+
diff --git a/mh/prompter-tty.c b/mh/prompter-tty.c
new file mode 100644
index 000000000..92c3f3f16
--- /dev/null
+++ b/mh/prompter-tty.c
@@ -0,0 +1,197 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 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/>. */
+
+/* MH prompter - traditional tty/termios support */
+
+#include <mh.h>
+#include "prompter.h"
+#include "muaux.h"
+#if HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+mu_stream_t strin;
+#if HAVE_TCGETATTR
+struct termios tios;
+#endif
+
+static int
+decode_key (const char *keyseq)
+{
+ if (keyseq[0] == '^' && keyseq[1])
+ {
+ if (keyseq[2])
+ {
+ mu_error (_("invalid keysequence (%s near %d)"),
+ keyseq, 2);
+ exit (1);
+ }
+ return keyseq[1] + 0100;
+ }
+ else if (keyseq[0] == '\\')
+ {
+ int i;
+ int c = 0;
+ for (i = 1; i <= 3; i++)
+ {
+ int n;
+ switch (keyseq[i])
+ {
+ case '0':
+ n = 0;
+ break;
+ case '1':
+ n = 1;
+ break;
+ case '2':
+ n = 2;
+ break;
+ case '3':
+ n = 3;
+ break;
+ case '4':
+ n = 4;
+ break;
+ case '5':
+ n = 5;
+ break;
+ case '6':
+ n = 6;
+ break;
+ case '7':
+ n = 7;
+ break;
+ default:
+ mu_error (_("invalid keysequence (%s near %d)"),
+ keyseq, i);
+ exit (1);
+ }
+ c = (c << 3) + n;
+ }
+ if (keyseq[i])
+ {
+ mu_error (_("invalid keysequence (%s near %d)"),
+ keyseq, i);
+ exit (1);
+ }
+ return c;
+ }
+ else if (keyseq[1])
+ {
+ mu_error (_("invalid keysequence (%s near %d)"),
+ keyseq, 2);
+ exit (1);
+ }
+
+ return keyseq[0];
+}
+
+static RETSIGTYPE
+sighan (int signo)
+{
+ prompter_done ();
+ exit (0);
+}
+
+void
+prompter_init ()
+{
+ int rc;
+ if ((rc = mu_stdio_stream_create (&strin, MU_STDIN_FD, MU_STREAM_READ)))
+ {
+ mu_error (_("cannot open stdout: %s"), mu_strerror (rc));
+ exit (1);
+ }
+#if HAVE_TCGETATTR
+ rc = tcgetattr (MU_STDOUT_FD, &tios);
+ if (rc)
+ {
+ mu_error (_("tcgetattr failed: %s"), mu_strerror (rc));
+ exit (1);
+ }
+ else
+ {
+ static int sigtab[] = { SIGPIPE, SIGABRT, SIGINT, SIGQUIT, SIGTERM };
+ mu_set_signals (sighan, sigtab, MU_ARRAY_SIZE (sigtab));
+ }
+#endif
+}
+
+void
+prompter_done ()
+{
+#if HAVE_TCGETATTR
+ int rc = tcsetattr (MU_STDOUT_FD, TCSANOW, &tios);
+ if (rc)
+ mu_error (_("tcsetattr failed: %s"), mu_strerror (rc));
+#endif
+}
+
+void
+prompter_set_erase (const char *keyseq)
+{
+#if HAVE_TCGETATTR
+ int rc;
+ struct termios t = tios;
+
+ t.c_lflag |= ICANON;
+ t.c_cc[VERASE] = decode_key (keyseq);
+ rc = tcsetattr (MU_STDOUT_FD, TCSANOW, &t);
+ if (rc)
+ mu_error (_("tcsetattr failed: %s"), mu_strerror (rc));
+#endif
+}
+
+void
+prompter_set_kill (const char *keyseq)
+{
+#if HAVE_TCGETATTR
+ int rc;
+ struct termios t = tios;
+
+ t.c_lflag |= ICANON;
+ t.c_cc[VKILL] = decode_key (keyseq);
+ rc = tcsetattr (MU_STDOUT_FD, TCSANOW, &t);
+ if (rc)
+ mu_error (_("tcsetattr failed: %s"), mu_strerror (rc));
+#endif
+}
+
+char *
+prompter_get_value (const char *name)
+{
+ size_t size = 0, n;
+ char *buf = NULL;
+
+ mu_stream_printf (strout, "%s: ", name);
+ mu_stream_flush (strout);
+ if (mu_stream_getline (strin, &buf, &size, &n) || n == 0)
+ return NULL;
+ mu_rtrim_cset (buf, "\n");
+ return buf;
+}
+
+char *
+prompter_get_line ()
+{
+ size_t size = 0, n;
+ char *buf = NULL;
+
+ if (mu_stream_getline (strin, &buf, &size, &n) || n == 0)
+ return NULL;
+ mu_rtrim_cset (buf, "\n");
+ return doteof_filter (buf);
+}
diff --git a/mh/prompter.c b/mh/prompter.c
new file mode 100644
index 000000000..1131c4ec3
--- /dev/null
+++ b/mh/prompter.c
@@ -0,0 +1,313 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 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/>. */
+
+/* MH prompter command */
+
+#include <mh.h>
+#include "prompter.h"
+
+static char doc[] = N_("GNU MH prompter")"\v"
+N_("Use -help to obtain the list of traditional MH options.");
+static char args_doc[] = N_("FILE");
+
+enum {
+ ARG_ERASE=256,
+ ARG_KILL,
+ ARG_PREPEND,
+ ARG_NOPREPEND,
+ ARG_RAPID,
+ ARG_NORAPID,
+ ARG_DOTEOF,
+ ARG_NODOTEOF
+};
+
+static struct argp_option options[] = {
+ { "erase", ARG_ERASE, N_("CHAR"), 0,
+ N_("set erase character") },
+ { "kill", ARG_KILL, N_("CHAR"), 0,
+ N_("set kill character") },
+ { "prepend", ARG_PREPEND, N_("BOOL"), 0,
+ N_("prepend user input to the message body") },
+ { "noprepend", ARG_NOPREPEND, NULL, OPTION_HIDDEN,
+ NULL },
+ { "rapid", ARG_RAPID, N_("BOOL"), 0,
+ N_("do not display message body") },
+ { "norapid", ARG_NORAPID, NULL, OPTION_HIDDEN,
+ NULL },
+ { "doteof", ARG_DOTEOF, N_("BOOL"), 0,
+ N_("a period on a line marks end-of-file") },
+ { "nodoteof", ARG_NODOTEOF, NULL, OPTION_HIDDEN,
+ NULL },
+ { NULL }
+};
+
+struct mh_option mh_option[] = {
+ { "erase", MH_OPT_ARG, "chr" },
+ { "kill", MH_OPT_ARG, "chr" },
+ { "prepend", MH_OPT_BOOL },
+ { "rapid", MH_OPT_BOOL },
+ { "doteof", MH_OPT_BOOL },
+ { NULL }
+};
+
+char *erase_seq;
+char *kill_seq;
+int prepend_option;
+int rapid_option;
+int doteof_option;
+
+static error_t
+opt_handler (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARG_ERASE:
+ erase_seq = arg;
+ break;
+
+ case ARG_KILL:
+ kill_seq = arg;
+ break;
+
+ case ARG_PREPEND:
+ prepend_option = is_true (arg);
+ break;
+
+ case ARG_NOPREPEND:
+ prepend_option = 0;
+ break;
+
+ case ARG_RAPID:
+ rapid_option = is_true (arg);
+ break;
+
+ case ARG_NORAPID:
+ rapid_option = 0;
+ break;
+
+ case ARG_DOTEOF:
+ doteof_option = is_true (arg);
+ break;
+
+ case ARG_NODOTEOF:
+ doteof_option = 0;
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static int
+is_empty_string (const char *str)
+{
+ if (!str)
+ return 1;
+ return mu_str_skip_class (str, MU_CTYPE_BLANK)[0] == 0;
+}
+
+char *
+doteof_filter (char *val)
+{
+ if (!val)
+ return NULL;
+ if (doteof_option && val[0] == '.')
+ {
+ if (val[1] == 0)
+ {
+ free (val);
+ return NULL;
+ }
+ memmove (val, val + 1, strlen (val + 1) + 1);
+ }
+ return val;
+}
+
+mu_stream_t strout;
+
+int
+main (int argc, char **argv)
+{
+ int index;
+ int rc;
+ mu_stream_t in, tmp;
+ mu_message_t msg;
+ mu_header_t hdr;
+ mu_iterator_t itr;
+ const char *file;
+ char *newval;
+ mu_off_t size;
+ mu_body_t body;
+ mu_stream_t bstr;
+
+ MU_APP_INIT_NLS ();
+
+ mh_argp_init ();
+ mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc,
+ opt_handler, NULL, &index);
+
+ if (index == argc)
+ {
+ mu_error (_("file name not given"));
+ exit (1);
+ }
+ file = argv[index];
+
+ prompter_init ();
+ if (erase_seq)
+ prompter_set_erase (erase_seq);
+ if (kill_seq)
+ prompter_set_erase (kill_seq);
+
+ if ((rc = mu_stdio_stream_create (&strout, MU_STDOUT_FD, MU_STREAM_WRITE)))
+ {
+ mu_error (_("cannot open stdout: %s"), mu_strerror (rc));
+ return 1;
+ }
+
+ if ((rc = mu_file_stream_create (&in, file, MU_STREAM_RDWR)))
+ {
+ mu_error (_("cannot open input file `%s': %s"),
+ file, mu_strerror (rc));
+ return 1;
+ }
+ rc = mu_stream_to_message (in, &msg);
+ if (rc)
+ {
+ mu_error (_("input stream %s is not a message (%s)"),
+ file, mu_strerror (rc));
+ return 1;
+ }
+
+ if ((rc = mu_temp_file_stream_create (&tmp, NULL)))
+ {
+ mu_error (_("Cannot open temporary file: %s"),
+ mu_strerror (rc));
+ return 1;
+ }
+
+ /* Copy headers */
+ mu_message_get_header (msg, &hdr);
+ mu_header_get_iterator (hdr, &itr);
+ for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+ mu_iterator_next (itr))
+ {
+ const char *name, *val;
+
+ mu_iterator_current_kv (itr, (const void **)&name, (void**)&val);
+ if (!is_empty_string (val))
+ {
+ mu_stream_printf (tmp, "%s: %s\n", name, val);
+ mu_stream_printf (strout, "%s: %s\n", name, val);
+ }
+ else
+ {
+ int cont = 0;
+ mu_opool_t opool;
+ const char *prompt = name;
+
+ mu_opool_create (&opool, 1);
+ do
+ {
+ size_t len;
+ char *p;
+ p = prompter_get_value (prompt);
+ if (!p)
+ return 1;
+ prompt = NULL;
+ if (cont)
+ {
+ mu_opool_append_char (opool, '\n');
+ if (!mu_isspace (p[0]))
+ mu_opool_append_char (opool, '\t');
+ }
+ len = strlen (p);
+ if (len > 0 && p[len-1] == '\\')
+ {
+ len--;
+ cont = 1;
+ }
+ else
+ cont = 0;
+ mu_opool_append (opool, p, len);
+ free (p);
+ }
+ while (cont);
+
+ newval = mu_opool_finish (opool, NULL);
+ if (!is_empty_string (newval))
+ mu_stream_printf (tmp, "%s: %s\n", name, newval);
+ mu_opool_destroy (&opool);
+ }
+ }
+ mu_stream_printf (strout, "--------\n");
+ mu_stream_write (tmp, "\n", 1, NULL);
+
+ /* Copy body */
+
+ if (prepend_option)
+ {
+ mu_stream_printf (strout, "\n--------%s\n\n", _("Enter initial text"));
+ while (newval = prompter_get_line ())
+ {
+ mu_stream_write (tmp, newval, strlen (newval), NULL);
+ free (newval);
+ mu_stream_write (tmp, "\n", 1, NULL);
+ }
+ }
+
+ mu_message_get_body (msg, &body);
+ mu_body_get_streamref (body, &bstr);
+
+ if (!prepend_option && !rapid_option)
+ {
+ mu_stream_copy (strout, bstr, 0, NULL);
+ mu_stream_seek (bstr, 0, MU_SEEK_SET, NULL);
+ }
+
+ mu_stream_copy (tmp, bstr, 0, NULL);
+ mu_stream_unref (bstr);
+
+ if (!prepend_option && !rapid_option)
+ {
+ printf ("\n--------%s\n\n", _("Enter additional text"));
+ while (newval = prompter_get_line ())
+ {
+ mu_stream_write (tmp, newval, strlen (newval), NULL);
+ free (newval);
+ mu_stream_write (tmp, "\n", 1, NULL);
+ }
+ }
+
+ /* Destroy the message */
+ mu_message_destroy (&msg, mu_message_get_owner (msg));
+
+ /* Rewind the streams and copy data back to in. */
+ mu_stream_seek (in, 0, MU_SEEK_SET, NULL);
+ mu_stream_seek (tmp, 0, MU_SEEK_SET, NULL);
+ mu_stream_copy (in, tmp, 0, &size);
+ mu_stream_truncate (in, size);
+
+ mu_stream_destroy (&in);
+ mu_stream_destroy (&tmp);
+ mu_stream_destroy (&strout);
+
+ prompter_done ();
+
+ return 0;
+}
+
diff --git a/mh/prompter.h b/mh/prompter.h
new file mode 100644
index 000000000..cdaf838c0
--- /dev/null
+++ b/mh/prompter.h
@@ -0,0 +1,27 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2010 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/>. */
+
+void prompter_init (void);
+void prompter_done (void);
+void prompter_set_erase (const char *keyseq);
+void prompter_set_kill (const char *keyseq);
+char *prompter_get_value (const char *name);
+char *prompter_get_line (void);
+char *doteof_filter (char *val);
+
+extern mu_stream_t strout;
+
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8d2e5e6b5..001f75452 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -179,6 +179,8 @@ mh/mhl.c
mh/mhn.c
mh/mhpath.c
mh/pick.c
+mh/prompter.c
+mh/prompter-tty.c
mh/refile.c
mh/repl.c
mh/rmf.c

Return to:

Send suggestions and report system problems to the System administrator.