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