remove LFS64 symbol aliases; replace with dynamic linker remapping
[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 void reap(pid_t pid)
15 {
16         int status;
17         while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
18 }
19
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
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
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
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 }