xref: /illumos-gate/usr/src/lib/libc/port/regex/wordexp.c (revision f808c858)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This code is MKS code ported to Solaris originally with minimum
30  * modifications so that upgrades from MKS would readily integrate.
31  * The MKS basis for this modification was:
32  *
33  *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
34  *
35  * Additional modifications have been made to this code to make it
36  * 64-bit clean.
37  */
38 
39 /*
40  * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
41  *
42  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
43  *
44  */
45 
46 #include "synonyms.h"
47 #include <stdio.h>
48 #include <unistd.h>
49 #include <limits.h>
50 #include <fcntl.h>
51 #include <limits.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sys/wait.h>
55 #include <unistd.h>
56 #include <wordexp.h>
57 #include <stdio.h>
58 #include <errno.h>
59 
60 #define	INITIAL	8		/* initial pathv allocation */
61 #define	BUFSZ	256		/* allocation unit of the line buffer */
62 
63 static int	append(wordexp_t *, char *);
64 
65 extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
66 
67 /*
68  * Do word expansion.
69  * We just pass our arguments to shell with -E option.  Note that the
70  * underlying shell must recognize the -E option, and do the right thing
71  * with it.
72  */
73 int
74 wordexp(const char *word, wordexp_t *wp, int flags)
75 {
76 	static char options[9] = "-";
77 	static char *args[4];
78 	const char *path;
79 	wordexp_t wptmp;
80 	size_t si;
81 	int i;
82 	pid_t pid;
83 	char *line, *eob, *cp;		/* word from shell */
84 	int rv = WRDE_ERRNO;
85 	int status;
86 	int pv[2];			/* pipe from shell stdout */
87 	FILE *fp;			/* pipe read stream */
88 	char *optendp = options+1;
89 	int serrno, tmpalloc;
90 	char *wd = NULL;
91 
92 	static const char *sun_path = "/bin/ksh";
93 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
94 
95 	/*
96 	 * Do absolute minimum neccessary for the REUSE flag. Eventually
97 	 * want to be able to actually avoid excessive malloc calls.
98 	 */
99 	if (flags & WRDE_REUSE)
100 		wordfree(wp);
101 
102 	/*
103 	 * Initialize wordexp_t
104 	 *
105 	 * XPG requires that the struct pointed to by wp not be modified
106 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
107 	 * So we work with wptmp, and only copy wptmp to wp if one of the
108 	 * previously mentioned conditions is satisfied.
109 	 */
110 	wptmp = *wp;
111 
112 	/*
113 	 * Man page says:
114 	 * 2. All of the calls must set WRDE_DOOFFS, or  all  must  not
115 	 *    set it.
116 	 * Therefore, if it's not set, we_offs will always be reset.
117 	 */
118 	if ((flags & WRDE_DOOFFS) == 0)
119 		wptmp.we_offs = 0;
120 
121 	/*
122 	 * If we get APPEND|REUSE, how should we do?
123 	 * allocating buffer anyway to avoid segfault.
124 	 */
125 	tmpalloc = 0;
126 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
127 		wptmp.we_wordc = 0;
128 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
129 		wptmp.we_wordv = (char **)malloc(
130 					sizeof (char *) * wptmp.we_wordn);
131 		if (wptmp.we_wordv == NULL)
132 			return (WRDE_NOSPACE);
133 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
134 		for (si = 0; si < wptmp.we_offs; si++)
135 			wptmp.we_wordv[si] = NULL;
136 		tmpalloc = 1;
137 	}
138 
139 	/*
140 	 * Turn flags into shell options
141 	 */
142 	*optendp++ = (char)0x05;		/* ksh -^E */
143 	if (flags & WRDE_UNDEF)
144 		*optendp++ = 'u';
145 	if (flags & WRDE_NOCMD)
146 		*optendp++ = 'N';
147 	*optendp = '\0';
148 
149 	if (getenv("PWD") == NULL) {
150 		if ((wd = malloc(PATH_MAX + 4)) == NULL)
151 			goto cleanup;
152 		(void) strcpy(wd, "PWD=");
153 		if (getcwd(&wd[4], PATH_MAX) == NULL)
154 			(void) strcpy(&wd[4], "/");
155 	}
156 
157 	/*
158 	 * Set up pipe from shell stdout to "fp" for us
159 	 */
160 	if (pipe(pv) < 0)
161 		goto cleanup;
162 
163 	/*
164 	 * Fork/exec shell with -E word
165 	 */
166 
167 	if ((pid = fork1()) == -1) {
168 		serrno = errno;
169 		(void) close(pv[0]);
170 		(void) close(pv[1]);
171 		errno = serrno;
172 		goto cleanup;
173 	}
174 
175 	if (pid == 0) { 	/* child */
176 		if (wd != NULL) {
177 			/*
178 			 * fork1 handler takes care of __environ_lock.
179 			 * Thus we can safely call putenv().
180 			 */
181 			(void) putenv(wd);
182 		}
183 
184 		(void) dup2(pv[1], 1);
185 		(void) close(pv[0]);
186 		(void) close(pv[1]);
187 
188 		if ((flags & WRDE_SHOWERR) == 0) {
189 			int devnull;
190 			devnull = open("/dev/null", O_WRONLY);
191 			(void) dup2(devnull, 2);
192 			if (devnull != 2)
193 				(void) close(devnull);
194 		}
195 
196 		path = __xpg4 ? xpg4_path : sun_path;
197 		args[0] = strrchr(path, '/') + 1;
198 		args[1] = options;
199 		args[2] = (char *)word;
200 		args[3] = NULL;
201 
202 		(void) execv(path, args);
203 		_exit(127);
204 	}
205 
206 	(void) close(pv[1]);
207 
208 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
209 		serrno = errno;
210 		(void) close(pv[0]);
211 		errno = serrno;
212 		goto wait_cleanup;
213 	}
214 
215 	/*
216 	 * Read words from shell, separated with '\0'.
217 	 * Since there is no way to disable IFS splitting,
218 	 * it would be possible to separate the output with '\n'.
219 	 */
220 	cp = line = malloc(BUFSZ);
221 	if (line == NULL) {
222 		(void) fclose(fp);
223 		rv = WRDE_NOSPACE;
224 		goto wait_cleanup;
225 	}
226 	eob = line + BUFSZ;
227 
228 	rv = 0;
229 	while ((i = getc(fp)) != EOF) {
230 		*cp++ = (char)i;
231 		if (i == '\0') {
232 			cp = line;
233 			if ((rv = append(&wptmp, cp)) != 0) {
234 				break;
235 			}
236 		}
237 		if (cp == eob) {
238 			size_t bs = (eob - line);
239 			char *nl;
240 
241 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
242 				rv = WRDE_NOSPACE;
243 				break;
244 			}
245 			line = nl;
246 			cp = line + bs;
247 			eob = cp + BUFSZ;
248 		}
249 	}
250 
251 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
252 
253 	free(line);
254 	(void) fclose(fp);	/* kill shell if still writing */
255 
256 wait_cleanup:
257 	if (waitpid(pid, &status, 0) == -1)
258 		rv = WRDE_ERRNO;
259 	else if (rv == 0)
260 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
261 
262 cleanup:
263 	if (rv == 0)
264 		*wp = wptmp;
265 	else {
266 		if (tmpalloc)
267 			wordfree(&wptmp);
268 	}
269 
270 	if (wd)
271 		free(wd);
272 	/*
273 	 * Map ksh errors to wordexp() errors
274 	 */
275 	if (rv == 4)
276 		rv = WRDE_CMDSUB;
277 	else if (rv == 5)
278 		rv = WRDE_BADVAL;
279 	else if (rv == 6)
280 		rv = WRDE_SYNTAX;
281 	return (rv);
282 }
283 
284 /*
285  * Append a word to the wordexp_t structure, growing it as neccessary.
286  */
287 static int
288 append(wordexp_t *wp, char *str)
289 {
290 	char *cp;
291 	char **nwp;
292 
293 	/*
294 	 * We will be adding one entry and later adding
295 	 * one more NULL. So we need 2 more free slots.
296 	 */
297 	if ((wp->we_wordp + wp->we_wordc) ==
298 		(wp->we_wordv + wp->we_wordn - 1)) {
299 		nwp = realloc(wp->we_wordv,
300 			(wp->we_wordn + INITIAL) * sizeof (char *));
301 		if (nwp == NULL)
302 			return (WRDE_NOSPACE);
303 		wp->we_wordn += INITIAL;
304 		wp->we_wordv = nwp;
305 		wp->we_wordp = wp->we_wordv + wp->we_offs;
306 	}
307 	if ((cp = strdup(str)) == NULL)
308 		return (WRDE_NOSPACE);
309 	wp->we_wordp[wp->we_wordc++] = cp;
310 	return (0);
311 }
312 
313 /*
314  * Free all space owned by wordexp_t.
315  */
316 void
317 wordfree(wordexp_t *wp)
318 {
319 	size_t i;
320 
321 	if (wp->we_wordv == NULL)
322 		return;
323 	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
324 		free(wp->we_wordv[i]);
325 	free((void *)wp->we_wordv);
326 	wp->we_wordc = 0;
327 	wp->we_wordv = NULL;
328 }
329