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