1#include <wordexp.h>
2#include <unistd.h>
3#include <stdio.h>
4#include <string.h>
5#include <limits.h>
6#include <stdint.h>
7#include <stdlib.h>
8#include <sys/wait.h>
9#include <signal.h>
10#include <errno.h>
11#include <fcntl.h>
12#include "pthread_impl.h"
13
14static void reap(pid_t pid)
15{
16	int status;
17	for (;;) {
18		if (waitpid(pid, &status, 0) < 0) {
19			if (errno != EINTR) return;
20		} else {
21			if (WIFEXITED(status)) return;
22		}
23	}
24}
25
26static char *getword(FILE *f)
27{
28	char *s = 0;
29	return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
30}
31
32static int do_wordexp(const char *s, wordexp_t *we, int flags)
33{
34	size_t i, l;
35	int sq=0, dq=0;
36	size_t np=0;
37	char *w, **tmp;
38	char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
39	int err = 0;
40	FILE *f;
41	size_t wc = 0;
42	char **wv = 0;
43	int p[2];
44	pid_t pid;
45	sigset_t set;
46
47	if (flags & WRDE_REUSE) wordfree(we);
48
49	if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) {
50	case '\\':
51		if (!sq) i++;
52		break;
53	case '\'':
54		if (!dq) sq^=1;
55		break;
56	case '"':
57		if (!sq) dq^=1;
58		break;
59	case '(':
60		if (np) {
61			np++;
62			break;
63		}
64	case ')':
65		if (np) {
66			np--;
67			break;
68		}
69	case '\n':
70	case '|':
71	case '&':
72	case ';':
73	case '<':
74	case '>':
75	case '{':
76	case '}':
77		if (!(sq|dq|np)) return WRDE_BADCHAR;
78		break;
79	case '$':
80		if (sq) break;
81		if (s[i+1]=='(' && s[i+2]=='(') {
82			i += 2;
83			np += 2;
84			break;
85		} else if (s[i+1] != '(') break;
86	case '`':
87		if (sq) break;
88		return WRDE_CMDSUB;
89	}
90
91	if (flags & WRDE_APPEND) {
92		wc = we->we_wordc;
93		wv = we->we_wordv;
94	}
95
96	i = wc;
97	if (flags & WRDE_DOOFFS) {
98		if (we->we_offs > SIZE_MAX/sizeof(void *)/4)
99			goto nospace;
100		i += we->we_offs;
101	} else {
102		we->we_offs = 0;
103	}
104
105	if (pipe2(p, O_CLOEXEC) < 0) goto nospace;
106	__block_all_sigs(&set);
107	pid = fork();
108	__restore_sigs(&set);
109	if (pid < 0) {
110		close(p[0]);
111		close(p[1]);
112		goto nospace;
113	}
114	if (!pid) {
115		if (p[1] == 1) fcntl(1, F_SETFD, 0);
116		else dup2(p[1], 1);
117		execl("/bin/sh", "sh", "-c",
118			"eval \"printf %s\\\\\\\\0 x $1 $2\"",
119			"sh", s, redir, (char *)0);
120		_exit(1);
121	}
122	close(p[1]);
123
124	f = fdopen(p[0], "r");
125	if (!f) {
126		close(p[0]);
127		kill(pid, SIGKILL);
128		reap(pid);
129		goto nospace;
130	}
131
132	l = wv ? i+1 : 0;
133
134	free(getword(f));
135	if (feof(f)) {
136		fclose(f);
137		reap(pid);
138		return WRDE_SYNTAX;
139	}
140
141	while ((w = getword(f))) {
142		if (i+1 >= l) {
143			l += l/2+10;
144			tmp = realloc(wv, l*sizeof(char *));
145			if (!tmp) break;
146			wv = tmp;
147		}
148		wv[i++] = w;
149		wv[i] = 0;
150	}
151	if (!feof(f)) err = WRDE_NOSPACE;
152
153	fclose(f);
154	reap(pid);
155
156	if (!wv) wv = calloc(i+1, sizeof *wv);
157
158	we->we_wordv = wv;
159	we->we_wordc = i;
160
161	if (flags & WRDE_DOOFFS) {
162		if (wv) for (i=we->we_offs; i; i--)
163			we->we_wordv[i-1] = 0;
164		we->we_wordc -= we->we_offs;
165	}
166	return err;
167
168nospace:
169	if (!(flags & WRDE_APPEND)) {
170		we->we_wordc = 0;
171		we->we_wordv = 0;
172	}
173	return WRDE_NOSPACE;
174}
175
176int wordexp(const char *restrict s, wordexp_t *restrict we, int flags)
177{
178	int r, cs;
179	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
180	r = do_wordexp(s, we, flags);
181	pthread_setcancelstate(cs, 0);
182	return r;
183}
184
185void wordfree(wordexp_t *we)
186{
187	size_t i;
188	if (!we->we_wordv) return;
189	for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]);
190	free(we->we_wordv);
191	we->we_wordv = 0;
192	we->we_wordc = 0;
193}
194