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