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