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