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