diff options
-rw-r--r-- | configure.ac | 6 | ||||
-rw-r--r-- | doc/texinfo/mu-mh.texi | 63 | ||||
-rw-r--r-- | mh/Makefile.am | 16 | ||||
-rw-r--r-- | mh/TODO | 2 | ||||
-rw-r--r-- | mh/prompter-rl.c | 68 | ||||
-rw-r--r-- | mh/prompter-tty.c | 197 | ||||
-rw-r--r-- | mh/prompter.c | 313 | ||||
-rw-r--r-- | mh/prompter.h | 27 | ||||
-rw-r--r-- | po/POTFILES.in | 2 |
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= \ @@ -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 |