/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * This code is MKS code ported to Solaris originally with minimum * modifications so that upgrades from MKS would readily integrate. * The MKS basis for this modification was: * * $Id: wordexp.c 1.22 1994/11/21 18:24:50 miked * * Additional modifications have been made to this code to make it * 64-bit clean. */ /* * wordexp, wordfree -- POSIX.2 D11.2 word expansion routines. * * Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved. * */ #pragma weak wordexp = _wordexp #pragma weak wordfree = _wordfree #include "synonyms.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define INITIAL 8 /* initial pathv allocation */ #define BUFSZ 256 /* allocation unit of the line buffer */ static int append(wordexp_t *, char *); extern int __xpg4; /* defined in _xpg4.c; 0 if not xpg4-compiled program */ /* * Needs no locking if fetched only once. * See getenv()/putenv()/setenv(). */ extern const char **environ; /* * Do word expansion. * We just pass our arguments to shell with -E option. Note that the * underlying shell must recognize the -E option, and do the right thing * with it. */ int wordexp(const char *word, wordexp_t *wp, int flags) { char options[9]; char *optendp = options; char *argv[4]; const char *path; wordexp_t wptmp; size_t si; int i; pid_t pid; char *line, *eob, *cp; /* word from shell */ int rv = WRDE_ERRNO; int status; int pv[2]; /* pipe from shell stdout */ FILE *fp; /* pipe read stream */ int tmpalloc; char *wd = NULL; const char **env = NULL; const char **envp; const char *ev; int n; posix_spawnattr_t attr; posix_spawn_file_actions_t fact; int error; static const char *sun_path = "/bin/ksh"; static const char *xpg4_path = "/usr/xpg4/bin/sh"; /* * Do absolute minimum neccessary for the REUSE flag. Eventually * want to be able to actually avoid excessive malloc calls. */ if (flags & WRDE_REUSE) wordfree(wp); /* * Initialize wordexp_t * * XPG requires that the struct pointed to by wp not be modified * unless wordexp() either succeeds, or fails on WRDE_NOSPACE. * So we work with wptmp, and only copy wptmp to wp if one of the * previously mentioned conditions is satisfied. */ wptmp = *wp; /* * Man page says: * 2. All of the calls must set WRDE_DOOFFS, or all must not * set it. * Therefore, if it's not set, we_offs will always be reset. */ if ((flags & WRDE_DOOFFS) == 0) wptmp.we_offs = 0; /* * If we get APPEND|REUSE, how should we do? * allocating buffer anyway to avoid segfault. */ tmpalloc = 0; if ((flags & WRDE_APPEND) == 0 || (flags & WRDE_REUSE)) { wptmp.we_wordc = 0; wptmp.we_wordn = wptmp.we_offs + INITIAL; wptmp.we_wordv = malloc(sizeof (char *) * wptmp.we_wordn); if (wptmp.we_wordv == NULL) return (WRDE_NOSPACE); wptmp.we_wordp = wptmp.we_wordv + wptmp.we_offs; for (si = 0; si < wptmp.we_offs; si++) wptmp.we_wordv[si] = NULL; tmpalloc = 1; } /* * Turn flags into shell options */ *optendp++ = '-'; *optendp++ = (char)0x05; /* ksh -^E */ if (flags & WRDE_UNDEF) *optendp++ = 'u'; if (flags & WRDE_NOCMD) *optendp++ = 'N'; *optendp = '\0'; /* * Make sure PWD is in the environment. */ if ((envp = environ) == NULL) { /* can't happen? */ ev = NULL; n = 0; } else { for (n = 0; (ev = envp[n]) != NULL; n++) { if (*ev == 'P' && strncmp(ev, "PWD=", 4) == 0) break; } } if (ev == NULL) { /* PWD missing from the environment */ /* allocate a new environment */ if ((env = malloc((n + 2) * sizeof (char *))) == NULL || (wd = malloc(PATH_MAX + 4)) == NULL) goto cleanup; for (i = 0; i < n; i++) env[i] = envp[i]; (void) strcpy(wd, "PWD="); if (getcwd(&wd[4], PATH_MAX) == NULL) (void) strcpy(&wd[4], "/"); env[i] = wd; env[i + 1] = NULL; envp = env; } if ((error = posix_spawnattr_init(&attr)) != 0) { errno = error; goto cleanup; } if ((error = posix_spawn_file_actions_init(&fact)) != 0) { (void) posix_spawnattr_destroy(&attr); errno = error; goto cleanup; } /* * Set up pipe from shell stdout to "fp" for us */ if (pipe(pv) < 0) { error = errno; (void) posix_spawnattr_destroy(&attr); (void) posix_spawn_file_actions_destroy(&fact); errno = error; goto cleanup; } /* * Spawn shell with -E word */ error = posix_spawnattr_setflags(&attr, POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP); if (error == 0) error = posix_spawn_file_actions_adddup2(&fact, pv[1], 1); if (error == 0 && pv[0] != 1) error = posix_spawn_file_actions_addclose(&fact, pv[0]); if (error == 0 && pv[1] != 1) error = posix_spawn_file_actions_addclose(&fact, pv[1]); if (error == 0 && !(flags & WRDE_SHOWERR)) error = posix_spawn_file_actions_addopen(&fact, 2, "/dev/null", O_WRONLY, 0); path = __xpg4 ? xpg4_path : sun_path; argv[0] = strrchr(path, '/') + 1; argv[1] = options; argv[2] = (char *)word; argv[3] = NULL; if (error == 0) error = posix_spawn(&pid, path, &fact, &attr, (char *const *)argv, (char *const *)envp); (void) posix_spawnattr_destroy(&attr); (void) posix_spawn_file_actions_destroy(&fact); (void) close(pv[1]); if (error) { (void) close(pv[0]); errno = error; goto cleanup; } if ((fp = fdopen(pv[0], "rF")) == NULL) { error = errno; (void) close(pv[0]); errno = error; goto wait_cleanup; } /* * Read words from shell, separated with '\0'. * Since there is no way to disable IFS splitting, * it would be possible to separate the output with '\n'. */ cp = line = malloc(BUFSZ); if (line == NULL) { error = errno; (void) fclose(fp); errno = error; goto wait_cleanup; } eob = line + BUFSZ; rv = 0; while ((i = getc(fp)) != EOF) { *cp++ = (char)i; if (i == '\0') { cp = line; if ((rv = append(&wptmp, cp)) != 0) { break; } } if (cp == eob) { size_t bs = (eob - line); char *nl; if ((nl = realloc(line, bs + BUFSZ)) == NULL) { rv = WRDE_NOSPACE; break; } line = nl; cp = line + bs; eob = cp + BUFSZ; } } wptmp.we_wordp[wptmp.we_wordc] = NULL; free(line); (void) fclose(fp); /* kill shell if still writing */ wait_cleanup: while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { if (rv == 0) rv = WRDE_ERRNO; break; } } if (rv == 0) rv = WEXITSTATUS(status); /* shell WRDE_* status */ cleanup: if (rv == 0) *wp = wptmp; else if (tmpalloc) wordfree(&wptmp); if (env) free(env); if (wd) free(wd); /* * Map ksh errors to wordexp() errors */ if (rv == 4) rv = WRDE_CMDSUB; else if (rv == 5) rv = WRDE_BADVAL; else if (rv == 6) rv = WRDE_SYNTAX; return (rv); } /* * Append a word to the wordexp_t structure, growing it as neccessary. */ static int append(wordexp_t *wp, char *str) { char *cp; char **nwp; /* * We will be adding one entry and later adding * one more NULL. So we need 2 more free slots. */ if ((wp->we_wordp + wp->we_wordc) == (wp->we_wordv + wp->we_wordn - 1)) { nwp = realloc(wp->we_wordv, (wp->we_wordn + INITIAL) * sizeof (char *)); if (nwp == NULL) return (WRDE_NOSPACE); wp->we_wordn += INITIAL; wp->we_wordv = nwp; wp->we_wordp = wp->we_wordv + wp->we_offs; } if ((cp = strdup(str)) == NULL) return (WRDE_NOSPACE); wp->we_wordp[wp->we_wordc++] = cp; return (0); } /* * Free all space owned by wordexp_t. */ void wordfree(wordexp_t *wp) { size_t i; if (wp->we_wordv == NULL) return; for (i = wp->we_offs; i < wp->we_offs + wp->we_wordc; i++) free(wp->we_wordv[i]); free((void *)wp->we_wordv); wp->we_wordc = 0; wp->we_wordv = NULL; }