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