171f27027f6670bd735559948e1c1aef098cb167
[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
11 static char *getword(FILE *f)
12 {
13         char *s = 0;
14         return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
15 }
16
17 int wordexp(const char *s, wordexp_t *we, int flags)
18 {
19         size_t i, l;
20         int sq=0, dq=0;
21         size_t np=0;
22         char *w, **tmp;
23         char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
24         int err = 0, status;
25         FILE *f;
26         size_t wc = 0;
27         char **wv = 0;
28         int p[2];
29         pid_t pid;
30
31         if (flags & WRDE_REUSE) wordfree(we);
32
33         if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) {
34         case '\\':
35                 if (!sq) i++;
36                 break;
37         case '\'':
38                 if (!dq) sq^=1;
39                 break;
40         case '"':
41                 if (!sq) dq^=1;
42                 break;
43         case '(':
44                 if (np) {
45                         np++;
46                         break;
47                 }
48         case ')':
49                 if (np) {
50                         np--;
51                         break;
52                 }
53         case '\n':
54         case '|':
55         case '&':
56         case ';':
57         case '<':
58         case '>':
59         case '{':
60         case '}':
61                 if (!(sq|dq|np)) return WRDE_BADCHAR;
62                 break;
63         case '$':
64                 if (s[i+1]=='(' && s[i+2]=='(') {
65                         i += 2;
66                         np += 2;
67                         break;
68                 } else if (s[i+1] != '(') break;
69         case '`':
70                 if (sq) break;
71                 return WRDE_CMDSUB;
72         }
73
74         if (flags & WRDE_APPEND) {
75                 wc = we->we_wordc;
76                 wv = we->we_wordv;
77         }
78
79         i = wc;
80         if (flags & WRDE_DOOFFS) {
81                 if (we->we_offs > SIZE_MAX/sizeof(void *)/4)
82                         return WRDE_NOSPACE;
83                 i += we->we_offs;
84         }
85
86         pipe(p);
87         pid = fork();
88         if (!pid) {
89                 dup2(p[1], 1);
90                 close(p[0]);
91                 close(p[1]);
92                 execl("/bin/sh", "sh", "-c",
93                         "eval \"printf %s\\\\\\\\0 $1 $2\"",
94                         "sh", s, redir, (char *)0);
95                 _exit(1);
96         }
97         close(p[1]);
98         
99         f = fdopen(p[0], "r");
100         if (!f) {
101                 close(p[0]);
102                 kill(pid, SIGKILL);
103                 waitpid(pid, &status, 0);
104                 return WRDE_NOSPACE;
105         }
106
107         l = wv ? i+1 : 0;
108
109         while ((w = getword(f))) {
110                 if (i+1 >= l) {
111                         l += l/2+10;
112                         tmp = realloc(wv, l*sizeof(char *));
113                         if (!tmp) break;
114                         wv = tmp;
115                 }
116                 wv[i++] = w;
117                 wv[i] = 0;
118         }
119         if (!feof(f)) err = WRDE_NOSPACE;
120
121         fclose(f);
122         waitpid(pid, &status, 0);
123         if (WEXITSTATUS(status)) {
124                 if (!(flags & WRDE_APPEND)) {
125                         free(wv);
126                         return WRDE_SYNTAX;
127                 } else if (wv==we->we_wordv) {
128                         return WRDE_SYNTAX;
129                 }
130         }
131
132         we->we_wordv = wv;
133         we->we_wordc = i - we->we_offs;
134         return err;
135 }
136
137 void wordfree(wordexp_t *we)
138 {
139         size_t i;
140         if (!we->we_wordv) return;
141         for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]);
142         free(we->we_wordv);
143         we->we_wordv = 0;
144         we->we_wordc = 0;
145 }