• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
reap(pid_t pid)14 static void reap(pid_t pid)
15 {
16 	int status;
17 	while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
18 }
19 
getword(FILE * f)20 static char *getword(FILE *f)
21 {
22 	char *s = 0;
23 	return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
24 }
25 
do_wordexp(const char * s,wordexp_t * we,int flags)26 static 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 
162 nospace:
163 	if (!(flags & WRDE_APPEND)) {
164 		we->we_wordc = 0;
165 		we->we_wordv = 0;
166 	}
167 	return WRDE_NOSPACE;
168 }
169 
wordexp(const char * restrict s,wordexp_t * restrict we,int flags)170 int 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 
wordfree(wordexp_t * we)179 void 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 }
188