1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include "mail.h"
18 
19 static void
expand_bang(char ** pbuf,const char * arg,const char * last)20 expand_bang (char **pbuf, const char *arg, const char *last)
21 {
22   char *tmp, *q;
23   const char *p;
24   size_t count = 0;
25 
26   for (p = arg; *p; p++)
27     if (*p == '!')
28       count++;
29 
30   if (count == 0)
31     {
32       *pbuf = mu_strdup (arg);
33       return;
34     }
35 
36   if (!last)
37     {
38       mu_error (_("No previous command"));
39       return;
40     }
41 
42   tmp = mu_alloc (strlen (arg) + count * (strlen (last) - 1) + 1);
43   for (p = arg, q = tmp; *p; )
44     {
45       if (*p == '!')
46 	{
47 	  strcpy (q, last);
48 	  q += strlen (q);
49 	  p++;
50 	}
51       else
52 	*q++ = *p++;
53     }
54   *q = 0;
55 
56   free (*pbuf);
57   *pbuf = tmp;
58 }
59 
60 int
mail_execute(int shell,char * progname,int argc,char ** argv)61 mail_execute (int shell, char *progname, int argc, char **argv)
62 {
63   int xargc, i, status, rc;
64   char **xargv;
65   char *buf;
66   int expanded; /* Indicates whether argv has been bang-expanded */
67 
68   if (argc == 0)
69     {
70       /* No arguments mean execute a copy of the user shell */
71       shell = 1;
72     }
73 
74   xargc = argc;
75   if (shell && argc < 3)
76     xargc = 3;
77   xargv = mu_calloc (xargc + 1, sizeof (xargv[0]));
78 
79   /* Expand arguments if required */
80   if (mailvar_is_true (mailvar_name_bang))
81     {
82       int i;
83       char *last = NULL;
84 
85       mailvar_get (&last, mailvar_name_gnu_last_command,
86 		   mailvar_type_string, 0);
87       expand_bang (xargv, progname, last);
88       for (i = 1; i < argc; i++)
89 	expand_bang (xargv + i, argv[i], last);
90       expanded = 1;
91     }
92   else
93     {
94       if (argc)
95 	xargv[0] = progname;
96       for (i = 1; i < argc; i++)
97 	xargv[i] = argv[i];
98       expanded = 0;
99     }
100 
101   /* Reconstruct the command line and save it to gnu-last-command variable.
102      Important: use argc (not xargc)!
103   */
104   mu_argcv_string (argc, xargv, &buf);
105   mailvar_set (mailvar_name_gnu_last_command, buf, mailvar_type_string,
106 	       MOPTF_QUIET | MOPTF_OVERWRITE);
107 
108   if (shell)
109     {
110       if (expanded)
111 	{
112 	  for (i = 0; i < argc; i++)
113 	    free (xargv[i]);
114 	  expanded = 0;
115 	}
116 
117       xargv[0] = getenv ("SHELL");
118       if (argc == 0)
119 	{
120 	  xargv[1] = NULL;
121 	  xargc = 1;
122 	}
123       else
124 	{
125 	  xargv[1] = "-c";
126 	  xargv[2] = buf;
127 	  xargv[3] = NULL;
128 	  xargc = 3;
129 	}
130     }
131 
132   rc = mu_spawnvp (xargv[0], xargv, &status);
133   if (rc)
134     {
135       mu_diag_funcall (MU_DIAG_ERROR, "mu_spawnvp", xargv[0], rc);
136     }
137   /* FIXME:
138   else if (status)
139     mu_diag_output (MU_DIAG_NOTICE, ....
140   */
141   free (buf);
142   if (expanded)
143     for (i = 0; i < argc; i++)
144       free (xargv[i]);
145   free (xargv);
146   return rc;
147 }
148 
149 /*
150  * sh[ell] [command] -- GNU extension
151  * ![command] -- GNU extension
152  */
153 
154 int
mail_shell(int argc,char ** argv)155 mail_shell (int argc, char **argv)
156 {
157   if (argv[0][0] == '!' && strlen (argv[0]) > 1)
158     return mail_execute (1, argv[0] + 1, argc, argv);
159   else if (argc > 1)
160     return mail_execute (0, argv[1], argc-1, argv+1);
161   else
162     return mail_execute (1, NULL, 0, NULL);
163   return 1;
164 }
165 
166 
167