summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2019-01-23 17:57:42 +0200
committerSergey Poznyakoff <gray@gnu.org>2019-01-23 18:06:29 +0200
commit18c0c0316801eccecd3e6fb4f52ecf067f910b0c (patch)
tree3e4b84b0fe8e3aa6208d218cc9518676144f1621
parentbab5c3eef8e7c8ef272bcff6351cd7a06c5f755b (diff)
downloadmailutils-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--NEWS5
-rw-r--r--doc/texinfo/programs/mail.texi8
-rw-r--r--mail/echo.c140
-rw-r--r--mail/mail.c1
-rw-r--r--mail/mailvar.c107
-rw-r--r--mail/set.c4
6 files changed, 190 insertions, 75 deletions
diff --git a/NEWS b/NEWS
index 5c50fa9d1..84d36010c 100644
--- a/NEWS
+++ b/NEWS
@@ -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)
{

Return to:

Send suggestions and report system problems to the System administrator.