blob: 87a1ba223058e4e97403279ec0d2b7d06e9abef8 [file] [log] [blame]
Viet-Trung Luu96b05c12016-01-11 11:26:36 -08001#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
George Kulakowski17e3b042016-02-18 15:59:50 -080014static void reap(pid_t pid) {
15 int status;
16 for (;;) {
17 if (waitpid(pid, &status, 0) < 0) {
18 if (errno != EINTR)
19 return;
20 } else {
21 if (WIFEXITED(status))
22 return;
23 }
24 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -080025}
26
George Kulakowski17e3b042016-02-18 15:59:50 -080027static char* getword(FILE* f) {
28 char* s = 0;
29 return getdelim(&s, (size_t[1]){0}, 0, f) < 0 ? 0 : s;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -080030}
31
George Kulakowski17e3b042016-02-18 15:59:50 -080032static int do_wordexp(const char* s, wordexp_t* we, int flags) {
33 size_t i, l;
34 int sq = 0, dq = 0;
35 size_t np = 0;
36 char *w, **tmp;
37 char* redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
38 int err = 0;
39 FILE* f;
40 size_t wc = 0;
41 char** wv = 0;
42 int p[2];
43 pid_t pid;
44 sigset_t set;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -080045
George Kulakowski17e3b042016-02-18 15:59:50 -080046 if (flags & WRDE_REUSE)
47 wordfree(we);
Viet-Trung Luu96b05c12016-01-11 11:26:36 -080048
George Kulakowski17e3b042016-02-18 15:59:50 -080049 if (flags & WRDE_NOCMD)
50 for (i = 0; s[i]; i++)
51 switch (s[i]) {
52 case '\\':
53 if (!sq)
54 i++;
55 break;
56 case '\'':
57 if (!dq)
58 sq ^= 1;
59 break;
60 case '"':
61 if (!sq)
62 dq ^= 1;
63 break;
64 case '(':
65 if (np) {
66 np++;
67 break;
68 }
69 case ')':
70 if (np) {
71 np--;
72 break;
73 }
74 case '\n':
75 case '|':
76 case '&':
77 case ';':
78 case '<':
79 case '>':
80 case '{':
81 case '}':
82 if (!(sq | dq | np))
83 return WRDE_BADCHAR;
84 break;
85 case '$':
86 if (sq)
87 break;
88 if (s[i + 1] == '(' && s[i + 2] == '(') {
89 i += 2;
90 np += 2;
91 break;
92 } else if (s[i + 1] != '(')
93 break;
94 case '`':
95 if (sq)
96 break;
97 return WRDE_CMDSUB;
98 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -080099
George Kulakowski17e3b042016-02-18 15:59:50 -0800100 if (flags & WRDE_APPEND) {
101 wc = we->we_wordc;
102 wv = we->we_wordv;
103 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800104
George Kulakowski17e3b042016-02-18 15:59:50 -0800105 i = wc;
106 if (flags & WRDE_DOOFFS) {
107 if (we->we_offs > SIZE_MAX / sizeof(void*) / 4)
108 goto nospace;
109 i += we->we_offs;
110 } else {
111 we->we_offs = 0;
112 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800113
George Kulakowski17e3b042016-02-18 15:59:50 -0800114 if (pipe2(p, O_CLOEXEC) < 0)
115 goto nospace;
116 __block_all_sigs(&set);
117 pid = fork();
118 __restore_sigs(&set);
119 if (pid < 0) {
120 close(p[0]);
121 close(p[1]);
122 goto nospace;
123 }
124 if (!pid) {
125 if (p[1] == 1)
126 fcntl(1, F_SETFD, 0);
127 else
128 dup2(p[1], 1);
129 execl("/bin/sh", "sh", "-c", "eval \"printf %s\\\\\\\\0 x $1 $2\"", "sh", s,
130 redir, (char*)0);
131 _exit(1);
132 }
133 close(p[1]);
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800134
George Kulakowski17e3b042016-02-18 15:59:50 -0800135 f = fdopen(p[0], "r");
136 if (!f) {
137 close(p[0]);
138 kill(pid, SIGKILL);
139 reap(pid);
140 goto nospace;
141 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800142
George Kulakowski17e3b042016-02-18 15:59:50 -0800143 l = wv ? i + 1 : 0;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800144
George Kulakowski17e3b042016-02-18 15:59:50 -0800145 free(getword(f));
146 if (feof(f)) {
147 fclose(f);
148 reap(pid);
149 return WRDE_SYNTAX;
150 }
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800151
George Kulakowski17e3b042016-02-18 15:59:50 -0800152 while ((w = getword(f))) {
153 if (i + 1 >= l) {
154 l += l / 2 + 10;
155 tmp = realloc(wv, l * sizeof(char*));
156 if (!tmp)
157 break;
158 wv = tmp;
159 }
160 wv[i++] = w;
161 wv[i] = 0;
162 }
163 if (!feof(f))
164 err = WRDE_NOSPACE;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800165
George Kulakowski17e3b042016-02-18 15:59:50 -0800166 fclose(f);
167 reap(pid);
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800168
George Kulakowski17e3b042016-02-18 15:59:50 -0800169 if (!wv)
170 wv = calloc(i + 1, sizeof *wv);
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800171
George Kulakowski17e3b042016-02-18 15:59:50 -0800172 we->we_wordv = wv;
173 we->we_wordc = i;
174
175 if (flags & WRDE_DOOFFS) {
176 if (wv)
177 for (i = we->we_offs; i; i--)
178 we->we_wordv[i - 1] = 0;
179 we->we_wordc -= we->we_offs;
180 }
181 return err;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800182
183nospace:
George Kulakowski17e3b042016-02-18 15:59:50 -0800184 if (!(flags & WRDE_APPEND)) {
185 we->we_wordc = 0;
186 we->we_wordv = 0;
187 }
188 return WRDE_NOSPACE;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800189}
190
George Kulakowski17e3b042016-02-18 15:59:50 -0800191int wordexp(const char* restrict s, wordexp_t* restrict we, int flags) {
192 int r, cs;
193 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
194 r = do_wordexp(s, we, flags);
195 pthread_setcancelstate(cs, 0);
196 return r;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800197}
198
George Kulakowski17e3b042016-02-18 15:59:50 -0800199void wordfree(wordexp_t* we) {
200 size_t i;
201 if (!we->we_wordv)
202 return;
203 for (i = 0; i < we->we_wordc; i++)
204 free(we->we_wordv[we->we_offs + i]);
205 free(we->we_wordv);
206 we->we_wordv = 0;
207 we->we_wordc = 0;
Viet-Trung Luu96b05c12016-01-11 11:26:36 -0800208}