]> diplodocus.org Git - nmh/blob - sbr/arglist.c
Reverted commit 9a4b4a3d3b27fe4a7ff6d0b8724ce1c06b5917eb.
[nmh] / sbr / arglist.c
1
2 /*
3 * arglist.c -- Routines for handling argument lists for execvp() and friends
4 *
5 * This code is Copyright (c) 2013, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 #include <h/mh.h>
11 #include <h/utils.h>
12
13 /*
14 * Split up a command into an appropriate array to pass to execvp()
15 * or similar function. Returns an allocated argv[] array.
16 *
17 * Function arguments:
18 *
19 * command - String to split up
20 * file - the first argument to "command", suitable for the first argument
21 * to execvp(). Returns allocated memory that must be free()d.
22 * argp - Index to last element (NULL) of returned argv[] array.
23 *
24 * Our basic algorithm is this:
25 *
26 * - If there are no spaces or shell metacharacters in "command", then
27 * take it as-is.
28 * - If there are spaces in command, space-split command string.
29 * - If we have shell metacharacters, run the command using
30 * /bin/sh -c 'command "$@"'. In this case, any additional arguments
31 * appended to the arglist will be expanded by "$@".
32 *
33 * In all cases additional arguments can be added to the argv[] array.
34 */
35
36 /* Shell metacharacters we use to trigger a call to the shell */
37
38 #define METACHARS "$&*(){}[]'\";\\|?<>~`\n"
39
40 char **
41 argsplit(char *command, char **file, int *argp)
42 {
43 char **argvarray, *p;
44 int space = 0, metachar = 0, i;
45
46 for (p = command; *p; p++) {
47 if (*p == ' ' || *p == '\t') {
48 space = 1;
49 } else if (strchr(METACHARS, *p)) {
50 metachar = 1;
51 break;
52 }
53 }
54
55 argvarray = (char **) mh_xmalloc((sizeof(char **) * (MAXARGS + 5)));
56
57 /*
58 * The simple case - no spaces or shell metacharacters
59 */
60
61 if (!space && !metachar) {
62 argvarray[0] = getcpy(r1bindex(command, '/'));
63 argvarray[1] = NULL;
64 *file = mh_xstrdup(command);
65 if (argp)
66 *argp = 1;
67 return argvarray;
68 }
69
70 /*
71 * Spaces, but no shell metacharacters; space-split into separate
72 * arguments
73 */
74
75 if (space && !metachar) {
76 char **split;
77 p = mh_xstrdup(command);
78 split = brkstring(p, " \t", NULL);
79 if (split[0] == NULL) {
80 adios(NULL, "Invalid blank command found");
81 }
82 argvarray[0] = mh_xstrdup(r1bindex(split[0], '/'));
83 for (i = 1; split[i] != NULL; i++) {
84 if (i > MAXARGS) {
85 adios(NULL, "Command exceeded argument limit");
86 }
87 argvarray[i] = mh_xstrdup(split[i]);
88 }
89 argvarray[i] = NULL;
90 *file = mh_xstrdup(split[0]);
91 if (argp)
92 *argp = i;
93 free(p);
94 return argvarray;
95 }
96
97 /*
98 * Remaining option - pass to the shell.
99 *
100 * Some notes here:
101 *
102 * - The command passed to "sh -c" is actually:
103 * command "$@"
104 *
105 * If there are additional arguments they will be expanded by the
106 * shell, otherwise "$@" expands to nothing.
107 *
108 * - Every argument after the -c string gets put into positional
109 * parameters starting at $0, but $@ starts expanding with $1.
110 * So we put in a dummy argument (we just use /bin/sh)
111 */
112
113 *file = mh_xstrdup("/bin/sh");
114 argvarray[0] = mh_xstrdup("sh");
115 argvarray[1] = mh_xstrdup("-c");
116 argvarray[2] = mh_xstrdup(command);
117 argvarray[2] = add(" \"$@\"", argvarray[2]);
118 argvarray[3] = mh_xstrdup("/bin/sh");
119 argvarray[4] = NULL;
120
121 if (argp)
122 *argp = 4;
123
124 return argvarray;
125 }
126
127 /*
128 * Free our argument array
129 */
130
131 void
132 arglist_free(char *command, char **argvarray)
133 {
134 int i;
135
136 mh_xfree(command);
137
138 if (argvarray != NULL) {
139 for (i = 0; argvarray[i] != NULL; i++)
140 free(argvarray[i]);
141 free(argvarray);
142 }
143 }
144
145 /*
146 * Similar in functionality to argsplit, but is designed to deal with
147 * a msgs_array.
148 */
149
150 void
151 argsplit_msgarg(struct msgs_array *msgs, char *command, char **program)
152 {
153 int argp, i;
154 char **vec;
155
156 vec = argsplit(command, program, &argp);
157
158 /*
159 * As usual, there is lousy memory management in nmh. Nothing ever
160 * free's the msgs_array, and a lot of the arguments are allocated
161 * from static memory. I could have app_msgarg() allocate new
162 * memory for each pointer, but for now I decided to stick with
163 * the existing interface; maybe that will be revisited later.
164 * So we'll just copy over our pointers and free the pointer list
165 * (not the actual pointers themselves). Note that we don't
166 * include a trailing NULL, since we are expecting the application
167 * to take care of that.
168 */
169
170 for (i = 0; i < argp; i++) {
171 app_msgarg(msgs, vec[i]);
172 }
173
174 free(vec);
175 }
176
177 /*
178 * Insert a arglist vector into the beginning of an struct msgs array
179 *
180 * Uses by some programs (e.g., show) who want to decide which proc
181 * to use after the argument vector has been constructed
182 */
183
184 #ifndef MAXMSGS
185 #define MAXMSGS 256
186 #endif
187
188 void
189 argsplit_insert(struct msgs_array *msgs, char *command, char **program)
190 {
191 int argp, i;
192 char **vec;
193
194 vec = argsplit(command, program, &argp);
195
196 /*
197 * Okay, we want to shuffle all of our arguments down so we have room
198 * for argp number of arguments. This means we need to know about
199 * msgs_array internals. If that changes, we need to change this
200 * code here.
201 */
202
203 if (msgs->size + argp >= msgs->max) {
204 msgs->max += MAXMSGS > argp ? MAXMSGS : argp;
205 msgs->msgs = mh_xrealloc(msgs->msgs, msgs->max * sizeof(*msgs->msgs));
206 }
207
208 for (i = msgs->size - 1; i >= 0; i--)
209 msgs->msgs[i + argp] = msgs->msgs[i];
210
211 msgs->size += argp;
212
213 /*
214 * Now fill in the arguments at the beginning of the vector.
215 */
216
217 for (i = 0; i < argp; i++)
218 msgs->msgs[i] = vec[i];
219
220 free(vec);
221 }