wordexp.c revision 1.2 1 1.2 christos /* $NetBSD: wordexp.c,v 1.2 2005/11/29 03:11:59 christos Exp $ */
2 1.1 seb
3 1.1 seb /*-
4 1.1 seb * Copyright (c) 2002 Tim J. Robbins.
5 1.1 seb * All rights reserved.
6 1.1 seb *
7 1.1 seb * Redistribution and use in source and binary forms, with or without
8 1.1 seb * modification, are permitted provided that the following conditions
9 1.1 seb * are met:
10 1.1 seb * 1. Redistributions of source code must retain the above copyright
11 1.1 seb * notice, this list of conditions and the following disclaimer.
12 1.1 seb * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 seb * notice, this list of conditions and the following disclaimer in the
14 1.1 seb * documentation and/or other materials provided with the distribution.
15 1.1 seb *
16 1.1 seb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 1.1 seb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 1.1 seb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.1 seb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 1.1 seb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.1 seb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.1 seb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.1 seb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.1 seb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.1 seb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.1 seb * SUCH DAMAGE.
27 1.1 seb */
28 1.1 seb
29 1.1 seb #include "namespace.h"
30 1.1 seb #include <sys/cdefs.h>
31 1.1 seb #include <sys/types.h>
32 1.1 seb #include <assert.h>
33 1.1 seb #include <sys/wait.h>
34 1.1 seb #include <fcntl.h>
35 1.1 seb #include <paths.h>
36 1.1 seb #include <stdio.h>
37 1.1 seb #include <stdlib.h>
38 1.1 seb #include <string.h>
39 1.1 seb #include <unistd.h>
40 1.1 seb #include <wordexp.h>
41 1.1 seb
42 1.1 seb #if defined(LIBC_SCCS) && !defined(lint)
43 1.1 seb #if 0
44 1.1 seb __FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libc/gen/wordexp.c,v 1.5 2004/04/09 11:32:32 tjr Exp $");
45 1.1 seb #else
46 1.2 christos __RCSID("$NetBSD: wordexp.c,v 1.2 2005/11/29 03:11:59 christos Exp $");
47 1.1 seb #endif
48 1.1 seb #endif /* LIBC_SCCS and not lint */
49 1.1 seb
50 1.1 seb static int we_askshell(const char *, wordexp_t *, int);
51 1.1 seb static int we_check(const char *, int);
52 1.1 seb
53 1.1 seb /*
54 1.1 seb * wordexp --
55 1.1 seb * Perform shell word expansion on `words' and place the resulting list
56 1.1 seb * of words in `we'. See wordexp(3).
57 1.1 seb *
58 1.1 seb */
59 1.1 seb int
60 1.1 seb wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags)
61 1.1 seb {
62 1.1 seb int error;
63 1.1 seb
64 1.1 seb _DIAGASSERT(we != NULL);
65 1.1 seb _DIAGASSERT(words != NULL);
66 1.1 seb if (flags & WRDE_REUSE)
67 1.1 seb wordfree(we);
68 1.1 seb if ((flags & WRDE_APPEND) == 0) {
69 1.1 seb we->we_wordc = 0;
70 1.1 seb we->we_wordv = NULL;
71 1.1 seb we->we_strings = NULL;
72 1.1 seb we->we_nbytes = 0;
73 1.1 seb }
74 1.1 seb if ((error = we_check(words, flags)) != 0) {
75 1.1 seb wordfree(we);
76 1.1 seb return (error);
77 1.1 seb }
78 1.1 seb if ((error = we_askshell(words, we, flags)) != 0) {
79 1.1 seb wordfree(we);
80 1.1 seb return (error);
81 1.1 seb }
82 1.1 seb return (0);
83 1.1 seb }
84 1.1 seb
85 1.1 seb /*
86 1.1 seb * we_askshell --
87 1.1 seb * Use the `wordexp' /bin/sh builtin function to do most of the work
88 1.1 seb * in expanding the word string. This function is complicated by
89 1.1 seb * memory management.
90 1.1 seb */
91 1.1 seb static int
92 1.1 seb we_askshell(const char *words, wordexp_t *we, int flags)
93 1.1 seb {
94 1.1 seb int pdes[2]; /* Pipe to child */
95 1.1 seb size_t nwords, nbytes; /* Number of words, bytes from child */
96 1.1 seb int i; /* Handy integer */
97 1.1 seb size_t sofs; /* Offset into we->we_strings */
98 1.1 seb size_t vofs; /* Offset into we->we_wordv */
99 1.1 seb pid_t pid; /* Process ID of child */
100 1.1 seb int status; /* Child exit status */
101 1.2 christos const char *ifs; /* IFS env. var. */
102 1.1 seb char *np, *p; /* Handy pointers */
103 1.1 seb char *nstrings; /* Temporary for realloc() */
104 1.1 seb char **nwv; /* Temporary for realloc() */
105 1.1 seb FILE *fp; /* Stream to read pipe */
106 1.1 seb extern char **environ;
107 1.1 seb char *cmd;
108 1.1 seb
109 1.1 seb if ((ifs = getenv("IFS")) == NULL)
110 1.1 seb ifs = " \t\n";
111 1.1 seb if (asprintf(&cmd, "wordexp%c%s\n", *ifs, words) < 0)
112 1.1 seb return (WRDE_NOSPACE);
113 1.1 seb if (pipe(pdes) < 0) {
114 1.1 seb free(cmd);
115 1.1 seb return (WRDE_ERRNO);
116 1.1 seb }
117 1.1 seb if ((fp = fdopen(pdes[0], "r")) == NULL) {
118 1.1 seb free(cmd);
119 1.1 seb return (WRDE_ERRNO);
120 1.1 seb }
121 1.1 seb if ((pid = fork()) < 0) {
122 1.1 seb free(cmd);
123 1.1 seb fclose(fp);
124 1.1 seb close(pdes[1]);
125 1.1 seb return (WRDE_ERRNO);
126 1.1 seb }
127 1.1 seb else if (pid == 0) {
128 1.1 seb /*
129 1.1 seb * We are the child; just get /bin/sh to run the wordexp
130 1.1 seb * builtin on `words'.
131 1.1 seb */
132 1.1 seb int devnull;
133 1.1 seb
134 1.1 seb close(pdes[0]);
135 1.1 seb if (pdes[1] != STDOUT_FILENO) {
136 1.1 seb if (dup2(pdes[1], STDOUT_FILENO) < 0)
137 1.1 seb _exit(1);
138 1.1 seb close(pdes[1]);
139 1.1 seb }
140 1.1 seb if ((flags & WRDE_SHOWERR) == 0) {
141 1.1 seb if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0666)) < 0)
142 1.1 seb _exit(1);
143 1.1 seb if (dup2(devnull, STDERR_FILENO) < 0)
144 1.1 seb _exit(1);
145 1.1 seb close(devnull);
146 1.1 seb }
147 1.1 seb execle(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
148 1.1 seb "-c", cmd, (char *)NULL, environ);
149 1.1 seb _exit(1);
150 1.1 seb }
151 1.1 seb
152 1.1 seb /*
153 1.1 seb * We are the parent; read the output of the shell wordexp function,
154 1.1 seb * which is a decimal word count, an null, a decimal byte count,
155 1.1 seb * (not including terminating null bytes), a null and then followed
156 1.1 seb * by the expanded words separated by nulls.
157 1.1 seb */
158 1.1 seb free(cmd);
159 1.1 seb close(pdes[1]);
160 1.1 seb /* read the word count */
161 1.1 seb nwords = 0;
162 1.1 seb while ((i = getc(fp)) != EOF) {
163 1.1 seb if (i == '\0')
164 1.1 seb break;
165 1.1 seb nwords *= 10;
166 1.1 seb nwords += (i - '0');
167 1.1 seb }
168 1.1 seb /* read the byte count */
169 1.1 seb nbytes = 0;
170 1.1 seb while ((i = getc(fp)) != EOF) {
171 1.1 seb if (i == '\0')
172 1.1 seb break;
173 1.1 seb nbytes *= 10;
174 1.1 seb nbytes += (i - '0');
175 1.1 seb }
176 1.1 seb if (i == EOF) {
177 1.1 seb fclose(fp);
178 1.1 seb waitpid(pid, &status, 0);
179 1.1 seb return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
180 1.1 seb }
181 1.1 seb nbytes += nwords;
182 1.1 seb
183 1.1 seb /*
184 1.1 seb * Allocate or reallocate (when flags & WRDE_APPEND) the word vector
185 1.1 seb * and string storage buffers for the expanded words we're about to
186 1.1 seb * read from the child.
187 1.1 seb */
188 1.1 seb sofs = we->we_nbytes;
189 1.1 seb vofs = we->we_wordc;
190 1.1 seb if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
191 1.1 seb vofs += we->we_offs;
192 1.1 seb we->we_wordc += nwords;
193 1.1 seb we->we_nbytes += nbytes;
194 1.1 seb if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
195 1.1 seb (flags & WRDE_DOOFFS ? we->we_offs : 0)) *
196 1.1 seb sizeof(char *))) == NULL) {
197 1.1 seb fclose(fp);
198 1.1 seb waitpid(pid, &status, 0);
199 1.1 seb return (WRDE_NOSPACE);
200 1.1 seb }
201 1.1 seb we->we_wordv = nwv;
202 1.1 seb if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
203 1.1 seb fclose(fp);
204 1.1 seb waitpid(pid, &status, 0);
205 1.1 seb return (WRDE_NOSPACE);
206 1.1 seb }
207 1.1 seb for (i = 0; i < vofs; i++)
208 1.1 seb if (we->we_wordv[i] != NULL)
209 1.1 seb we->we_wordv[i] += nstrings - we->we_strings;
210 1.1 seb we->we_strings = nstrings;
211 1.1 seb
212 1.1 seb if (fread(we->we_strings + sofs, sizeof(char), nbytes, fp) != nbytes) {
213 1.1 seb fclose(fp);
214 1.1 seb waitpid(pid, &status, 0);
215 1.1 seb return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
216 1.1 seb }
217 1.1 seb
218 1.1 seb if (waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
219 1.1 seb WEXITSTATUS(status) != 0) {
220 1.1 seb fclose(fp);
221 1.1 seb return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
222 1.1 seb }
223 1.1 seb fclose(fp);
224 1.1 seb
225 1.1 seb /*
226 1.1 seb * Break the null-terminated expanded word strings out into
227 1.1 seb * the vector.
228 1.1 seb */
229 1.1 seb if (vofs == 0 && flags & WRDE_DOOFFS)
230 1.1 seb while (vofs < we->we_offs)
231 1.1 seb we->we_wordv[vofs++] = NULL;
232 1.1 seb p = we->we_strings + sofs;
233 1.1 seb while (nwords-- != 0) {
234 1.1 seb we->we_wordv[vofs++] = p;
235 1.1 seb if ((np = memchr(p, '\0', nbytes)) == NULL)
236 1.1 seb return (WRDE_NOSPACE); /* XXX */
237 1.1 seb nbytes -= np - p + 1;
238 1.1 seb p = np + 1;
239 1.1 seb }
240 1.1 seb we->we_wordv[vofs] = NULL;
241 1.1 seb
242 1.1 seb return (0);
243 1.1 seb }
244 1.1 seb
245 1.1 seb /*
246 1.1 seb * we_check --
247 1.1 seb * Check that the string contains none of the following unquoted
248 1.1 seb * special characters: <newline> |&;<>(){}
249 1.1 seb * or command substitutions when WRDE_NOCMD is set in flags.
250 1.1 seb */
251 1.1 seb static int
252 1.1 seb we_check(const char *words, int flags)
253 1.1 seb {
254 1.1 seb char c;
255 1.1 seb int dquote, level, quote, squote;
256 1.1 seb
257 1.1 seb quote = squote = dquote = 0;
258 1.1 seb while ((c = *words++) != '\0') {
259 1.1 seb switch (c) {
260 1.1 seb case '\\':
261 1.1 seb quote ^= 1;
262 1.1 seb continue;
263 1.1 seb case '\'':
264 1.1 seb if (quote + dquote == 0)
265 1.1 seb squote ^= 1;
266 1.1 seb break;
267 1.1 seb case '"':
268 1.1 seb if (quote + squote == 0)
269 1.1 seb dquote ^= 1;
270 1.1 seb break;
271 1.1 seb case '`':
272 1.1 seb if (quote + squote == 0 && flags & WRDE_NOCMD)
273 1.1 seb return (WRDE_CMDSUB);
274 1.1 seb while ((c = *words++) != '\0' && c != '`')
275 1.1 seb if (c == '\\' && (c = *words++) == '\0')
276 1.1 seb break;
277 1.1 seb if (c == '\0')
278 1.1 seb return (WRDE_SYNTAX);
279 1.1 seb break;
280 1.1 seb case '|': case '&': case ';': case '<': case '>':
281 1.1 seb case '{': case '}': case '(': case ')': case '\n':
282 1.1 seb if (quote + squote + dquote == 0)
283 1.1 seb return (WRDE_BADCHAR);
284 1.1 seb break;
285 1.1 seb case '$':
286 1.1 seb if ((c = *words++) == '\0')
287 1.1 seb break;
288 1.1 seb else if (quote + squote == 0 && c == '(') {
289 1.1 seb if (flags & WRDE_NOCMD && *words != '(')
290 1.1 seb return (WRDE_CMDSUB);
291 1.1 seb level = 1;
292 1.1 seb while ((c = *words++) != '\0') {
293 1.1 seb if (c == '\\') {
294 1.1 seb if ((c = *words++) == '\0')
295 1.1 seb break;
296 1.1 seb } else if (c == '(')
297 1.1 seb level++;
298 1.1 seb else if (c == ')' && --level == 0)
299 1.1 seb break;
300 1.1 seb }
301 1.1 seb if (c == '\0' || level != 0)
302 1.1 seb return (WRDE_SYNTAX);
303 1.1 seb } else if (quote + squote == 0 && c == '{') {
304 1.1 seb level = 1;
305 1.1 seb while ((c = *words++) != '\0') {
306 1.1 seb if (c == '\\') {
307 1.1 seb if ((c = *words++) == '\0')
308 1.1 seb break;
309 1.1 seb } else if (c == '{')
310 1.1 seb level++;
311 1.1 seb else if (c == '}' && --level == 0)
312 1.1 seb break;
313 1.1 seb }
314 1.1 seb if (c == '\0' || level != 0)
315 1.1 seb return (WRDE_SYNTAX);
316 1.1 seb } else
317 1.1 seb c = *--words;
318 1.1 seb break;
319 1.1 seb default:
320 1.1 seb break;
321 1.1 seb }
322 1.1 seb quote = 0;
323 1.1 seb }
324 1.1 seb if (quote + squote + dquote != 0)
325 1.1 seb return (WRDE_SYNTAX);
326 1.1 seb
327 1.1 seb return (0);
328 1.1 seb }
329 1.1 seb
330 1.1 seb /*
331 1.1 seb * wordfree --
332 1.1 seb * Free the result of wordexp(). See wordexp(3).
333 1.1 seb *
334 1.1 seb */
335 1.1 seb void
336 1.1 seb wordfree(wordexp_t *we)
337 1.1 seb {
338 1.1 seb _DIAGASSERT(we != NULL);
339 1.1 seb free(we->we_wordv);
340 1.1 seb free(we->we_strings);
341 1.1 seb we->we_wordv = NULL;
342 1.1 seb we->we_strings = NULL;
343 1.1 seb we->we_nbytes = 0;
344 1.1 seb we->we_wordc = 0;
345 1.1 seb }
346