#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#include <wordexp.h>
int wordexp(const char *restrict words __unused, wordexp_t *restrict pwordexp __unused, int flags __unused) {
return WRDE_NOSPACE;
}
void wordfree(wordexp_t *pwordexp __unused) {
}
#else
#include "namespace.h"
#include <sys/cdefs.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wordexp.h>
#include "un-namespace.h"
#ifdef __APPLE__
#include <crt_externs.h>
#include <spawn.h>
#endif
__FBSDID("$FreeBSD$");
#ifdef __APPLE__
typedef struct {
char *wi_strings;
size_t wi_nbytes;
char *wi_wordv[0];
} we_int_t;
#define WE_INT_T(x) ((we_int_t *)((char *)(x) - sizeof(we_int_t)))
#define WE_STRINGS(we) (WE_INT_T((we)->we_wordv)->wi_strings)
#define WE_WORDV(x) ((x)->wi_wordv)
static const char command_substitution_str[] = "command substitution";
static const char syntax_error_str[] = "syntax error";
static const char unbound_variable_str[] = " unbound variable";
#endif
static int we_askshell(const char *, wordexp_t *, int);
static int we_check(const char *, int);
int
#ifdef __APPLE__
wordexp(const char * __restrict words, wordexp_t * __restrict we0, int flags)
#else
wordexp(const char * __restrict words, wordexp_t * __restrict we, int flags)
#endif
{
int error;
#ifdef __APPLE__
wordexp_t temp;
wordexp_t *we = &temp;
if ((error = we_check(words, flags)) != 0) return (error);
we->we_offs = we0->we_offs;
if (flags & WRDE_APPEND) {
size_t i;
size_t vofs = we0->we_wordc + (flags & WRDE_DOOFFS ? we0->we_offs : 0);
we_int_t *wi0 = WE_INT_T(we0->we_wordv);
we_int_t *wi = malloc((vofs + 1) * sizeof(char *) + sizeof(we_int_t));
if (!wi) return (WRDE_NOSPACE);
memcpy(wi, wi0, (vofs + 1) * sizeof(char *) + sizeof(we_int_t));
wi->wi_strings = malloc(wi->wi_nbytes);
if (!wi->wi_strings) {
free(wi);
return (WRDE_NOSPACE);
}
memcpy(wi->wi_strings, wi0->wi_strings, wi->wi_nbytes);
for (i = 0; i < vofs; i++)
if (wi->wi_wordv[i] != NULL)
wi->wi_wordv[i] += wi->wi_strings - wi0->wi_strings;
we->we_wordc = we0->we_wordc;
we->we_wordv = WE_WORDV(wi);
} else {
we->we_wordc = 0;
we->we_wordv = NULL;
}
#else
if (flags & WRDE_REUSE)
wordfree(we);
if ((flags & WRDE_APPEND) == 0) {
we->we_wordc = 0;
we->we_wordv = NULL;
we->we_strings = NULL;
we->we_nbytes = 0;
}
if ((error = we_check(words, flags)) != 0) {
wordfree(we);
return (error);
}
#endif
if ((error = we_askshell(words, we, flags)) != 0) {
#ifdef __APPLE__
if (error == WRDE_NOSPACE) {
if (flags & WRDE_REUSE)
wordfree(we0);
*we0 = *we;
} else {
wordfree(we);
}
#else
wordfree(we);
#endif
return (error);
}
#ifdef __APPLE__
if (flags & WRDE_REUSE)
wordfree(we0);
*we0 = *we;
#endif
return (0);
}
static size_t
we_read_fully(int fd, char *buffer, size_t len)
{
size_t done;
ssize_t nread;
done = 0;
do {
nread = _read(fd, buffer + done, len - done);
if (nread == -1 && errno == EINTR)
continue;
if (nread <= 0)
break;
done += nread;
} while (done != len);
return done;
}
static int
we_askshell(const char *words, wordexp_t *we, int flags)
{
int pdes[2];
char bbuf[9];
char wbuf[9];
long nwords, nbytes;
long i;
size_t sofs;
size_t vofs;
pid_t pid;
pid_t wpid;
int status;
int error;
int serrno;
char *np, *p;
char *nstrings;
#ifdef __APPLE__
int perr[2];
we_int_t *nwv;
int spawnerr = 0;
posix_spawn_file_actions_t file_actions;
posix_spawnattr_t attr;
#else
char **nwv;
#endif
sigset_t newsigblock, oldsigblock;
serrno = errno;
#ifdef __APPLE__
if (pipe(pdes) < 0)
#else
if (pipe2(pdes, O_CLOEXEC) < 0)
#endif
return (WRDE_NOSPACE);
#ifdef __APPLE__
if (pipe(perr) < 0) {
close(pdes[0]);
close(pdes[1]);
return (WRDE_NOSPACE);
}
#endif
(void)sigemptyset(&newsigblock);
(void)sigaddset(&newsigblock, SIGCHLD);
(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
#ifdef __APPLE__
if ((spawnerr = posix_spawnattr_init(&attr)) != 0) goto spawnerrexit;
do {
sigset_t spawnsig;
if ((spawnerr = posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT)) != 0) break;
(void)sigfillset(&spawnsig);
if ((spawnerr = posix_spawnattr_setsigdefault(&attr, &spawnsig)) != 0) break;
(void)sigemptyset(&spawnsig);
if ((spawnerr = posix_spawnattr_setsigmask(&attr, &spawnsig)) != 0) break;
if ((spawnerr = posix_spawn_file_actions_init(&file_actions)) != 0) break;
do {
char *argv[7] = {"sh"};
const char cmd[] = "[ $# -gt 0 ] && export IFS=\"$1\";/usr/lib/system/wordexp-helper ";
int a = 1;
char *buf;
int buflen;
char *IFS;
if (pdes[1] == STDOUT_FILENO) {
if ((spawnerr = posix_spawn_file_actions_addinherit_np(&file_actions, STDOUT_FILENO)) != 0) break;
} else {
if ((spawnerr = posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDOUT_FILENO)) != 0) break;
}
if (perr[1] == STDERR_FILENO) {
if ((spawnerr = posix_spawn_file_actions_addinherit_np(&file_actions, STDERR_FILENO)) != 0) break;
} else {
if ((spawnerr = posix_spawn_file_actions_adddup2(&file_actions, perr[1], STDERR_FILENO)) != 0) break;
}
if (flags & WRDE_UNDEF) argv[a++] = "-u";
argv[a++] = "-c";
buflen = (sizeof(cmd) - 1) + strlen(words) + 1;
if ((buf = malloc(buflen)) == NULL) {
spawnerr = errno;
break;
}
strcpy(buf, cmd);
strcat(buf, words);
argv[a++] = buf;
if ((IFS = getenv("IFS")) != NULL) {
argv[a++] = "--";
argv[a++] = IFS;
}
argv[a] = NULL;
spawnerr = posix_spawn(&pid, _PATH_BSHELL, &file_actions, &attr, argv, *_NSGetEnviron());
free(buf);
} while(0);
posix_spawn_file_actions_destroy(&file_actions);
} while(0);
posix_spawnattr_destroy(&attr);
if (spawnerr) {
spawnerrexit:
close(pdes[0]);
close(pdes[1]);
close(perr[0]);
close(perr[1]);
errno = spawnerr;
return (WRDE_NOSPACE);
}
#else
if ((pid = fork()) < 0) {
serrno = errno;
_close(pdes[0]);
_close(pdes[1]);
(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
errno = serrno;
return (WRDE_NOSPACE);
}
else if (pid == 0) {
(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
if ((pdes[1] != STDOUT_FILENO ?
_dup2(pdes[1], STDOUT_FILENO) :
_fcntl(pdes[1], F_SETFD, 0)) < 0)
_exit(1);
execl(_PATH_BSHELL, "sh", flags & WRDE_UNDEF ? "-u" : "+u",
"-c", "eval \"$1\";eval \"wordexp $2\"", "",
flags & WRDE_SHOWERR ? "" : "exec 2>/dev/null", words,
(char *)NULL);
_exit(1);
}
#endif
_close(pdes[1]);
#ifdef __APPLE__
close(perr[1]);
#endif
if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
we_read_fully(pdes[0], bbuf, 8) != 8) {
error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
serrno = errno;
goto cleanup;
}
wbuf[8] = bbuf[8] = '\0';
nwords = strtol(wbuf, NULL, 16);
nbytes = strtol(bbuf, NULL, 16) + nwords;
#ifndef __APPLE__
sofs = we->we_nbytes;
#endif
vofs = we->we_wordc;
if ((flags & (WRDE_DOOFFS|WRDE_APPEND)) == (WRDE_DOOFFS|WRDE_APPEND))
vofs += we->we_offs;
we->we_wordc += nwords;
#ifndef __APPLE__
we->we_nbytes += nbytes;
#endif
#ifdef __APPLE__
if ((nwv = realloc(we->we_wordv ? WE_INT_T(we->we_wordv) : NULL, (we->we_wordc + 1 +
(flags & WRDE_DOOFFS ? we->we_offs : 0)) *
sizeof(char *) + sizeof(we_int_t))) == NULL)
#else
if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
(flags & WRDE_DOOFFS ? we->we_offs : 0)) *
sizeof(char *))) == NULL)
#endif
{
error = WRDE_NOSPACE;
goto cleanup;
}
#ifdef __APPLE__
if (!we->we_wordv) {
nwv->wi_strings = NULL;
nwv->wi_nbytes = 0;
}
sofs = nwv->wi_nbytes;
nwv->wi_nbytes += nbytes;
we->we_wordv = WE_WORDV(nwv);
#else
we->we_wordv = nwv;
#endif
#ifdef __APPLE__
if ((nstrings = realloc(nwv->wi_strings, nwv->wi_nbytes)) == NULL)
#else
if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL)
#endif
{
error = WRDE_NOSPACE;
goto cleanup;
}
for (i = 0; i < vofs; i++)
if (we->we_wordv[i] != NULL)
#ifdef __APPLE__
we->we_wordv[i] += nstrings - nwv->wi_strings;
#else
we->we_wordv[i] += nstrings - we->we_strings;
#endif
#ifdef __APPLE__
nwv->wi_strings = nstrings;
#else
we->we_strings = nstrings;
#endif
#ifdef __APPLE__
if (we_read_fully(pdes[0], nwv->wi_strings + sofs, nbytes) != nbytes)
#else
if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes)
#endif
{
error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
serrno = errno;
goto cleanup;
}
#ifdef __APPLE__
char err_buf[1024];
ssize_t err_sz = read(perr[0], err_buf, sizeof(err_buf) - 1);
if (err_sz > 0) {
err_buf[err_sz] = '\0';
if (flags & WRDE_SHOWERR) {
fputs(err_buf, stderr);
}
} else if (err_sz < 0) {
serrno = errno;
error = WRDE_NOSPACE;
goto cleanup;
}
#endif
error = 0;
cleanup:
_close(pdes[0]);
#ifdef __APPLE__
close(perr[0]);
#endif
do
wpid = _waitpid(pid, &status, 0);
while (wpid < 0 && errno == EINTR);
(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
if (error != 0) {
errno = serrno;
return (error);
}
if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
#ifdef __APPLE__
const char *cs = strstr(err_buf, command_substitution_str);
if (cs && strstr(cs + (sizeof(command_substitution_str) - 1), syntax_error_str)) {
return (strstr(err_buf, unbound_variable_str) ? WRDE_BADVAL : WRDE_SYNTAX);
}
#endif
if (vofs == 0 && flags & WRDE_DOOFFS)
while (vofs < we->we_offs)
we->we_wordv[vofs++] = NULL;
#ifdef __APPLE__
p = nwv->wi_strings + sofs;
#else
p = we->we_strings + sofs;
#endif
while (nwords-- != 0) {
we->we_wordv[vofs++] = p;
if ((np = memchr(p, '\0', nbytes)) == NULL)
return (WRDE_NOSPACE);
nbytes -= np - p + 1;
p = np + 1;
}
we->we_wordv[vofs] = NULL;
return (0);
}
static int
we_check(const char *words, int flags)
{
char c;
int dquote, level, quote, squote;
quote = squote = dquote = 0;
while ((c = *words++) != '\0') {
switch (c) {
case '\\':
if (squote == 0)
quote ^= 1;
continue;
case '\'':
if (quote + dquote == 0)
squote ^= 1;
break;
case '"':
if (quote + squote == 0)
dquote ^= 1;
break;
case '`':
if (quote + squote == 0 && flags & WRDE_NOCMD)
return (WRDE_CMDSUB);
while ((c = *words++) != '\0' && c != '`')
if (c == '\\' && (c = *words++) == '\0')
break;
if (c == '\0')
return (WRDE_SYNTAX);
break;
case '|': case '&': case ';': case '<': case '>':
case '{': case '}': case '(': case ')': case '\n':
if (quote + squote + dquote == 0)
return (WRDE_BADCHAR);
break;
case '$':
if ((c = *words++) == '\0')
break;
else if (quote + squote == 0 && c == '(') {
if (flags & WRDE_NOCMD && *words != '(')
return (WRDE_CMDSUB);
level = 1;
while ((c = *words++) != '\0') {
if (c == '\\') {
if ((c = *words++) == '\0')
break;
} else if (c == '(')
level++;
else if (c == ')' && --level == 0)
break;
}
if (c == '\0' || level != 0)
return (WRDE_SYNTAX);
} else if (quote + squote == 0 && c == '{') {
level = 1;
while ((c = *words++) != '\0') {
if (c == '\\') {
if ((c = *words++) == '\0')
break;
} else if (c == '{')
level++;
else if (c == '}' && --level == 0)
break;
}
if (c == '\0' || level != 0)
return (WRDE_SYNTAX);
} else
--words;
break;
default:
break;
}
quote = 0;
}
if (quote + squote + dquote != 0)
return (WRDE_SYNTAX);
return (0);
}
void
wordfree(wordexp_t *we)
{
if (we == NULL)
return;
#ifdef __APPLE__
if (we->we_wordv) {
free(WE_STRINGS(we));
free(WE_INT_T(we->we_wordv));
}
#else
free(we->we_wordv);
#endif
#ifndef __APPLE__
free(we->we_strings);
#endif
we->we_wordv = NULL;
#ifndef __APPLE__
we->we_strings = NULL;
we->we_nbytes = 0;
#endif
we->we_wordc = 0;
}
#endif