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