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