xref: /illumos-gate/usr/src/lib/libc/port/regex/wordexp.c (revision 00ae5933)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5004388ebScasper  * Common Development and Distribution License (the "License").
6004388ebScasper  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21ec6bbcf8Sraf 
227c478bd9Sstevel@tonic-gate /*
23ead1f93eSLiane Praza  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  * This code is MKS code ported to Solaris originally with minimum
297c478bd9Sstevel@tonic-gate  * modifications so that upgrades from MKS would readily integrate.
307c478bd9Sstevel@tonic-gate  * The MKS basis for this modification was:
317c478bd9Sstevel@tonic-gate  *
327c478bd9Sstevel@tonic-gate  *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * Additional modifications have been made to this code to make it
357c478bd9Sstevel@tonic-gate  * 64-bit clean.
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate /*
397c478bd9Sstevel@tonic-gate  * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
42da2e3ebdSchin  * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
437c478bd9Sstevel@tonic-gate  */
447c478bd9Sstevel@tonic-gate 
457257d1b4Sraf #pragma	weak _wordexp = wordexp
467257d1b4Sraf #pragma	weak _wordfree = wordfree
4764e3e6f9Scraigm 
487257d1b4Sraf #include "lint.h"
497c478bd9Sstevel@tonic-gate #include <stdio.h>
507c478bd9Sstevel@tonic-gate #include <unistd.h>
517c478bd9Sstevel@tonic-gate #include <limits.h>
527c478bd9Sstevel@tonic-gate #include <fcntl.h>
537c478bd9Sstevel@tonic-gate #include <limits.h>
547c478bd9Sstevel@tonic-gate #include <stdlib.h>
55da2e3ebdSchin #include <alloca.h>
567c478bd9Sstevel@tonic-gate #include <string.h>
577c478bd9Sstevel@tonic-gate #include <sys/wait.h>
58a574db85Sraf #include <pthread.h>
597c478bd9Sstevel@tonic-gate #include <unistd.h>
607c478bd9Sstevel@tonic-gate #include <wordexp.h>
617c478bd9Sstevel@tonic-gate #include <stdio.h>
62657b1f3dSraf #include <spawn.h>
637c478bd9Sstevel@tonic-gate #include <errno.h>
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate #define	INITIAL	8		/* initial pathv allocation */
667c478bd9Sstevel@tonic-gate #define	BUFSZ	256		/* allocation unit of the line buffer */
677c478bd9Sstevel@tonic-gate 
681c7ddb37SRoger A. Faulkner /*
691c7ddb37SRoger A. Faulkner  * Needs no locking if fetched only once.
701c7ddb37SRoger A. Faulkner  * See getenv()/putenv()/setenv().
711c7ddb37SRoger A. Faulkner  */
721c7ddb37SRoger A. Faulkner extern	const char **_environ;
731c7ddb37SRoger A. Faulkner 
74da2e3ebdSchin /* Local prototypes */
75da2e3ebdSchin static int append(wordexp_t *, char *);
76da2e3ebdSchin 
77da2e3ebdSchin /*
78da2e3ebdSchin  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
79da2e3ebdSchin  * We'll add this later (and a matching multibyte/widechar version)
80da2e3ebdSchin  * as normal libc function.
81da2e3ebdSchin  *
82da2e3ebdSchin  * Copy string s2 to s1.  s1 must be large enough.
83da2e3ebdSchin  * return s1-1 (position of string terminator ('\0') in destination buffer).
84da2e3ebdSchin  */
85da2e3ebdSchin static char *
mystpcpy(char * s1,const char * s2)86da2e3ebdSchin mystpcpy(char *s1, const char *s2)
87da2e3ebdSchin {
88*00ae5933SToomas Soome 	while ((*s1++ = *s2++) != '\0')
89da2e3ebdSchin 		;
90da2e3ebdSchin 	return (s1-1);
91da2e3ebdSchin }
92da2e3ebdSchin 
93da2e3ebdSchin /*
94da2e3ebdSchin  * Do word expansion.
951c7ddb37SRoger A. Faulkner  * We build a mini-script in |buff| which takes care of all details,
96da2e3ebdSchin  * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
97da2e3ebdSchin  * the word expansion itself.
98da2e3ebdSchin  */
99da2e3ebdSchin int
wordexp(const char * word,wordexp_t * wp,int flags)100da2e3ebdSchin wordexp(const char *word, wordexp_t *wp, int flags)
101da2e3ebdSchin {
1021c7ddb37SRoger A. Faulkner 	const char *path = "/usr/bin/ksh93";
103da2e3ebdSchin 	wordexp_t wptmp;
104da2e3ebdSchin 	size_t si;
105da2e3ebdSchin 	pid_t pid;
1061c7ddb37SRoger A. Faulkner 	char *line, *eob, *cp;		/* word from shell */
107da2e3ebdSchin 	int rv = WRDE_ERRNO;
108da2e3ebdSchin 	int status;
1091c7ddb37SRoger A. Faulkner 	int pv[2];			/* pipe from shell stdout */
1101c7ddb37SRoger A. Faulkner 	FILE *fp;			/* pipe read stream */
1111c7ddb37SRoger A. Faulkner 	int tmpalloc;
1121c7ddb37SRoger A. Faulkner 	char *wd = NULL;
1131c7ddb37SRoger A. Faulkner 	const char **env = NULL;
1141c7ddb37SRoger A. Faulkner 	const char **envp;
1151c7ddb37SRoger A. Faulkner 	const char *ev;
1161c7ddb37SRoger A. Faulkner 	int n;
1171c7ddb37SRoger A. Faulkner 	posix_spawnattr_t attr;
1181c7ddb37SRoger A. Faulkner 	posix_spawn_file_actions_t fact;
1191c7ddb37SRoger A. Faulkner 	int error;
120a574db85Sraf 	int cancel_state;
1211c7ddb37SRoger A. Faulkner 	size_t bufflen; /* Length of |buff| */
1221c7ddb37SRoger A. Faulkner 	char *buff;
1231c7ddb37SRoger A. Faulkner 	char *currbuffp; /* Current position of '\0' in |buff| */
1241c7ddb37SRoger A. Faulkner 	char *args[10];
1251c7ddb37SRoger A. Faulkner 	int i;
126da2e3ebdSchin 
127da2e3ebdSchin 	/*
128da2e3ebdSchin 	 * Do absolute minimum necessary for the REUSE flag. Eventually
129da2e3ebdSchin 	 * want to be able to actually avoid excessive malloc calls.
130da2e3ebdSchin 	 */
131da2e3ebdSchin 	if (flags & WRDE_REUSE)
132da2e3ebdSchin 		wordfree(wp);
133da2e3ebdSchin 
134da2e3ebdSchin 	/*
135da2e3ebdSchin 	 * Initialize wordexp_t
136da2e3ebdSchin 	 *
137da2e3ebdSchin 	 * XPG requires that the struct pointed to by wp not be modified
138da2e3ebdSchin 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
139da2e3ebdSchin 	 * So we work with wptmp, and only copy wptmp to wp if one of the
140da2e3ebdSchin 	 * previously mentioned conditions is satisfied.
141da2e3ebdSchin 	 */
142da2e3ebdSchin 	wptmp = *wp;
143da2e3ebdSchin 
144da2e3ebdSchin 	/*
145da2e3ebdSchin 	 * Man page says:
146da2e3ebdSchin 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
1471c7ddb37SRoger A. Faulkner 	 *    set it.
148da2e3ebdSchin 	 * Therefore, if it's not set, we_offs will always be reset.
149da2e3ebdSchin 	 */
150da2e3ebdSchin 	if ((flags & WRDE_DOOFFS) == 0)
151da2e3ebdSchin 		wptmp.we_offs = 0;
152da2e3ebdSchin 
153da2e3ebdSchin 	/*
154da2e3ebdSchin 	 * If we get APPEND|REUSE, how should we do?
1551c7ddb37SRoger A. Faulkner 	 * allocating buffer anyway to avoid segfault.
156da2e3ebdSchin 	 */
157da2e3ebdSchin 	tmpalloc = 0;
158da2e3ebdSchin 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
159da2e3ebdSchin 		wptmp.we_wordc = 0;
160da2e3ebdSchin 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
1611c7ddb37SRoger A. Faulkner 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
162da2e3ebdSchin 		if (wptmp.we_wordv == NULL)
163da2e3ebdSchin 			return (WRDE_NOSPACE);
164da2e3ebdSchin 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
165da2e3ebdSchin 		for (si = 0; si < wptmp.we_offs; si++)
166da2e3ebdSchin 			wptmp.we_wordv[si] = NULL;
167da2e3ebdSchin 		tmpalloc = 1;
168da2e3ebdSchin 	}
169da2e3ebdSchin 
170ec6bbcf8Sraf 	/*
171ec6bbcf8Sraf 	 * The UNIX98 Posix conformance test suite requires
1721c7ddb37SRoger A. Faulkner 	 * |wordexp()| to not be a cancellation point.
173ec6bbcf8Sraf 	 */
174ec6bbcf8Sraf 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
175ec6bbcf8Sraf 
176da2e3ebdSchin 	/*
1771c7ddb37SRoger A. Faulkner 	 * Make sure PWD is in the environment.
178da2e3ebdSchin 	 */
1791c7ddb37SRoger A. Faulkner 	if ((envp = _environ) == NULL) {
1801c7ddb37SRoger A. Faulkner 		/* can happen when processing a SunOS 4.x AOUT file */
1811c7ddb37SRoger A. Faulkner 		ev = NULL;
1821c7ddb37SRoger A. Faulkner 		n = 0;
1831c7ddb37SRoger A. Faulkner 	} else {
1841c7ddb37SRoger A. Faulkner 		for (n = 0; (ev = envp[n]) != NULL; n++) {
1851c7ddb37SRoger A. Faulkner 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
1861c7ddb37SRoger A. Faulkner 				break;
1871c7ddb37SRoger A. Faulkner 		}
1881c7ddb37SRoger A. Faulkner 	}
1891c7ddb37SRoger A. Faulkner 	if (ev == NULL) {	/* PWD missing from the environment */
1901c7ddb37SRoger A. Faulkner 		/* allocate a new environment */
1911c7ddb37SRoger A. Faulkner 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
1921c7ddb37SRoger A. Faulkner 		    (wd = malloc(PATH_MAX + 4)) == NULL)
1931c7ddb37SRoger A. Faulkner 			goto cleanup;
1941c7ddb37SRoger A. Faulkner 		for (i = 0; i < n; i++)
1951c7ddb37SRoger A. Faulkner 			env[i] = envp[i];
1961c7ddb37SRoger A. Faulkner 		(void) strcpy(wd, "PWD=");
1971c7ddb37SRoger A. Faulkner 		if (getcwd(&wd[4], PATH_MAX) == NULL)
1981c7ddb37SRoger A. Faulkner 			(void) strcpy(&wd[4], "/");
1991c7ddb37SRoger A. Faulkner 		env[i] = wd;
2001c7ddb37SRoger A. Faulkner 		env[i + 1] = NULL;
2011c7ddb37SRoger A. Faulkner 		envp = env;
2021c7ddb37SRoger A. Faulkner 	}
203da2e3ebdSchin 
204da2e3ebdSchin 	/*
2051c7ddb37SRoger A. Faulkner 	 * Calculate size of required buffer (which is size of the
2061c7ddb37SRoger A. Faulkner 	 * input string (|word|) plus all string literals below;
2071c7ddb37SRoger A. Faulkner 	 * this value MUST be adjusted each time the literals are
2081c7ddb37SRoger A. Faulkner 	 * changed!!).
209da2e3ebdSchin 	 */
2101c7ddb37SRoger A. Faulkner 	bufflen = 165 + strlen(word);
2111c7ddb37SRoger A. Faulkner 	buff = alloca(bufflen);
2121c7ddb37SRoger A. Faulkner 	i = 0;
213da2e3ebdSchin 
2141c7ddb37SRoger A. Faulkner 	/* Start filling the buffer */
2151c7ddb37SRoger A. Faulkner 	buff[0] = '\0';
2161c7ddb37SRoger A. Faulkner 	currbuffp = buff;
217da2e3ebdSchin 
2181c7ddb37SRoger A. Faulkner 	if (flags & WRDE_UNDEF)
2191c7ddb37SRoger A. Faulkner 		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
2201c7ddb37SRoger A. Faulkner 	if ((flags & WRDE_SHOWERR) == 0) {
221da2e3ebdSchin 		/*
2221c7ddb37SRoger A. Faulkner 		 * The newline ('\n') is neccesary to make sure that
2231c7ddb37SRoger A. Faulkner 		 * the redirection to /dev/null is already active in
2241c7ddb37SRoger A. Faulkner 		 * the case the printf below contains a syntax
2251c7ddb37SRoger A. Faulkner 		 * error...
226da2e3ebdSchin 		 */
2271c7ddb37SRoger A. Faulkner 		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
2281c7ddb37SRoger A. Faulkner 	}
2291c7ddb37SRoger A. Faulkner 	/* Squish stdin */
2301c7ddb37SRoger A. Faulkner 	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
231da2e3ebdSchin 
2321c7ddb37SRoger A. Faulkner 	if (flags & WRDE_NOCMD) {
2331c7ddb37SRoger A. Faulkner 		/*
2341c7ddb37SRoger A. Faulkner 		 * Switch to restricted shell (rksh) mode here to
2351c7ddb37SRoger A. Faulkner 		 * put the word expansion into a "cage" which
2361c7ddb37SRoger A. Faulkner 		 * prevents users from executing external commands
2371c7ddb37SRoger A. Faulkner 		 * (outside those listed by ${PATH} (which we set
2381c7ddb37SRoger A. Faulkner 		 * explicitly to /usr/no/such/path/element/)).
2391c7ddb37SRoger A. Faulkner 		 */
2401c7ddb37SRoger A. Faulkner 		currbuffp = mystpcpy(currbuffp,
2411c7ddb37SRoger A. Faulkner 		    "export PATH=/usr/no/such/path/element/ ; "
2421c7ddb37SRoger A. Faulkner 		    "set -o restricted\n");
2431c7ddb37SRoger A. Faulkner 	}
244da2e3ebdSchin 
2451c7ddb37SRoger A. Faulkner 	(void) snprintf(currbuffp, bufflen,
2461c7ddb37SRoger A. Faulkner 	    "print -f '%%s\\000' -- %s", word);
247da2e3ebdSchin 
2481c7ddb37SRoger A. Faulkner 	args[i++] = strrchr(path, '/') + 1;
2491c7ddb37SRoger A. Faulkner 	args[i++] = "-c";
2501c7ddb37SRoger A. Faulkner 	args[i++] = buff;
2511c7ddb37SRoger A. Faulkner 	args[i++] = NULL;
252da2e3ebdSchin 
2531c7ddb37SRoger A. Faulkner 	if ((error = posix_spawnattr_init(&attr)) != 0) {
2541c7ddb37SRoger A. Faulkner 		errno = error;
2551c7ddb37SRoger A. Faulkner 		goto cleanup;
2561c7ddb37SRoger A. Faulkner 	}
2571c7ddb37SRoger A. Faulkner 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
2581c7ddb37SRoger A. Faulkner 		(void) posix_spawnattr_destroy(&attr);
2591c7ddb37SRoger A. Faulkner 		errno = error;
2601c7ddb37SRoger A. Faulkner 		goto cleanup;
2611c7ddb37SRoger A. Faulkner 	}
262da2e3ebdSchin 
2631c7ddb37SRoger A. Faulkner 	/*
2641c7ddb37SRoger A. Faulkner 	 * Set up pipe from shell stdout to "fp" for us
2651c7ddb37SRoger A. Faulkner 	 */
2661c7ddb37SRoger A. Faulkner 	if (pipe(pv) < 0) {
2671c7ddb37SRoger A. Faulkner 		error = errno;
2681c7ddb37SRoger A. Faulkner 		(void) posix_spawnattr_destroy(&attr);
2691c7ddb37SRoger A. Faulkner 		(void) posix_spawn_file_actions_destroy(&fact);
2701c7ddb37SRoger A. Faulkner 		errno = error;
2711c7ddb37SRoger A. Faulkner 		goto cleanup;
272da2e3ebdSchin 	}
273da2e3ebdSchin 
2741c7ddb37SRoger A. Faulkner 	/*
2751c7ddb37SRoger A. Faulkner 	 * Spawn shell
2761c7ddb37SRoger A. Faulkner 	 */
2771c7ddb37SRoger A. Faulkner 	error = posix_spawnattr_setflags(&attr,
2781c7ddb37SRoger A. Faulkner 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
2791c7ddb37SRoger A. Faulkner 	if (error == 0)
2801c7ddb37SRoger A. Faulkner 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
2811c7ddb37SRoger A. Faulkner 	if (error == 0 && pv[0] != 1)
2821c7ddb37SRoger A. Faulkner 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
2831c7ddb37SRoger A. Faulkner 	if (error == 0 && pv[1] != 1)
2841c7ddb37SRoger A. Faulkner 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
2851c7ddb37SRoger A. Faulkner 	if (error == 0 && !(flags & WRDE_SHOWERR))
2861c7ddb37SRoger A. Faulkner 		error = posix_spawn_file_actions_addopen(&fact, 2,
2871c7ddb37SRoger A. Faulkner 		    "/dev/null", O_WRONLY, 0);
2881c7ddb37SRoger A. Faulkner 
2891c7ddb37SRoger A. Faulkner 	if (error == 0)
2901c7ddb37SRoger A. Faulkner 		error = posix_spawn(&pid, path, &fact, &attr,
2911c7ddb37SRoger A. Faulkner 		    (char *const *)args, (char *const *)envp);
2921c7ddb37SRoger A. Faulkner 	(void) posix_spawnattr_destroy(&attr);
2931c7ddb37SRoger A. Faulkner 	(void) posix_spawn_file_actions_destroy(&fact);
294da2e3ebdSchin 	(void) close(pv[1]);
2951c7ddb37SRoger A. Faulkner 	if (error) {
2961c7ddb37SRoger A. Faulkner 		(void) close(pv[0]);
2971c7ddb37SRoger A. Faulkner 		errno = error;
2981c7ddb37SRoger A. Faulkner 		goto cleanup;
2991c7ddb37SRoger A. Faulkner 	}
300da2e3ebdSchin 
301da2e3ebdSchin 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
3021c7ddb37SRoger A. Faulkner 		error = errno;
303da2e3ebdSchin 		(void) close(pv[0]);
3041c7ddb37SRoger A. Faulkner 		errno = error;
305da2e3ebdSchin 		goto wait_cleanup;
306da2e3ebdSchin 	}
307da2e3ebdSchin 
308da2e3ebdSchin 	/*
309da2e3ebdSchin 	 * Read words from shell, separated with '\0'.
310da2e3ebdSchin 	 * Since there is no way to disable IFS splitting,
311da2e3ebdSchin 	 * it would be possible to separate the output with '\n'.
312da2e3ebdSchin 	 */
313da2e3ebdSchin 	cp = line = malloc(BUFSZ);
314da2e3ebdSchin 	if (line == NULL) {
3151c7ddb37SRoger A. Faulkner 		error = errno;
316da2e3ebdSchin 		(void) fclose(fp);
3171c7ddb37SRoger A. Faulkner 		errno = error;
318da2e3ebdSchin 		goto wait_cleanup;
319da2e3ebdSchin 	}
320da2e3ebdSchin 	eob = line + BUFSZ;
321da2e3ebdSchin 
322da2e3ebdSchin 	rv = 0;
3231c7ddb37SRoger A. Faulkner 	flockfile(fp);
3241c7ddb37SRoger A. Faulkner 	while ((i = getc_unlocked(fp)) != EOF) {
325da2e3ebdSchin 		*cp++ = (char)i;
326da2e3ebdSchin 		if (i == '\0') {
327da2e3ebdSchin 			cp = line;
328da2e3ebdSchin 			if ((rv = append(&wptmp, cp)) != 0) {
329da2e3ebdSchin 				break;
330da2e3ebdSchin 			}
331da2e3ebdSchin 		}
332da2e3ebdSchin 		if (cp == eob) {
333da2e3ebdSchin 			size_t bs = (eob - line);
334da2e3ebdSchin 			char *nl;
335da2e3ebdSchin 
336da2e3ebdSchin 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
337da2e3ebdSchin 				rv = WRDE_NOSPACE;
338da2e3ebdSchin 				break;
339da2e3ebdSchin 			}
340da2e3ebdSchin 			line = nl;
341da2e3ebdSchin 			cp = line + bs;
342da2e3ebdSchin 			eob = cp + BUFSZ;
343da2e3ebdSchin 		}
344da2e3ebdSchin 	}
3451c7ddb37SRoger A. Faulkner 	funlockfile(fp);
346da2e3ebdSchin 
347da2e3ebdSchin 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
348da2e3ebdSchin 
349da2e3ebdSchin 	free(line);
350da2e3ebdSchin 	(void) fclose(fp);	/* kill shell if still writing */
351da2e3ebdSchin 
352da2e3ebdSchin wait_cleanup:
3531c7ddb37SRoger A. Faulkner 	while (waitpid(pid, &status, 0) == -1) {
3541c7ddb37SRoger A. Faulkner 		if (errno != EINTR) {
3551c7ddb37SRoger A. Faulkner 			if (rv == 0)
3561c7ddb37SRoger A. Faulkner 				rv = WRDE_ERRNO;
3571c7ddb37SRoger A. Faulkner 			break;
3581c7ddb37SRoger A. Faulkner 		}
3591c7ddb37SRoger A. Faulkner 	}
3601c7ddb37SRoger A. Faulkner 	if (rv == 0)
361da2e3ebdSchin 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
362da2e3ebdSchin 
363da2e3ebdSchin cleanup:
364da2e3ebdSchin 	if (rv == 0)
365da2e3ebdSchin 		*wp = wptmp;
3661c7ddb37SRoger A. Faulkner 	else if (tmpalloc)
3671c7ddb37SRoger A. Faulkner 		wordfree(&wptmp);
368da2e3ebdSchin 
3691c7ddb37SRoger A. Faulkner 	if (env)
3701c7ddb37SRoger A. Faulkner 		free(env);
3711c7ddb37SRoger A. Faulkner 	if (wd)
3721c7ddb37SRoger A. Faulkner 		free(wd);
373da2e3ebdSchin 	/*
3741c7ddb37SRoger A. Faulkner 	 * Map ksh93 errors to |wordexp()| errors
375da2e3ebdSchin 	 */
3761c7ddb37SRoger A. Faulkner 	switch (rv) {
3771c7ddb37SRoger A. Faulkner 		case 1:
3781c7ddb37SRoger A. Faulkner 			rv = WRDE_BADVAL;
3791c7ddb37SRoger A. Faulkner 			break;
3801c7ddb37SRoger A. Faulkner 		case 127:
3811c7ddb37SRoger A. Faulkner 			rv = WRDE_BADCHAR;
3821c7ddb37SRoger A. Faulkner 			break;
3831c7ddb37SRoger A. Faulkner 	}
384ec6bbcf8Sraf 
385ec6bbcf8Sraf 	(void) pthread_setcancelstate(cancel_state, NULL);
386da2e3ebdSchin 	return (rv);
387da2e3ebdSchin }
388da2e3ebdSchin 
3897c478bd9Sstevel@tonic-gate /*
390da2e3ebdSchin  * Append a word to the wordexp_t structure, growing it as necessary.
3917c478bd9Sstevel@tonic-gate  */
3927c478bd9Sstevel@tonic-gate static int
append(wordexp_t * wp,char * str)3937c478bd9Sstevel@tonic-gate append(wordexp_t *wp, char *str)
3947c478bd9Sstevel@tonic-gate {
3957c478bd9Sstevel@tonic-gate 	char *cp;
3967c478bd9Sstevel@tonic-gate 	char **nwp;
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	/*
3997c478bd9Sstevel@tonic-gate 	 * We will be adding one entry and later adding
4007c478bd9Sstevel@tonic-gate 	 * one more NULL. So we need 2 more free slots.
4017c478bd9Sstevel@tonic-gate 	 */
4027c478bd9Sstevel@tonic-gate 	if ((wp->we_wordp + wp->we_wordc) ==
403da2e3ebdSchin 	    (wp->we_wordv + wp->we_wordn - 1)) {
4047c478bd9Sstevel@tonic-gate 		nwp = realloc(wp->we_wordv,
405da2e3ebdSchin 		    (wp->we_wordn + INITIAL) * sizeof (char *));
4067c478bd9Sstevel@tonic-gate 		if (nwp == NULL)
4077c478bd9Sstevel@tonic-gate 			return (WRDE_NOSPACE);
4087c478bd9Sstevel@tonic-gate 		wp->we_wordn += INITIAL;
4097c478bd9Sstevel@tonic-gate 		wp->we_wordv = nwp;
4107c478bd9Sstevel@tonic-gate 		wp->we_wordp = wp->we_wordv + wp->we_offs;
4117c478bd9Sstevel@tonic-gate 	}
4127c478bd9Sstevel@tonic-gate 	if ((cp = strdup(str)) == NULL)
4137c478bd9Sstevel@tonic-gate 		return (WRDE_NOSPACE);
4147c478bd9Sstevel@tonic-gate 	wp->we_wordp[wp->we_wordc++] = cp;
4157c478bd9Sstevel@tonic-gate 	return (0);
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate /*
4197c478bd9Sstevel@tonic-gate  * Free all space owned by wordexp_t.
4207c478bd9Sstevel@tonic-gate  */
4217c478bd9Sstevel@tonic-gate void
wordfree(wordexp_t * wp)4227c478bd9Sstevel@tonic-gate wordfree(wordexp_t *wp)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	size_t i;
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	if (wp->we_wordv == NULL)
4277c478bd9Sstevel@tonic-gate 		return;
4287c478bd9Sstevel@tonic-gate 	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
4297c478bd9Sstevel@tonic-gate 		free(wp->we_wordv[i]);
4307c478bd9Sstevel@tonic-gate 	free((void *)wp->we_wordv);
4317c478bd9Sstevel@tonic-gate 	wp->we_wordc = 0;
4327c478bd9Sstevel@tonic-gate 	wp->we_wordv = NULL;
4337c478bd9Sstevel@tonic-gate }
434