Home | History | Annotate | Download | only in regex
      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 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * This code is MKS code ported to Solaris originally with minimum
     29  * modifications so that upgrades from MKS would readily integrate.
     30  * The MKS basis for this modification was:
     31  *
     32  *	$Id: wordexp.c 1.22 1994/11/21 18:24:50 miked
     33  *
     34  * Additional modifications have been made to this code to make it
     35  * 64-bit clean.
     36  */
     37 
     38 /*
     39  * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines.
     40  *
     41  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
     42  * Modified by Roland Mainz <roland.mainz (at) nrubsig.org> to support ksh93.
     43  */
     44 
     45 #pragma	weak _wordexp = wordexp
     46 #pragma	weak _wordfree = wordfree
     47 
     48 /* Safeguard against mistakes in the Makefiles */
     49 #ifndef WORDEXP_KSH93
     50 #error "WORDEXP_KSH93 not set. Please check the Makefile flags."
     51 #endif
     52 
     53 #include "lint.h"
     54 #include <stdio.h>
     55 #include <unistd.h>
     56 #include <limits.h>
     57 #include <fcntl.h>
     58 #include <limits.h>
     59 #include <stdlib.h>
     60 #if WORDEXP_KSH93
     61 #include <alloca.h>
     62 #endif /* WORDEXP_KSH93 */
     63 #include <string.h>
     64 #include <sys/wait.h>
     65 #include <pthread.h>
     66 #include <unistd.h>
     67 #include <wordexp.h>
     68 #include <stdio.h>
     69 #include <spawn.h>
     70 #include <errno.h>
     71 
     72 #define	INITIAL	8		/* initial pathv allocation */
     73 #define	BUFSZ	256		/* allocation unit of the line buffer */
     74 
     75 /*
     76  * Needs no locking if fetched only once.
     77  * See getenv()/putenv()/setenv().
     78  */
     79 extern	const char **_environ;
     80 
     81 /* Local prototypes */
     82 static int append(wordexp_t *, char *);
     83 
     84 #if WORDEXP_KSH93
     85 /*
     86  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
     87  * We'll add this later (and a matching multibyte/widechar version)
     88  * as normal libc function.
     89  *
     90  * Copy string s2 to s1.  s1 must be large enough.
     91  * return s1-1 (position of string terminator ('\0') in destination buffer).
     92  */
     93 static char *
     94 mystpcpy(char *s1, const char *s2)
     95 {
     96 	while (*s1++ = *s2++)
     97 		;
     98 	return (s1-1);
     99 }
    100 
    101 /*
    102  * Do word expansion.
    103  * We build a mini-script in |buff| which takes care of all details,
    104  * including stdin/stdout/stderr redirection, WRDE_NOCMD mode and
    105  * the word expansion itself.
    106  */
    107 int
    108 wordexp(const char *word, wordexp_t *wp, int flags)
    109 {
    110 	const char *path = "/usr/bin/ksh93";
    111 	wordexp_t wptmp;
    112 	size_t si;
    113 	pid_t pid;
    114 	char *line, *eob, *cp;		/* word from shell */
    115 	int rv = WRDE_ERRNO;
    116 	int status;
    117 	int pv[2];			/* pipe from shell stdout */
    118 	FILE *fp;			/* pipe read stream */
    119 	int tmpalloc;
    120 	char *wd = NULL;
    121 	const char **env = NULL;
    122 	const char **envp;
    123 	const char *ev;
    124 	int n;
    125 	posix_spawnattr_t attr;
    126 	posix_spawn_file_actions_t fact;
    127 	int error;
    128 	int cancel_state;
    129 	size_t bufflen; /* Length of |buff| */
    130 	char *buff;
    131 	char *currbuffp; /* Current position of '\0' in |buff| */
    132 	char *args[10];
    133 	int i;
    134 
    135 	/*
    136 	 * Do absolute minimum necessary for the REUSE flag. Eventually
    137 	 * want to be able to actually avoid excessive malloc calls.
    138 	 */
    139 	if (flags & WRDE_REUSE)
    140 		wordfree(wp);
    141 
    142 	/*
    143 	 * Initialize wordexp_t
    144 	 *
    145 	 * XPG requires that the struct pointed to by wp not be modified
    146 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
    147 	 * So we work with wptmp, and only copy wptmp to wp if one of the
    148 	 * previously mentioned conditions is satisfied.
    149 	 */
    150 	wptmp = *wp;
    151 
    152 	/*
    153 	 * Man page says:
    154 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
    155 	 *    set it.
    156 	 * Therefore, if it's not set, we_offs will always be reset.
    157 	 */
    158 	if ((flags & WRDE_DOOFFS) == 0)
    159 		wptmp.we_offs = 0;
    160 
    161 	/*
    162 	 * If we get APPEND|REUSE, how should we do?
    163 	 * allocating buffer anyway to avoid segfault.
    164 	 */
    165 	tmpalloc = 0;
    166 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
    167 		wptmp.we_wordc = 0;
    168 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
    169 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
    170 		if (wptmp.we_wordv == NULL)
    171 			return (WRDE_NOSPACE);
    172 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
    173 		for (si = 0; si < wptmp.we_offs; si++)
    174 			wptmp.we_wordv[si] = NULL;
    175 		tmpalloc = 1;
    176 	}
    177 
    178 	/*
    179 	 * The UNIX98 Posix conformance test suite requires
    180 	 * |wordexp()| to not be a cancellation point.
    181 	 */
    182 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
    183 
    184 	/*
    185 	 * Make sure PWD is in the environment.
    186 	 */
    187 	if ((envp = _environ) == NULL) {
    188 		/* can happen when processing a SunOS 4.x AOUT file */
    189 		ev = NULL;
    190 		n = 0;
    191 	} else {
    192 		for (n = 0; (ev = envp[n]) != NULL; n++) {
    193 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
    194 				break;
    195 		}
    196 	}
    197 	if (ev == NULL) {	/* PWD missing from the environment */
    198 		/* allocate a new environment */
    199 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
    200 		    (wd = malloc(PATH_MAX + 4)) == NULL)
    201 			goto cleanup;
    202 		for (i = 0; i < n; i++)
    203 			env[i] = envp[i];
    204 		(void) strcpy(wd, "PWD=");
    205 		if (getcwd(&wd[4], PATH_MAX) == NULL)
    206 			(void) strcpy(&wd[4], "/");
    207 		env[i] = wd;
    208 		env[i + 1] = NULL;
    209 		envp = env;
    210 	}
    211 
    212 	/*
    213 	 * Calculate size of required buffer (which is size of the
    214 	 * input string (|word|) plus all string literals below;
    215 	 * this value MUST be adjusted each time the literals are
    216 	 * changed!!).
    217 	 */
    218 	bufflen = 165 + strlen(word);
    219 	buff = alloca(bufflen);
    220 	i = 0;
    221 
    222 	/* Start filling the buffer */
    223 	buff[0] = '\0';
    224 	currbuffp = buff;
    225 
    226 	if (flags & WRDE_UNDEF)
    227 		currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
    228 	if ((flags & WRDE_SHOWERR) == 0) {
    229 		/*
    230 		 * The newline ('\n') is neccesary to make sure that
    231 		 * the redirection to /dev/null is already active in
    232 		 * the case the printf below contains a syntax
    233 		 * error...
    234 		 */
    235 		currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
    236 	}
    237 	/* Squish stdin */
    238 	currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
    239 
    240 	if (flags & WRDE_NOCMD) {
    241 		/*
    242 		 * Switch to restricted shell (rksh) mode here to
    243 		 * put the word expansion into a "cage" which
    244 		 * prevents users from executing external commands
    245 		 * (outside those listed by ${PATH} (which we set
    246 		 * explicitly to /usr/no/such/path/element/)).
    247 		 */
    248 		currbuffp = mystpcpy(currbuffp,
    249 		    "export PATH=/usr/no/such/path/element/ ; "
    250 		    "set -o restricted\n");
    251 	}
    252 
    253 	(void) snprintf(currbuffp, bufflen,
    254 	    "print -f '%%s\\000' -- %s", word);
    255 
    256 	args[i++] = strrchr(path, '/') + 1;
    257 	args[i++] = "-c";
    258 	args[i++] = buff;
    259 	args[i++] = NULL;
    260 
    261 	if ((error = posix_spawnattr_init(&attr)) != 0) {
    262 		errno = error;
    263 		goto cleanup;
    264 	}
    265 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
    266 		(void) posix_spawnattr_destroy(&attr);
    267 		errno = error;
    268 		goto cleanup;
    269 	}
    270 
    271 	/*
    272 	 * Set up pipe from shell stdout to "fp" for us
    273 	 */
    274 	if (pipe(pv) < 0) {
    275 		error = errno;
    276 		(void) posix_spawnattr_destroy(&attr);
    277 		(void) posix_spawn_file_actions_destroy(&fact);
    278 		errno = error;
    279 		goto cleanup;
    280 	}
    281 
    282 	/*
    283 	 * Spawn shell
    284 	 */
    285 	error = posix_spawnattr_setflags(&attr,
    286 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
    287 	if (error == 0)
    288 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
    289 	if (error == 0 && pv[0] != 1)
    290 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
    291 	if (error == 0 && pv[1] != 1)
    292 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
    293 	if (error == 0 && !(flags & WRDE_SHOWERR))
    294 		error = posix_spawn_file_actions_addopen(&fact, 2,
    295 		    "/dev/null", O_WRONLY, 0);
    296 
    297 	if (error == 0)
    298 		error = posix_spawn(&pid, path, &fact, &attr,
    299 		    (char *const *)args, (char *const *)envp);
    300 	(void) posix_spawnattr_destroy(&attr);
    301 	(void) posix_spawn_file_actions_destroy(&fact);
    302 	(void) close(pv[1]);
    303 	if (error) {
    304 		(void) close(pv[0]);
    305 		errno = error;
    306 		goto cleanup;
    307 	}
    308 
    309 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
    310 		error = errno;
    311 		(void) close(pv[0]);
    312 		errno = error;
    313 		goto wait_cleanup;
    314 	}
    315 
    316 	/*
    317 	 * Read words from shell, separated with '\0'.
    318 	 * Since there is no way to disable IFS splitting,
    319 	 * it would be possible to separate the output with '\n'.
    320 	 */
    321 	cp = line = malloc(BUFSZ);
    322 	if (line == NULL) {
    323 		error = errno;
    324 		(void) fclose(fp);
    325 		errno = error;
    326 		goto wait_cleanup;
    327 	}
    328 	eob = line + BUFSZ;
    329 
    330 	rv = 0;
    331 	flockfile(fp);
    332 	while ((i = getc_unlocked(fp)) != EOF) {
    333 		*cp++ = (char)i;
    334 		if (i == '\0') {
    335 			cp = line;
    336 			if ((rv = append(&wptmp, cp)) != 0) {
    337 				break;
    338 			}
    339 		}
    340 		if (cp == eob) {
    341 			size_t bs = (eob - line);
    342 			char *nl;
    343 
    344 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
    345 				rv = WRDE_NOSPACE;
    346 				break;
    347 			}
    348 			line = nl;
    349 			cp = line + bs;
    350 			eob = cp + BUFSZ;
    351 		}
    352 	}
    353 	funlockfile(fp);
    354 
    355 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
    356 
    357 	free(line);
    358 	(void) fclose(fp);	/* kill shell if still writing */
    359 
    360 wait_cleanup:
    361 	while (waitpid(pid, &status, 0) == -1) {
    362 		if (errno != EINTR) {
    363 			if (rv == 0)
    364 				rv = WRDE_ERRNO;
    365 			break;
    366 		}
    367 	}
    368 	if (rv == 0)
    369 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
    370 
    371 cleanup:
    372 	if (rv == 0)
    373 		*wp = wptmp;
    374 	else if (tmpalloc)
    375 		wordfree(&wptmp);
    376 
    377 	if (env)
    378 		free(env);
    379 	if (wd)
    380 		free(wd);
    381 	/*
    382 	 * Map ksh93 errors to |wordexp()| errors
    383 	 */
    384 	switch (rv) {
    385 		case 1:
    386 			rv = WRDE_BADVAL;
    387 			break;
    388 		case 127:
    389 			rv = WRDE_BADCHAR;
    390 			break;
    391 	}
    392 
    393 	(void) pthread_setcancelstate(cancel_state, NULL);
    394 	return (rv);
    395 }
    396 
    397 #else /* WORDEXP_KSH93 */
    398 
    399 extern	int __xpg4;	/* defined in _xpg4.c; 0 if not xpg4-compiled program */
    400 
    401 /*
    402  * Do word expansion.
    403  * We just pass our arguments to shell with -E option.  Note that the
    404  * underlying shell must recognize the -E option, and do the right thing
    405  * with it.
    406  */
    407 int
    408 wordexp(const char *word, wordexp_t *wp, int flags)
    409 {
    410 	char options[9];
    411 	char *optendp = options;
    412 	char *argv[4];
    413 	const char *path;
    414 	wordexp_t wptmp;
    415 	size_t si;
    416 	int i;
    417 	pid_t pid;
    418 	char *line, *eob, *cp;		/* word from shell */
    419 	int rv = WRDE_ERRNO;
    420 	int status;
    421 	int pv[2];			/* pipe from shell stdout */
    422 	FILE *fp;			/* pipe read stream */
    423 	int tmpalloc;
    424 	char *wd = NULL;
    425 	const char **env = NULL;
    426 	const char **envp;
    427 	const char *ev;
    428 	int n;
    429 	posix_spawnattr_t attr;
    430 	posix_spawn_file_actions_t fact;
    431 	int error;
    432 	int cancel_state;
    433 
    434 	static const char *sun_path = "/bin/ksh";
    435 	static const char *xpg4_path = "/usr/xpg4/bin/sh";
    436 
    437 	/*
    438 	 * Do absolute minimum necessary for the REUSE flag. Eventually
    439 	 * want to be able to actually avoid excessive malloc calls.
    440 	 */
    441 	if (flags & WRDE_REUSE)
    442 		wordfree(wp);
    443 
    444 	/*
    445 	 * Initialize wordexp_t
    446 	 *
    447 	 * XPG requires that the struct pointed to by wp not be modified
    448 	 * unless wordexp() either succeeds, or fails on WRDE_NOSPACE.
    449 	 * So we work with wptmp, and only copy wptmp to wp if one of the
    450 	 * previously mentioned conditions is satisfied.
    451 	 */
    452 	wptmp = *wp;
    453 
    454 	/*
    455 	 * Man page says:
    456 	 * 2. All of the calls must set WRDE_DOOFFS, or all must not
    457 	 *    set it.
    458 	 * Therefore, if it's not set, we_offs will always be reset.
    459 	 */
    460 	if ((flags & WRDE_DOOFFS) == 0)
    461 		wptmp.we_offs = 0;
    462 
    463 	/*
    464 	 * If we get APPEND|REUSE, how should we do?
    465 	 * allocating buffer anyway to avoid segfault.
    466 	 */
    467 	tmpalloc = 0;
    468 	if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) {
    469 		wptmp.we_wordc = 0;
    470 		wptmp.we_wordn = wptmp.we_offs + INITIAL;
    471 		wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn);
    472 		if (wptmp.we_wordv == NULL)
    473 			return (WRDE_NOSPACE);
    474 		wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs;
    475 		for (si = 0; si < wptmp.we_offs; si++)
    476 			wptmp.we_wordv[si] = NULL;
    477 		tmpalloc = 1;
    478 	}
    479 
    480 	/*
    481 	 * The UNIX98 Posix conformance test suite requires
    482 	 * wordexp() to not be a cancellation point.
    483 	 */
    484 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
    485 
    486 	/*
    487 	 * Turn flags into shell options
    488 	 */
    489 	*optendp++ = '-';
    490 	*optendp++ = (char)0x05;		/* ksh -^E */
    491 	if (flags & WRDE_UNDEF)
    492 		*optendp++ = 'u';
    493 	if (flags & WRDE_NOCMD)
    494 		*optendp++ = 'N';
    495 	*optendp = '\0';
    496 
    497 	/*
    498 	 * Make sure PWD is in the environment.
    499 	 */
    500 	if ((envp = _environ) == NULL) {
    501 		/* can happen when processing a SunOS 4.x AOUT file */
    502 		ev = NULL;
    503 		n = 0;
    504 	} else {
    505 		for (n = 0; (ev = envp[n]) != NULL; n++) {
    506 			if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0)
    507 				break;
    508 		}
    509 	}
    510 	if (ev == NULL) {	/* PWD missing from the environment */
    511 		/* allocate a new environment */
    512 		if ((env = malloc((n + 2) * sizeof (char *))) == NULL ||
    513 		    (wd = malloc(PATH_MAX + 4)) == NULL)
    514 			goto cleanup;
    515 		for (i = 0; i < n; i++)
    516 			env[i] = envp[i];
    517 		(void) strcpy(wd, "PWD=");
    518 		if (getcwd(&wd[4], PATH_MAX) == NULL)
    519 			(void) strcpy(&wd[4], "/");
    520 		env[i] = wd;
    521 		env[i + 1] = NULL;
    522 		envp = env;
    523 	}
    524 
    525 	if ((error = posix_spawnattr_init(&attr)) != 0) {
    526 		errno = error;
    527 		goto cleanup;
    528 	}
    529 	if ((error = posix_spawn_file_actions_init(&fact)) != 0) {
    530 		(void) posix_spawnattr_destroy(&attr);
    531 		errno = error;
    532 		goto cleanup;
    533 	}
    534 
    535 	/*
    536 	 * Set up pipe from shell stdout to "fp" for us
    537 	 */
    538 	if (pipe(pv) < 0) {
    539 		error = errno;
    540 		(void) posix_spawnattr_destroy(&attr);
    541 		(void) posix_spawn_file_actions_destroy(&fact);
    542 		errno = error;
    543 		goto cleanup;
    544 	}
    545 
    546 	/*
    547 	 * Spawn shell with -E word
    548 	 */
    549 	error = posix_spawnattr_setflags(&attr,
    550 	    POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP);
    551 	if (error == 0)
    552 		error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1);
    553 	if (error == 0 && pv[0] != 1)
    554 		error = posix_spawn_file_actions_addclose(&fact, pv[0]);
    555 	if (error == 0 && pv[1] != 1)
    556 		error = posix_spawn_file_actions_addclose(&fact, pv[1]);
    557 	if (error == 0 && !(flags & WRDE_SHOWERR))
    558 		error = posix_spawn_file_actions_addopen(&fact, 2,
    559 		    "/dev/null", O_WRONLY, 0);
    560 	path = __xpg4 ? xpg4_path : sun_path;
    561 	argv[0] = strrchr(path, '/') + 1;
    562 	argv[1] = options;
    563 	argv[2] = (char *)word;
    564 	argv[3] = NULL;
    565 	if (error == 0)
    566 		error = posix_spawn(&pid, path, &fact, &attr,
    567 		    (char *const *)argv, (char *const *)envp);
    568 	(void) posix_spawnattr_destroy(&attr);
    569 	(void) posix_spawn_file_actions_destroy(&fact);
    570 	(void) close(pv[1]);
    571 	if (error) {
    572 		(void) close(pv[0]);
    573 		errno = error;
    574 		goto cleanup;
    575 	}
    576 
    577 	if ((fp = fdopen(pv[0], "rF")) == NULL) {
    578 		error = errno;
    579 		(void) close(pv[0]);
    580 		errno = error;
    581 		goto wait_cleanup;
    582 	}
    583 
    584 	/*
    585 	 * Read words from shell, separated with '\0'.
    586 	 * Since there is no way to disable IFS splitting,
    587 	 * it would be possible to separate the output with '\n'.
    588 	 */
    589 	cp = line = malloc(BUFSZ);
    590 	if (line == NULL) {
    591 		error = errno;
    592 		(void) fclose(fp);
    593 		errno = error;
    594 		goto wait_cleanup;
    595 	}
    596 	eob = line + BUFSZ;
    597 
    598 	rv = 0;
    599 	flockfile(fp);
    600 	while ((i = getc_unlocked(fp)) != EOF) {
    601 		*cp++ = (char)i;
    602 		if (i == '\0') {
    603 			cp = line;
    604 			if ((rv = append(&wptmp, cp)) != 0) {
    605 				break;
    606 			}
    607 		}
    608 		if (cp == eob) {
    609 			size_t bs = (eob - line);
    610 			char *nl;
    611 
    612 			if ((nl = realloc(line, bs + BUFSZ)) == NULL) {
    613 				rv = WRDE_NOSPACE;
    614 				break;
    615 			}
    616 			line = nl;
    617 			cp = line + bs;
    618 			eob = cp + BUFSZ;
    619 		}
    620 	}
    621 	funlockfile(fp);
    622 
    623 	wptmp.we_wordp[wptmp.we_wordc] = NULL;
    624 
    625 	free(line);
    626 	(void) fclose(fp);	/* kill shell if still writing */
    627 
    628 wait_cleanup:
    629 	while (waitpid(pid, &status, 0) == -1) {
    630 		if (errno != EINTR) {
    631 			if (rv == 0)
    632 				rv = WRDE_ERRNO;
    633 			break;
    634 		}
    635 	}
    636 	if (rv == 0)
    637 		rv = WEXITSTATUS(status); /* shell WRDE_* status */
    638 
    639 cleanup:
    640 	if (rv == 0)
    641 		*wp = wptmp;
    642 	else if (tmpalloc)
    643 		wordfree(&wptmp);
    644 
    645 	if (env)
    646 		free(env);
    647 	if (wd)
    648 		free(wd);
    649 	/*
    650 	 * Map ksh errors to wordexp() errors
    651 	 */
    652 	if (rv == 4)
    653 		rv = WRDE_CMDSUB;
    654 	else if (rv == 5)
    655 		rv = WRDE_BADVAL;
    656 	else if (rv == 6)
    657 		rv = WRDE_SYNTAX;
    658 
    659 	(void) pthread_setcancelstate(cancel_state, NULL);
    660 	return (rv);
    661 }
    662 
    663 #endif /* WORDEXP_KSH93 */
    664 
    665 /*
    666  * Append a word to the wordexp_t structure, growing it as necessary.
    667  */
    668 static int
    669 append(wordexp_t *wp, char *str)
    670 {
    671 	char *cp;
    672 	char **nwp;
    673 
    674 	/*
    675 	 * We will be adding one entry and later adding
    676 	 * one more NULL. So we need 2 more free slots.
    677 	 */
    678 	if ((wp->we_wordp + wp->we_wordc) ==
    679 	    (wp->we_wordv + wp->we_wordn - 1)) {
    680 		nwp = realloc(wp->we_wordv,
    681 		    (wp->we_wordn + INITIAL) * sizeof (char *));
    682 		if (nwp == NULL)
    683 			return (WRDE_NOSPACE);
    684 		wp->we_wordn += INITIAL;
    685 		wp->we_wordv = nwp;
    686 		wp->we_wordp = wp->we_wordv + wp->we_offs;
    687 	}
    688 	if ((cp = strdup(str)) == NULL)
    689 		return (WRDE_NOSPACE);
    690 	wp->we_wordp[wp->we_wordc++] = cp;
    691 	return (0);
    692 }
    693 
    694 /*
    695  * Free all space owned by wordexp_t.
    696  */
    697 void
    698 wordfree(wordexp_t *wp)
    699 {
    700 	size_t i;
    701 
    702 	if (wp->we_wordv == NULL)
    703 		return;
    704 	for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++)
    705 		free(wp->we_wordv[i]);
    706 	free((void *)wp->we_wordv);
    707 	wp->we_wordc = 0;
    708 	wp->we_wordv = NULL;
    709 }
    710