diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2019-01-23 17:57:42 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2019-01-23 18:06:29 +0200 |
commit | 18c0c0316801eccecd3e6fb4f52ecf067f910b0c (patch) | |
tree | 3e4b84b0fe8e3aa6208d218cc9518676144f1621 | |
parent | bab5c3eef8e7c8ef272bcff6351cd7a06c5f755b (diff) | |
download | mailutils-18c0c0316801eccecd3e6fb4f52ecf067f910b0c.tar.gz mailutils-18c0c0316801eccecd3e6fb4f52ecf067f910b0c.tar.bz2 |
mail: implement variable and command expansion in echo command
* mail/echo.c: Implement variable and command expansion.
* mail/mail.c (main): Set PID mailvar.
* mail/mailvar.c (mailvar_tab): New variable PID (to
help in debugging).
(set_folder): Expand initial ~
* mail/set.c: Remove misguiding comment.
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | doc/texinfo/programs/mail.texi | 8 | ||||
-rw-r--r-- | mail/echo.c | 140 | ||||
-rw-r--r-- | mail/mail.c | 1 | ||||
-rw-r--r-- | mail/mailvar.c | 107 | ||||
-rw-r--r-- | mail/set.c | 4 |
6 files changed, 190 insertions, 75 deletions
@@ -28,6 +28,11 @@ envelope on the stdout, instead of using the current output stream. Last argument always specifies the command to pipe messages to. +** echo command + +The echo command behaves similarly to its shell equivalent. In +particular, variable and command expansions are performed. + ** fixed / and :/ searches in message sets ** Fix and improve argument completion diff --git a/doc/texinfo/programs/mail.texi b/doc/texinfo/programs/mail.texi index 6a10435ab..00ba17811 100644 --- a/doc/texinfo/programs/mail.texi +++ b/doc/texinfo/programs/mail.texi @@ -2509,6 +2509,14 @@ Contains the directory in which files created by @code{save}, @code{write}, etc. commands will be stored. When unset, current directory is assumed. +@kwindex PID +@item PID +@*Type: String, Read-Only +@*Default: PID of the process. +@vrindex PID, mail variable + +PID of the current @command{mail} process. + @kwindex page @item page @*Type: Boolean. diff --git a/mail/echo.c b/mail/echo.c index 317f81a1f..98de664da 100644 --- a/mail/echo.c +++ b/mail/echo.c @@ -16,43 +16,129 @@ #include "mail.h" +#if !HAVE_DECL_ENVIRON +extern char **environ; +#endif + /* * ec[ho] string ... */ static int -echo (char *s) +echo_runcmd (char **ret, const char *str, size_t len, char **argv, void *closure) { - struct mu_wordsplit ws; - size_t len = strlen (s); - int rc = 1; + int rc; + mu_stream_t ps; + mu_stream_t outs; + size_t i; + int status = MU_WRDSE_OK; + char buf[128]; + size_t n; + + *ret = NULL; + for (i = 0; argv[i]; i++) + ; + rc = mu_prog_stream_create (&ps, argv[0], i, argv, 0, NULL, MU_STREAM_READ); + if (rc) + { + mu_error (_("Can't run %s: %s"), argv[0], mu_strerror (rc)); + return MU_WRDSE_USERERR; + } + + rc = mu_memory_stream_create (&outs, MU_STREAM_RDWR); + if (rc) + { + mu_diag_funcall (MU_DIAG_ERROR, "mu_memory_stream_create", NULL, rc); + mu_stream_destroy (&ps); + return MU_WRDSE_USERERR; + } - /* FIXME: This logic is flawed */ - if (len > 0 && s[len - 1] == '\\') + while ((rc = mu_stream_read (ps, buf, sizeof (buf), &n)) == 0 && n > 0) { - if (len == 1) - return 0; - if (s[len-2] != '\\') + int wn = mu_stream_write (outs, buf, n, NULL); + if (wn) { - --len; - rc = 0; + mu_error (_("error writing to temporary stream: %s"), + mu_strerror (wn)); + status = MU_WRDSE_USERERR; + break; } } - - - if (mu_wordsplit_len (s, len, &ws, - MU_WRDSF_NOSPLIT | - MU_WRDSF_NOCMD | MU_WRDSF_NOVAR | MU_WRDSF_QUOTE)) + + if (status == MU_WRDSE_OK && rc) { - mu_error (_("cannot split `%s': %s"), s, - mu_wordsplit_strerror (&ws)); + mu_error (_("error reading %s output: %s"), argv[0], mu_strerror (rc)); + status = MU_WRDSE_USERERR; } - else + + mu_stream_destroy (&ps); + + if (status == MU_WRDSE_OK) { - mu_printf ("%s", ws.ws_wordv[0]); + mu_off_t size; + char *p; + + mu_stream_size (outs, &size); + p = malloc (size + 1); + if (p) + { + mu_stream_seek (outs, 0, MU_SEEK_SET, NULL); + rc = mu_stream_read (outs, p, size, NULL); + if (rc == 0) + { + p[size] = 0; + *ret = p; + } + else + { + free (p); + mu_error (_("error reading from temporary stream: %s"), + mu_strerror (rc)); + status = MU_WRDSE_USERERR; + } + } + else + status = MU_WRDSE_NOSPACE; + } + + mu_stream_destroy (&outs); + + return status; +} + +static int +echo (char *s, int *nl) +{ + int rc; + struct mu_wordsplit ws; + int wsflags = MU_WRDSF_NOSPLIT | MU_WRDSF_QUOTE | MU_WRDSF_ENV; + size_t len; + + ws.ws_env = (const char **) environ; + ws.ws_command = echo_runcmd; + + rc = mu_wordsplit (s, &ws, wsflags); + switch (rc) + { + case MU_WRDSE_OK: + break; + + case MU_WRDSE_USERERR: + /* error message already displayed */ + mu_wordsplit_free (&ws); + return 1; + + default: + mu_error ("%s", mu_wordsplit_strerror (&ws)); mu_wordsplit_free (&ws); + return 1; } - return rc; + + len = strlen (ws.ws_wordv[0]); + mu_stream_write (mu_strout, ws.ws_wordv[0], len, NULL); + *nl = len > 0 && ws.ws_wordv[0][len-1] == '\n'; + mu_wordsplit_free (&ws); + return 0; } int @@ -60,15 +146,17 @@ mail_echo (int argc, char **argv) { if (argc > 1) { - int i = 0; - + int i; + int nl = 0; for (i = 1; i < argc; i++) { - if (echo (argv[i])) + if (i > 1) mu_printf (" "); + if (echo (argv[i], &nl)) + break; } - mu_printf ("\n"); + if (!nl) + mu_printf ("\n"); } return 0; } - diff --git a/mail/mail.c b/mail/mail.c index b9142ea24..8c12cf2a7 100644 --- a/mail/mail.c +++ b/mail/mail.c @@ -463,6 +463,7 @@ main (int argc, char **argv) setenv ("VISUAL", "vi", 0); /* set defaults for execution */ + util_do_command ("setq PID=\"%lu\"", (unsigned long) getpid ()); for (i = 0; i < sizeof (default_setup)/sizeof (default_setup[0]); i++) util_do_command ("%s", default_setup[i]); diff --git a/mail/mailvar.c b/mail/mailvar.c index 5cdb7f1c2..ae82b0b78 100644 --- a/mail/mailvar.c +++ b/mail/mailvar.c @@ -52,12 +52,12 @@ static int set_folder (enum mailvar_cmd cmd, struct mailvar_variable *); static int set_headline (enum mailvar_cmd, struct mailvar_variable *); - + struct mailvar_symbol mailvar_tab[] = { /* FIXME: */ { { "allnet", }, MAILVAR_HIDDEN }, - + /* For compatibility with other mailx implementations. Never used, always true. */ { { "append", }, @@ -110,7 +110,7 @@ struct mailvar_symbol mailvar_tab[] = N_("get date from the `Date:' header, instead of the envelope") }, { { "debug", }, MAILVAR_TYPEMASK (mailvar_type_string) | - MAILVAR_TYPEMASK (mailvar_type_boolean), + MAILVAR_TYPEMASK (mailvar_type_boolean), N_("set Mailutils debug level"), set_debug }, { { "decode-fallback", }, @@ -134,7 +134,7 @@ struct mailvar_symbol mailvar_tab[] = MAILVAR_TYPEMASK (mailvar_type_boolean), N_("swap the meaning of reply and Reply commands") }, { { "folder", }, - MAILVAR_TYPEMASK (mailvar_type_string), + MAILVAR_TYPEMASK (mailvar_type_string), N_("folder directory name"), set_folder }, { { "fromfield", }, @@ -279,7 +279,7 @@ struct mailvar_symbol mailvar_tab[] = { { "xmailer", }, MAILVAR_TYPEMASK (mailvar_type_boolean), N_("add the `X-Mailer' header to the outgoing messages") }, - + { { "mime" }, MAILVAR_TYPEMASK (mailvar_type_boolean), N_("always compose MIME messages") }, @@ -288,7 +288,10 @@ struct mailvar_symbol mailvar_tab[] = { { "onehop", }, MAILVAR_HIDDEN, NULL }, { { "quiet", }, MAILVAR_TYPEMASK (mailvar_type_boolean) | MAILVAR_HIDDEN, - "suppresses the printing of the version when first invoked" }, + N_("suppresses the printing of the version when first invoked") }, + + { { "PID" }, MAILVAR_TYPEMASK (mailvar_type_string) | MAILVAR_RDONLY, + N_("PID of this process") }, { { NULL }, } }; @@ -314,10 +317,10 @@ print_descr (mu_stream_t out, const char *s, int n, int doc_col, int rmargin, char *pfx) { mu_stream_stat_buffer stat; - + if (!s) return; - + mu_stream_set_stat (out, MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT), stat); stat[MU_STREAM_STAT_OUT] = n; @@ -328,14 +331,14 @@ print_descr (mu_stream_t out, const char *s, int n, if (stat[MU_STREAM_STAT_OUT] && pfx) mu_stream_printf (out, "%s", pfx); - + while (stat[MU_STREAM_STAT_OUT] < doc_col) mu_stream_write (out, " ", 1, NULL); for (p = s; *p && p < s + (rmargin - doc_col); p++) if (mu_isspace (*p)) space = p; - + if (!space || p < s + (rmargin - doc_col)) { mu_stream_printf (out, "%s", s); @@ -372,7 +375,7 @@ mailvar_find_variable (const char *name, int create) { struct mailvar_symbol *sym; struct mailvar_variable *var; - + sym = find_mailvar_symbol (name); if (sym) var = &sym->var; @@ -387,7 +390,7 @@ mailvar_find_variable (const char *name, int create) mu_list_create (&mailvar_list); mu_list_set_comparator (mailvar_list, mailvar_variable_comp); } - + if (mu_list_locate (mailvar_list, &entry, (void**)&p)) { if (!create) @@ -419,11 +422,11 @@ mailvar_find_variable (const char *name, int create) If PTR is not NULL, it must point to int if TYPE is mailvar_type_number or mailvar_type_boolean - const char * if TYPE is mailvar_type_string. + const char * if TYPE is mailvar_type_string. Passing PTR=NULL may be used to check whether the variable is set without retrieving its value. */ - + int mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn) { @@ -453,7 +456,7 @@ mailvar_get (void *ptr, const char *variable, enum mailvar_type type, int warn) default: break; } - + return 0; } @@ -470,14 +473,14 @@ mailvar_variable_reset (struct mailvar_variable *var) { if (!var->set) return; - + switch (var->type) { case mailvar_type_string: free (var->value.string); var->value.string = NULL; break; - + default: break; } @@ -493,7 +496,7 @@ mailvar_variable_reset (struct mailvar_variable *var) Unless MOPTF_QUIET bit is set in FLAGS, the function performs semantic check, using the builtin options table. - + If VALUE is null the VARIABLE is unset. */ int mailvar_set (const char *variable, void *value, enum mailvar_type type, @@ -503,7 +506,7 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, const struct mailvar_symbol *sym = find_mailvar_symbol (variable); enum mailvar_cmd cmd = (flags & MOPTF_UNSET) ? mailvar_cmd_unset : mailvar_cmd_set; - + if (!(flags & MOPTF_QUIET) && mailvar_is_true ("variable-strict")) { if (!sym) @@ -511,7 +514,9 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, variable); else if (sym->flags & MAILVAR_RDONLY) { - mu_error (_("Cannot set read-only variable %s"), + mu_error (cmd == mailvar_cmd_set + ? _("Cannot set read-only variable %s") + : _("Cannot unset read-only variable %s"), variable); return 1; } @@ -532,7 +537,7 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, newvar.type = var->type; newvar.set = 0; memset (&newvar.value, 0, sizeof (newvar.value)); - + switch (cmd) { case mailvar_cmd_set: @@ -543,7 +548,7 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, case mailvar_type_number: newvar.value.number = *(int*)value; break; - + case mailvar_type_string: { char *p = strdup (value); @@ -555,11 +560,11 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, newvar.value.string = p; } break; - + case mailvar_type_boolean: newvar.value.bool = *(int*)value; break; - + default: abort(); } @@ -592,20 +597,33 @@ mailvar_set (const char *variable, void *value, enum mailvar_type type, static int set_folder (enum mailvar_cmd cmd, struct mailvar_variable *var) { - int rc = mu_set_folder_directory (var->value.string); + int rc; + + if (var->value.string) + { + char *p = mu_tilde_expansion (var->value.string, MU_HIERARCHY_DELIMITER, + NULL); + if (!p) + mu_alloc_die (); + if (var->set) + free (var->value.string); + var->value.string = p; + } + + rc = mu_set_folder_directory (var->value.string); if (rc) mu_diag_funcall (MU_DIAG_ERROR, "mu_set_folder_directory", var->value.string, rc); return rc; } - + static int set_headline (enum mailvar_cmd cmd, struct mailvar_variable *var) { if (cmd == mailvar_cmd_unset) return 1; - + mail_compile_headline (var->value.string); return 0; } @@ -615,7 +633,7 @@ set_decode_fallback (enum mailvar_cmd cmd, struct mailvar_variable *var) { char *value; int rc; - + switch (cmd) { case mailvar_cmd_set: @@ -625,7 +643,7 @@ set_decode_fallback (enum mailvar_cmd cmd, struct mailvar_variable *var) case mailvar_cmd_unset: value = "none"; } - + rc = mu_set_default_fallback (value); if (rc) mu_error (_("Incorrect value for decode-fallback")); @@ -738,7 +756,7 @@ mu_list_t mailvar_list_copy (int set) { mu_list_t list = NULL; - + if (mailvar_list) mu_list_map (mailvar_list, mailvar_mapper, NULL, 1, &list); if (!list) @@ -747,7 +765,7 @@ mailvar_list_copy (int set) mu_list_sort (list, mailvar_variable_comp); return list; } - + struct mailvar_iterator { @@ -756,7 +774,7 @@ struct mailvar_iterator mu_list_t varlist; mu_iterator_t varitr; }; - + const char * mailvar_iterate_next (struct mailvar_iterator *itr) { @@ -766,7 +784,7 @@ mailvar_iterate_next (struct mailvar_iterator *itr) { mu_iterator_current (itr->varitr, (void**) &vp); mu_iterator_next (itr->varitr); - + if (strlen (vp->name) >= itr->prefixlen && strncmp (vp->name, itr->prefix, itr->prefixlen) == 0) return vp->name; @@ -812,7 +830,7 @@ mailvar_printer (void *item, void *data) { struct mailvar_variable *vp = item; struct mailvar_print_closure *clos = data; - + if (clos->prettyprint) { const struct mailvar_symbol *sym = find_mailvar_symbol (vp->name); @@ -832,17 +850,17 @@ mailvar_printer (void *item, void *data) case mailvar_type_number: mu_stream_printf (clos->out, "%s=%d", vp->name, vp->value.number); break; - + case mailvar_type_string: mu_stream_printf (clos->out, "%s=\"%s\"", vp->name, vp->value.string); break; - + case mailvar_type_boolean: if (!vp->value.bool) mu_stream_printf (clos->out, "no"); mu_stream_printf (clos->out, "%s", vp->name); break; - + case mailvar_type_whatever: mu_stream_printf (clos->out, "%s %s", vp->name, _("oops?")); } @@ -856,7 +874,7 @@ mailvar_print (int set) mu_list_t varlist; size_t count; struct mailvar_print_closure clos; - + varlist = mailvar_list_copy (set); mu_list_count (varlist, &count); clos.out = open_pager (count); @@ -928,7 +946,7 @@ describe_symbol (mu_stream_t out, int width, const struct mailvar_symbol *sym) } mu_stream_printf (out, "\n"); mu_stream_set_stat (out, 0, NULL); - + mu_stream_printf (out, _("Type: ")); for (i = 0, t = 0; i < sizeof (typestr) / sizeof (typestr[0]); i++) if (sym->flags & MAILVAR_TYPEMASK (i)) @@ -940,14 +958,14 @@ describe_symbol (mu_stream_t out, int width, const struct mailvar_symbol *sym) if (!t) mu_stream_printf (out, "%s", gettext (typestr[0])); mu_stream_printf (out, "\n"); - + mu_stream_printf (out, "%s", _("Current value: ")); mailvar_variable_format (out, &sym->var, _("[not set]")); if (sym->flags & MAILVAR_RDONLY) mu_stream_printf (out, " [%s]", _("read-only")); mu_stream_printf (out, "\n"); - + print_descr (out, gettext (sym->descr ? sym->descr : N_("Not documented")), 1, 1, width - 1, NULL); mu_stream_printf (out, "\n"); @@ -959,7 +977,7 @@ mail_variable (int argc, char **argv) int pagelines = util_get_crt (); int width = util_screen_columns (); mu_stream_t out = open_pager (pagelines + 1); - + if (argc == 1) { struct mailvar_symbol *sym; @@ -984,7 +1002,6 @@ mail_variable (int argc, char **argv) mu_stream_unref (out); return 0; } - #ifdef WITH_READLINE static char * @@ -992,7 +1009,7 @@ mailvar_generator (int set, const char *text, int state) { static struct mailvar_iterator *itr; const char *p; - + if (!state) p = mailvar_iterate_first (set, text, &itr); else @@ -1026,7 +1043,7 @@ mailvar_set_compl (int argc, char **argv, int ws) /* Possible values for argv[0] are: set, unset, variable */ argv[0][0] == 'u' ? mailvar_unset_generator - : mailvar_set_generator); + : mailvar_set_generator); } #endif diff --git a/mail/set.c b/mail/set.c index 58a860944..5926847bb 100644 --- a/mail/set.c +++ b/mail/set.c @@ -20,10 +20,6 @@ * se[t] [name[=[string]] ...] [name=number ...] [noname ...] */ -/* - * NOTE: ask is a synonym for asksub - */ - int mail_set (int argc, char **argv) { |