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