xref: /onnv-gate/usr/src/lib/libc/port/regex/wordexp.c (revision 11838:32bb5d254240)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51914Scasper  * Common Development and Distribution License (the "License").
61914Scasper  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
216737Sraf 
220Sstevel@tonic-gate /*
23*11838SLiane.Praza@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * This code is MKS code ported to Solaris originally with minimum
290Sstevel@tonic-gate  * modifications so that upgrades from MKS would readily integrate.
300Sstevel@tonic-gate  * The MKS basis for this modification was:
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * Additional modifications have been made to this code to make it
350Sstevel@tonic-gate  * 64-bit clean.
360Sstevel@tonic-gate  */
370Sstevel@tonic-gate 
380Sstevel@tonic-gate /*
390Sstevel@tonic-gate  * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
400Sstevel@tonic-gate  *
410Sstevel@tonic-gate  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
424887Schin  * Modified by Roland Mainz <roland.mainz@nrubsig.org> to support ksh93.
430Sstevel@tonic-gate  */
440Sstevel@tonic-gate 
456812Sraf #pragma	weak _wordexp = wordexp
466812Sraf #pragma	weak _wordfree = wordfree
472552Scraigm 
486812Sraf #include "lint.h"
490Sstevel@tonic-gate #include <stdio.h>
500Sstevel@tonic-gate #include <unistd.h>
510Sstevel@tonic-gate #include <limits.h>
520Sstevel@tonic-gate #include <fcntl.h>
530Sstevel@tonic-gate #include <limits.h>
540Sstevel@tonic-gate #include <stdlib.h>
554887Schin #include <alloca.h>
560Sstevel@tonic-gate #include <string.h>
570Sstevel@tonic-gate #include <sys/wait.h>
585891Sraf #include <pthread.h>
590Sstevel@tonic-gate #include <unistd.h>
600Sstevel@tonic-gate #include <wordexp.h>
610Sstevel@tonic-gate #include <stdio.h>
623235Sraf #include <spawn.h>
630Sstevel@tonic-gate #include <errno.h>
640Sstevel@tonic-gate 
650Sstevel@tonic-gate #define	INITIAL	8		/* initial pathv allocation */
660Sstevel@tonic-gate #define	BUFSZ	256		/* allocation unit of the line buffer */
670Sstevel@tonic-gate 
688070SRoger.Faulkner@Sun.COM /*
698070SRoger.Faulkner@Sun.COM  * Needs no locking if fetched only once.
708070SRoger.Faulkner@Sun.COM  * See getenv()/putenv()/setenv().
718070SRoger.Faulkner@Sun.COM  */
728070SRoger.Faulkner@Sun.COM extern	const char **_environ;
738070SRoger.Faulkner@Sun.COM 
744887Schin /* Local prototypes */
754887Schin static int append(wordexp_t *, char *);
764887Schin 
774887Schin /*
784887Schin  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
794887Schin  * We'll add this later (and a matching multibyte/widechar version)
804887Schin  * as normal libc function.
814887Schin  *
824887Schin  * Copy string s2 to s1.  s1 must be large enough.
834887Schin  * return s1-1 (position of string terminator ('\0') in destination buffer).
844887Schin  */
854887Schin static char *
mystpcpy(char * s1,const char * s2)864887Schin mystpcpy(char *s1, const char *s2)
874887Schin {
884887Schin 	while (*s1++ = *s2++)
894887Schin 		;
904887Schin 	return (s1-1);
914887Schin }
924887Schin 
934887Schin /*
944887Schin  * Do word expansion.
958070SRoger.Faulkner@Sun.COM  * We build a mini-script in |buff| which takes care of all details,
964887Schin  * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
974887Schin  * the word expansion itself.
984887Schin  */
994887Schin int
wordexp(const char * word,wordexp_t * wp,int flags)1004887Schin wordexp(const char *word, wordexp_t *wp, int flags)
1014887Schin {
1028070SRoger.Faulkner@Sun.COM 	const char *path = "/usr/bin/ksh93";
1034887Schin 	wordexp_t wptmp;
1044887Schin 	size_t si;
1054887Schin 	pid_t pid;
1068070SRoger.Faulkner@Sun.COM 	char *line, *eob, *cp;		/* word from shell */
1074887Schin 	int rv = WRDE_ERRNO;
1084887Schin 	int status;
1098070SRoger.Faulkner@Sun.COM 	int pv[2];			/* pipe from shell stdout */
1108070SRoger.Faulkner@Sun.COM 	FILE *fp;			/* pipe read stream */
1118070SRoger.Faulkner@Sun.COM 	int tmpalloc;
1128070SRoger.Faulkner@Sun.COM 	char *wd = NULL;
1138070SRoger.Faulkner@Sun.COM 	const char **env = NULL;
1148070SRoger.Faulkner@Sun.COM 	const char **envp;
1158070SRoger.Faulkner@Sun.COM 	const char *ev;
1168070SRoger.Faulkner@Sun.COM 	int n;
1178070SRoger.Faulkner@Sun.COM 	posix_spawnattr_t attr;
1188070SRoger.Faulkner@Sun.COM 	posix_spawn_file_actions_t fact;
1198070SRoger.Faulkner@Sun.COM 	int error;
1205891Sraf 	int cancel_state;
1218070SRoger.Faulkner@Sun.COM 	size_t bufflen; /* Length of |buff| */
1228070SRoger.Faulkner@Sun.COM 	char *buff;
1238070SRoger.Faulkner@Sun.COM 	char *currbuffp; /* Current position of '\0' in |buff| */
1248070SRoger.Faulkner@Sun.COM 	char *args[10];
1258070SRoger.Faulkner@Sun.COM 	int i;
1264887Schin 
1274887Schin 	/*
1284887Schin 	 * Do absolute minimum necessary for the REUSE flag. Eventually
1294887Schin 	 * want to be able to actually avoid excessive malloc calls.
1304887Schin 	 */
1314887Schin 	if (flags & WRDE_REUSE)
1324887Schin 		wordfree(wp);
1334887Schin 
1344887Schin 	/*
1354887Schin 	 * Initialize wordexp_t
1364887Schin 	 *
1374887Schin 	 * XPG requires that the struct pointed to by wp not be modified
1384887Schin 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
1394887Schin 	 * So we work with wptmp, and only copy wptmp to wp if one of the
1404887Schin 	 * previously mentioned conditions is satisfied.
1414887Schin 	 */
1424887Schin 	wptmp = *wp;
1434887Schin 
1444887Schin 	/*
1454887Schin 	 * Man page says:
1464887Schin 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
1478070SRoger.Faulkner@Sun.COM 	 *    set it.
1484887Schin 	 * Therefore, if it's not set, we_offs will always be reset.
1494887Schin 	 */
1504887Schin 	if ((flags & WRDE_DOOFFS) == 0)
1514887Schin 		wptmp.we_offs = 0;
1524887Schin 
1534887Schin 	/*
1544887Schin 	 * If we get APPEND|REUSE, how should we do?
1558070SRoger.Faulkner@Sun.COM 	 * allocating buffer anyway to avoid segfault.
1564887Schin 	 */
1574887Schin 	tmpalloc = 0;
1584887Schin 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
1594887Schin 		wptmp.we_wordc = 0;
1604887Schin 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
1618070SRoger.Faulkner@Sun.COM 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
1624887Schin 		if (wptmp.we_wordv == NULL)
1634887Schin 			return (WRDE_NOSPACE);
1644887Schin 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
1654887Schin 		for (si = 0; si < wptmp.we_offs; si++)
1664887Schin 			wptmp.we_wordv[si] = NULL;
1674887Schin 		tmpalloc = 1;
1684887Schin 	}
1694887Schin 
1704887Schin 	/*
1716737Sraf 	 * The UNIX98 Posix conformance test suite requires
1728070SRoger.Faulkner@Sun.COM 	 * |wordexp()| to not be a cancellation point.
1736737Sraf 	 */
1746737Sraf 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
1756737Sraf 
1766737Sraf 	/*
1778070SRoger.Faulkner@Sun.COM 	 * Make sure PWD is in the environment.
1788070SRoger.Faulkner@Sun.COM 	 */
1798070SRoger.Faulkner@Sun.COM 	if ((envp = _environ) == NULL) {
1808070SRoger.Faulkner@Sun.COM 		/* can happen when processing a SunOS 4.x AOUT file */
1818070SRoger.Faulkner@Sun.COM 		ev = NULL;
1828070SRoger.Faulkner@Sun.COM 		n = 0;
1838070SRoger.Faulkner@Sun.COM 	} else {
1848070SRoger.Faulkner@Sun.COM 		for (n = 0; (ev = envp[n]) != NULL; n++) {
1858070SRoger.Faulkner@Sun.COM 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
1868070SRoger.Faulkner@Sun.COM 				break;
1878070SRoger.Faulkner@Sun.COM 		}
1888070SRoger.Faulkner@Sun.COM 	}
1898070SRoger.Faulkner@Sun.COM 	if (ev == NULL) {	/* PWD missing from the environment */
1908070SRoger.Faulkner@Sun.COM 		/* allocate a new environment */
1918070SRoger.Faulkner@Sun.COM 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
1928070SRoger.Faulkner@Sun.COM 		    (wd = malloc(PATH_MAX + 4)) == NULL)
1938070SRoger.Faulkner@Sun.COM 			goto cleanup;
1948070SRoger.Faulkner@Sun.COM 		for (i = 0; i < n; i++)
1958070SRoger.Faulkner@Sun.COM 			env[i] = envp[i];
1968070SRoger.Faulkner@Sun.COM 		(void) strcpy(wd, "PWD=");
1978070SRoger.Faulkner@Sun.COM 		if (getcwd(&wd[4], PATH_MAX) == NULL)
1988070SRoger.Faulkner@Sun.COM 			(void) strcpy(&wd[4], "/");
1998070SRoger.Faulkner@Sun.COM 		env[i] = wd;
2008070SRoger.Faulkner@Sun.COM 		env[i + 1] = NULL;
2018070SRoger.Faulkner@Sun.COM 		envp = env;
2028070SRoger.Faulkner@Sun.COM 	}
2038070SRoger.Faulkner@Sun.COM 
2048070SRoger.Faulkner@Sun.COM 	/*
2058070SRoger.Faulkner@Sun.COM 	 * Calculate size of required buffer (which is size of the
2068070SRoger.Faulkner@Sun.COM 	 * input string (|word|) plus all string literals below;
2078070SRoger.Faulkner@Sun.COM 	 * this value MUST be adjusted each time the literals are
2088070SRoger.Faulkner@Sun.COM 	 * changed!!).
2098070SRoger.Faulkner@Sun.COM 	 */
2108070SRoger.Faulkner@Sun.COM 	bufflen = 165 + strlen(word);
2118070SRoger.Faulkner@Sun.COM 	buff = alloca(bufflen);
2128070SRoger.Faulkner@Sun.COM 	i = 0;
2138070SRoger.Faulkner@Sun.COM 
2148070SRoger.Faulkner@Sun.COM 	/* Start filling the buffer */
2158070SRoger.Faulkner@Sun.COM 	buff[0] = '\0';
2168070SRoger.Faulkner@Sun.COM 	currbuffp = buff;
2178070SRoger.Faulkner@Sun.COM 
2188070SRoger.Faulkner@Sun.COM 	if (flags & WRDE_UNDEF)
2198070SRoger.Faulkner@Sun.COM 		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
2208070SRoger.Faulkner@Sun.COM 	if ((flags & WRDE_SHOWERR) == 0) {
2218070SRoger.Faulkner@Sun.COM 		/*
2228070SRoger.Faulkner@Sun.COM 		 * The newline ('\n') is neccesary to make sure that
2238070SRoger.Faulkner@Sun.COM 		 * the redirection to /dev/null is already active in
2248070SRoger.Faulkner@Sun.COM 		 * the case the printf below contains a syntax
2258070SRoger.Faulkner@Sun.COM 		 * error...
2268070SRoger.Faulkner@Sun.COM 		 */
2278070SRoger.Faulkner@Sun.COM 		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
2288070SRoger.Faulkner@Sun.COM 	}
2298070SRoger.Faulkner@Sun.COM 	/* Squish stdin */
2308070SRoger.Faulkner@Sun.COM 	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
2318070SRoger.Faulkner@Sun.COM 
2328070SRoger.Faulkner@Sun.COM 	if (flags & WRDE_NOCMD) {
2338070SRoger.Faulkner@Sun.COM 		/*
2348070SRoger.Faulkner@Sun.COM 		 * Switch to restricted shell (rksh) mode here to
2358070SRoger.Faulkner@Sun.COM 		 * put the word expansion into a "cage" which
2368070SRoger.Faulkner@Sun.COM 		 * prevents users from executing external commands
2378070SRoger.Faulkner@Sun.COM 		 * (outside those listed by ${PATH} (which we set
2388070SRoger.Faulkner@Sun.COM 		 * explicitly to /usr/no/such/path/element/)).
2398070SRoger.Faulkner@Sun.COM 		 */
2408070SRoger.Faulkner@Sun.COM 		currbuffp = mystpcpy(currbuffp,
2418070SRoger.Faulkner@Sun.COM 		    "export PATH=/usr/no/such/path/element/ ; "
2428070SRoger.Faulkner@Sun.COM 		    "set -o restricted\n");
2438070SRoger.Faulkner@Sun.COM 	}
2448070SRoger.Faulkner@Sun.COM 
2458070SRoger.Faulkner@Sun.COM 	(void) snprintf(currbuffp, bufflen,
2468070SRoger.Faulkner@Sun.COM 	    "print -f '%%s\\000' -- %s", word);
2478070SRoger.Faulkner@Sun.COM 
2488070SRoger.Faulkner@Sun.COM 	args[i++] = strrchr(path, '/') + 1;
2498070SRoger.Faulkner@Sun.COM 	args[i++] = "-c";
2508070SRoger.Faulkner@Sun.COM 	args[i++] = buff;
2518070SRoger.Faulkner@Sun.COM 	args[i++] = NULL;
2528070SRoger.Faulkner@Sun.COM 
2538070SRoger.Faulkner@Sun.COM 	if ((error = posix_spawnattr_init(&attr)) != 0) {
2548070SRoger.Faulkner@Sun.COM 		errno = error;
2558070SRoger.Faulkner@Sun.COM 		goto cleanup;
2568070SRoger.Faulkner@Sun.COM 	}
2578070SRoger.Faulkner@Sun.COM 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
2588070SRoger.Faulkner@Sun.COM 		(void) posix_spawnattr_destroy(&attr);
2598070SRoger.Faulkner@Sun.COM 		errno = error;
2608070SRoger.Faulkner@Sun.COM 		goto cleanup;
2618070SRoger.Faulkner@Sun.COM 	}
2628070SRoger.Faulkner@Sun.COM 
2638070SRoger.Faulkner@Sun.COM 	/*
2644887Schin 	 * Set up pipe from shell stdout to "fp" for us
2654887Schin 	 */
2668070SRoger.Faulkner@Sun.COM 	if (pipe(pv) < 0) {
2678070SRoger.Faulkner@Sun.COM 		error = errno;
2688070SRoger.Faulkner@Sun.COM 		(void) posix_spawnattr_destroy(&attr);
2698070SRoger.Faulkner@Sun.COM 		(void) posix_spawn_file_actions_destroy(&fact);
2708070SRoger.Faulkner@Sun.COM 		errno = error;
2714887Schin 		goto cleanup;
2724887Schin 	}
2734887Schin 
2748070SRoger.Faulkner@Sun.COM 	/*
2758070SRoger.Faulkner@Sun.COM 	 * Spawn shell
2768070SRoger.Faulkner@Sun.COM 	 */
2778070SRoger.Faulkner@Sun.COM 	error = posix_spawnattr_setflags(&attr,
2788070SRoger.Faulkner@Sun.COM 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
2798070SRoger.Faulkner@Sun.COM 	if (error == 0)
2808070SRoger.Faulkner@Sun.COM 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
2818070SRoger.Faulkner@Sun.COM 	if (error == 0 && pv[0] != 1)
2828070SRoger.Faulkner@Sun.COM 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
2838070SRoger.Faulkner@Sun.COM 	if (error == 0 && pv[1] != 1)
2848070SRoger.Faulkner@Sun.COM 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
2858070SRoger.Faulkner@Sun.COM 	if (error == 0 && !(flags & WRDE_SHOWERR))
2868070SRoger.Faulkner@Sun.COM 		error = posix_spawn_file_actions_addopen(&fact, 2,
2878070SRoger.Faulkner@Sun.COM 		    "/dev/null", O_WRONLY, 0);
2884887Schin 
2898070SRoger.Faulkner@Sun.COM 	if (error == 0)
2908070SRoger.Faulkner@Sun.COM 		error = posix_spawn(&pid, path, &fact, &attr,
2918070SRoger.Faulkner@Sun.COM 		    (char *const *)args, (char *const *)envp);
2928070SRoger.Faulkner@Sun.COM 	(void) posix_spawnattr_destroy(&attr);
2938070SRoger.Faulkner@Sun.COM 	(void) posix_spawn_file_actions_destroy(&fact);
2948070SRoger.Faulkner@Sun.COM 	(void) close(pv[1]);
2958070SRoger.Faulkner@Sun.COM 	if (error) {
2968070SRoger.Faulkner@Sun.COM 		(void) close(pv[0]);
2978070SRoger.Faulkner@Sun.COM 		errno = error;
2988070SRoger.Faulkner@Sun.COM 		goto cleanup;
2994887Schin 	}
3004887Schin 
3014887Schin 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
3028070SRoger.Faulkner@Sun.COM 		error = errno;
3034887Schin 		(void) close(pv[0]);
3048070SRoger.Faulkner@Sun.COM 		errno = error;
3054887Schin 		goto wait_cleanup;
3064887Schin 	}
3074887Schin 
3084887Schin 	/*
3094887Schin 	 * Read words from shell, separated with '\0'.
3104887Schin 	 * Since there is no way to disable IFS splitting,
3114887Schin 	 * it would be possible to separate the output with '\n'.
3124887Schin 	 */
3134887Schin 	cp = line = malloc(BUFSZ);
3144887Schin 	if (line == NULL) {
3158070SRoger.Faulkner@Sun.COM 		error = errno;
3164887Schin 		(void) fclose(fp);
3178070SRoger.Faulkner@Sun.COM 		errno = error;
3184887Schin 		goto wait_cleanup;
3194887Schin 	}
3204887Schin 	eob = line + BUFSZ;
3214887Schin 
3224887Schin 	rv = 0;
3238070SRoger.Faulkner@Sun.COM 	flockfile(fp);
3248070SRoger.Faulkner@Sun.COM 	while ((i = getc_unlocked(fp)) != EOF) {
3254887Schin 		*cp++ = (char)i;
3264887Schin 		if (i == '\0') {
3274887Schin 			cp = line;
3284887Schin 			if ((rv = append(&wptmp, cp)) != 0) {
3294887Schin 				break;
3304887Schin 			}
3314887Schin 		}
3324887Schin 		if (cp == eob) {
3334887Schin 			size_t bs = (eob - line);
3344887Schin 			char *nl;
3354887Schin 
3364887Schin 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
3374887Schin 				rv = WRDE_NOSPACE;
3384887Schin 				break;
3394887Schin 			}
3404887Schin 			line = nl;
3414887Schin 			cp = line + bs;
3424887Schin 			eob = cp + BUFSZ;
3434887Schin 		}
3444887Schin 	}
3458070SRoger.Faulkner@Sun.COM 	funlockfile(fp);
3464887Schin 
3474887Schin 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
3484887Schin 
3494887Schin 	free(line);
3504887Schin 	(void) fclose(fp);	/* kill shell if still writing */
3514887Schin 
3524887Schin wait_cleanup:
3538070SRoger.Faulkner@Sun.COM 	while (waitpid(pid, &status, 0) == -1) {
3548070SRoger.Faulkner@Sun.COM 		if (errno != EINTR) {
3558070SRoger.Faulkner@Sun.COM 			if (rv == 0)
3568070SRoger.Faulkner@Sun.COM 				rv = WRDE_ERRNO;
3578070SRoger.Faulkner@Sun.COM 			break;
3588070SRoger.Faulkner@Sun.COM 		}
3598070SRoger.Faulkner@Sun.COM 	}
3608070SRoger.Faulkner@Sun.COM 	if (rv == 0)
3614887Schin 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
3624887Schin 
3634887Schin cleanup:
3644887Schin 	if (rv == 0)
3654887Schin 		*wp = wptmp;
3668070SRoger.Faulkner@Sun.COM 	else if (tmpalloc)
3678070SRoger.Faulkner@Sun.COM 		wordfree(&wptmp);
3684887Schin 
3698070SRoger.Faulkner@Sun.COM 	if (env)
3708070SRoger.Faulkner@Sun.COM 		free(env);
3718070SRoger.Faulkner@Sun.COM 	if (wd)
3728070SRoger.Faulkner@Sun.COM 		free(wd);
3734887Schin 	/*
3748070SRoger.Faulkner@Sun.COM 	 * Map ksh93 errors to |wordexp()| errors
3754887Schin 	 */
3768070SRoger.Faulkner@Sun.COM 	switch (rv) {
3778070SRoger.Faulkner@Sun.COM 		case 1:
3788070SRoger.Faulkner@Sun.COM 			rv = WRDE_BADVAL;
3798070SRoger.Faulkner@Sun.COM 			break;
3808070SRoger.Faulkner@Sun.COM 		case 127:
3818070SRoger.Faulkner@Sun.COM 			rv = WRDE_BADCHAR;
3828070SRoger.Faulkner@Sun.COM 			break;
3838070SRoger.Faulkner@Sun.COM 	}
3846737Sraf 
3856737Sraf 	(void) pthread_setcancelstate(cancel_state, NULL);
3864887Schin 	return (rv);
3874887Schin }
3884887Schin 
3890Sstevel@tonic-gate /*
3904887Schin  * Append a word to the wordexp_t structure, growing it as necessary.
3910Sstevel@tonic-gate  */
3920Sstevel@tonic-gate static int
append(wordexp_t * wp,char * str)3930Sstevel@tonic-gate append(wordexp_t *wp, char *str)
3940Sstevel@tonic-gate {
3950Sstevel@tonic-gate 	char *cp;
3960Sstevel@tonic-gate 	char **nwp;
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	/*
3990Sstevel@tonic-gate 	 * We will be adding one entry and later adding
4000Sstevel@tonic-gate 	 * one more NULL. So we need 2 more free slots.
4010Sstevel@tonic-gate 	 */
4020Sstevel@tonic-gate 	if ((wp->we_wordp + wp->we_wordc) ==
4034887Schin 	    (wp->we_wordv + wp->we_wordn - 1)) {
4040Sstevel@tonic-gate 		nwp = realloc(wp->we_wordv,
4054887Schin 		    (wp->we_wordn + INITIAL) * sizeof (char *));
4060Sstevel@tonic-gate 		if (nwp == NULL)
4070Sstevel@tonic-gate 			return (WRDE_NOSPACE);
4080Sstevel@tonic-gate 		wp->we_wordn += INITIAL;
4090Sstevel@tonic-gate 		wp->we_wordv = nwp;
4100Sstevel@tonic-gate 		wp->we_wordp = wp->we_wordv + wp->we_offs;
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 	if ((cp = strdup(str)) == NULL)
4130Sstevel@tonic-gate 		return (WRDE_NOSPACE);
4140Sstevel@tonic-gate 	wp->we_wordp[wp->we_wordc++] = cp;
4150Sstevel@tonic-gate 	return (0);
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate /*
4190Sstevel@tonic-gate  * Free all space owned by wordexp_t.
4200Sstevel@tonic-gate  */
4210Sstevel@tonic-gate void
wordfree(wordexp_t * wp)4220Sstevel@tonic-gate wordfree(wordexp_t *wp)
4230Sstevel@tonic-gate {
4240Sstevel@tonic-gate 	size_t i;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	if (wp->we_wordv == NULL)
4270Sstevel@tonic-gate 		return;
4280Sstevel@tonic-gate 	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
4290Sstevel@tonic-gate 		free(wp->we_wordv[i]);
4300Sstevel@tonic-gate 	free((void *)wp->we_wordv);
4310Sstevel@tonic-gate 	wp->we_wordc = 0;
4320Sstevel@tonic-gate 	wp->we_wordv = NULL;
4330Sstevel@tonic-gate }
434