#pragma weak _wordexp = wordexp
#pragma weak _wordfree = wordfree
#include "lint.h"
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>
#include <unistd.h>
#include <wordexp.h>
#include <stdio.h>
#include <spawn.h>
#include <errno.h>
#define INITIAL 8
#define BUFSZ 256
extern const char **_environ;
static int append(wordexp_t *, char *);
static char *
mystpcpy(char *s1, const char *s2)
{
while ((*s1++ = *s2++) != '\0')
;
return (s1-1);
}
int
wordexp(const char *word, wordexp_t *wp, int flags)
{
const char *path = "/usr/bin/ksh93";
wordexp_t wptmp;
size_t si;
pid_t pid;
char *line, *eob, *cp;
int rv = WRDE_ERRNO;
int status;
int pv[2];
FILE *fp;
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;
int cancel_state;
size_t bufflen;
char *buff;
char *currbuffp;
char *args[10];
int i;
if (flags & WRDE_REUSE)
wordfree(wp);
wptmp = *wp;
if ((flags & WRDE_DOOFFS) == 0)
wptmp.we_offs = 0;
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;
}
(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
if ((envp = _environ) == NULL) {
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) {
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;
}
bufflen = 165 + strlen(word);
buff = alloca(bufflen);
i = 0;
buff[0] = '\0';
currbuffp = buff;
if (flags & WRDE_UNDEF)
currbuffp = mystpcpy(currbuffp, "set -o nounset\n");
if ((flags & WRDE_SHOWERR) == 0) {
currbuffp = mystpcpy(currbuffp, "exec 2>/dev/null\n");
}
currbuffp = mystpcpy(currbuffp, "exec 0</dev/null\n");
if (flags & WRDE_NOCMD) {
currbuffp = mystpcpy(currbuffp,
"export PATH=/usr/no/such/path/element/ ; "
"set -o restricted\n");
}
(void) snprintf(currbuffp, bufflen,
"print -f '%%s\\000' -- %s", word);
args[i++] = strrchr(path, '/') + 1;
args[i++] = "-c";
args[i++] = buff;
args[i++] = NULL;
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;
}
if (pipe(pv) < 0) {
error = errno;
(void) posix_spawnattr_destroy(&attr);
(void) posix_spawn_file_actions_destroy(&fact);
errno = error;
goto cleanup;
}
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);
if (error == 0)
error = posix_spawn(&pid, path, &fact, &attr,
(char *const *)args, (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;
}
cp = line = malloc(BUFSZ);
if (line == NULL) {
error = errno;
(void) fclose(fp);
errno = error;
goto wait_cleanup;
}
eob = line + BUFSZ;
rv = 0;
flockfile(fp);
while ((i = getc_unlocked(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;
}
}
funlockfile(fp);
wptmp.we_wordp[wptmp.we_wordc] = NULL;
free(line);
(void) fclose(fp);
wait_cleanup:
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
if (rv == 0)
rv = WRDE_ERRNO;
break;
}
}
if (rv == 0)
rv = WEXITSTATUS(status);
cleanup:
if (rv == 0)
*wp = wptmp;
else if (tmpalloc)
wordfree(&wptmp);
if (env)
free(env);
if (wd)
free(wd);
switch (rv) {
case 1:
rv = WRDE_BADVAL;
break;
case 127:
rv = WRDE_BADCHAR;
break;
}
(void) pthread_setcancelstate(cancel_state, NULL);
return (rv);
}
static int
append(wordexp_t *wp, char *str)
{
char *cp;
char **nwp;
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);
}
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;
}