1#[repr(C)]
5pub struct WordexpT {
6 pub we_wordc: usize,
7 pub we_wordv: *mut *mut c_char,
8 pub we_offs: usize,
9}
10
11const WRDE_APPEND_V: c_int = 1;
12const WRDE_DOOFFS_V: c_int = 2;
13const WRDE_NOCMD_V: c_int = 4;
14const WRDE_REUSE_V: c_int = 8;
15const WRDE_UNDEF_V: c_int = 32;
16
17const WRDE_NOSPACE_V: c_int = 1;
18const WRDE_BADCHAR_V: c_int = 2;
19const WRDE_BADVAL_V: c_int = 3;
20const WRDE_CMDSUB_V: c_int = 4;
21const WRDE_SYNTAX_V: c_int = 5;
22
23const WORDEXP_CAP: usize = 4096;
24
25struct WordList {
26 items: *mut *mut c_char,
27 n: usize,
28 cap: usize,
29}
30
31unsafe fn wl_push(list: *mut WordList, s: *const u8, len: usize) {
32 if (*list).n == (*list).cap {
33 let ncap = if (*list).cap == 0 { 16 } else { (*list).cap * 2 };
34 let na = crate::realloc(
35 (*list).items as *mut c_void,
36 ncap * core::mem::size_of::<*mut c_char>(),
37 ) as *mut *mut c_char;
38 if na.is_null() { return; }
39 (*list).items = na;
40 (*list).cap = ncap;
41 }
42 let p = crate::malloc(len + 1) as *mut c_char;
43 if p.is_null() { return; }
44 core::ptr::copy_nonoverlapping(s, p as *mut u8, len);
45 *p.add(len) = 0;
46 *(*list).items.add((*list).n) = p;
47 (*list).n += 1;
48}
49
50unsafe fn wl_free(list: *mut WordList) {
51 for i in 0..(*list).n {
52 crate::free(*(*list).items.add(i) as *mut c_void);
53 }
54 crate::free((*list).items as *mut c_void);
55 (*list).items = core::ptr::null_mut();
56 (*list).n = 0;
57 (*list).cap = 0;
58}
59
60struct WordBuf {
61 buf: [u8; WORDEXP_CAP],
62 len: usize,
63 has_glob: bool,
64 quoted: bool,
65}
66
67unsafe fn wb_push(w: *mut WordBuf, c: u8, quoted: bool) -> c_int {
68 if (*w).len + 1 >= WORDEXP_CAP { return WRDE_NOSPACE_V; }
69 (*w).buf[(*w).len] = c;
70 (*w).len += 1;
71 if quoted {
72 (*w).quoted = true;
73 } else if c == b'*' || c == b'?' || c == b'[' {
74 (*w).has_glob = true;
75 }
76 0
77}
78
79unsafe fn wb_push_str(w: *mut WordBuf, s: *const u8, len: usize, quoted: bool) -> c_int {
80 if (*w).len + len >= WORDEXP_CAP { return WRDE_NOSPACE_V; }
81 core::ptr::copy_nonoverlapping(s, (*w).buf.as_mut_ptr().add((*w).len), len);
82 (*w).len += len;
83 if !quoted {
84 for i in 0..len {
85 let c = *s.add(i);
86 if c == b'*' || c == b'?' || c == b'[' { (*w).has_glob = true; break; }
87 }
88 }
89 (*w).quoted = true;
90 0
91}
92
93unsafe fn env_value(name: *const u8, len: usize) -> *mut c_char {
94 let mut buf = [0 as c_char; 256];
95 if len >= buf.len() { return core::ptr::null_mut(); }
96 for i in 0..len { buf[i] = *name.add(i) as c_char; }
97 buf[len] = 0;
98 crate::getenv(buf.as_ptr())
99}
100
101unsafe fn expand_var(
103 s: &[u8],
104 i_start: usize,
105 w: *mut WordBuf,
106 flags: c_int,
107) -> Result<usize, c_int> {
108 let mut i = i_start + 1;
109 let mut name = [0u8; 256];
110 let mut nl = 0usize;
111 if s[i] == b'{' {
112 i += 1;
113 while s[i] != 0 && s[i] != b'}' {
114 if nl + 1 >= name.len() { return Err(WRDE_NOSPACE_V); }
115 name[nl] = s[i];
116 nl += 1;
117 i += 1;
118 }
119 if s[i] != b'}' { return Err(WRDE_SYNTAX_V); }
120 i += 1;
121 } else {
122 while nl + 1 < name.len() {
123 let c = s[i];
124 if (c >= b'A' && c <= b'Z') || (c >= b'a' && c <= b'z') || (c >= b'0' && c <= b'9') || c == b'_' {
125 name[nl] = c;
126 nl += 1;
127 i += 1;
128 } else {
129 break;
130 }
131 }
132 if nl == 0 {
133 let r = wb_push(w, b'$', false);
135 if r != 0 { return Err(r); }
136 return Ok(i_start + 1);
137 }
138 }
139 if nl == 1 && name[0] == b'$' {
140 let mut pidbuf = [0 as c_char; 16];
141 let pid = crate::getpid();
142 let mut n = 0usize;
143 let mut v = pid as u32;
144 let mut digits = [0u8; 10];
145 if v == 0 { digits[0] = b'0'; n = 1; }
146 while v > 0 { digits[n] = b'0' + (v % 10) as u8; v /= 10; n += 1; }
147 let mut o = 0usize;
148 while n > 0 { n -= 1; pidbuf[o] = digits[n] as c_char; o += 1; }
149 pidbuf[o] = 0;
150 let r = wb_push_str(w, pidbuf.as_ptr() as *const u8, o, false);
151 if r != 0 { return Err(r); }
152 return Ok(i);
153 }
154 let val = env_value(name.as_ptr(), nl);
155 if val.is_null() {
156 if flags & WRDE_UNDEF_V != 0 { return Err(WRDE_BADVAL_V); }
157 return Ok(i);
158 }
159 let vl = crate::strlen(val);
160 let r = wb_push_str(w, val as *const u8, vl, false);
161 if r != 0 { return Err(r); }
162 Ok(i)
163}
164
165unsafe fn command_sub(
166 s: &[u8],
167 i_start: usize,
168 w: *mut WordBuf,
169 flags: c_int,
170) -> Result<usize, c_int> {
171 if flags & WRDE_NOCMD_V != 0 { return Err(WRDE_CMDSUB_V); }
172 let mut i = i_start;
173 let mut cmd = [0u8; 2048];
174 let mut cl = 0usize;
175 if s[i] == b'`' {
176 i += 1;
177 while s[i] != 0 && s[i] != b'`' {
178 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
179 cmd[cl] = s[i];
180 cl += 1;
181 i += 1;
182 }
183 if s[i] != b'`' { return Err(WRDE_SYNTAX_V); }
184 i += 1;
185 } else {
186 i += 2;
188 let mut depth = 1;
189 while s[i] != 0 {
190 let c = s[i];
191 if c == b'\\' && s[i + 1] != 0 {
192 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
193 cmd[cl] = c;
194 cl += 1;
195 i += 1;
196 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
197 cmd[cl] = s[i];
198 cl += 1;
199 i += 1;
200 continue;
201 }
202 if c == b'\'' {
203 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
205 cmd[cl] = c;
206 cl += 1;
207 i += 1;
208 while s[i] != 0 && s[i] != b'\'' {
209 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
210 cmd[cl] = s[i];
211 cl += 1;
212 i += 1;
213 }
214 if s[i] == b'\'' {
215 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
216 cmd[cl] = s[i];
217 cl += 1;
218 i += 1;
219 }
220 continue;
221 }
222 if c == b'(' { depth += 1; }
223 if c == b')' {
224 depth -= 1;
225 if depth == 0 { i += 1; break; }
226 }
227 if cl + 1 >= cmd.len() { return Err(WRDE_NOSPACE_V); }
228 cmd[cl] = c;
229 cl += 1;
230 i += 1;
231 }
232 if depth != 0 { return Err(WRDE_SYNTAX_V); }
233 }
234 cmd[cl] = 0;
235 let f = crate::popen(cmd.as_ptr() as *const c_char, b"r\0".as_ptr() as *const c_char);
236 if f.is_null() { return Ok(i); }
237 let mut out = [0u8; 4096];
238 let n = crate::fread(out.as_mut_ptr() as *mut c_void, 1, out.len() - 1, f);
239 crate::pclose(f);
240 let mut l = n;
241 while l > 0 && (out[l - 1] == b'\n' || out[l - 1] == b'\r') { l -= 1; }
242 let mut p = 0usize;
244 let mut first = true;
245 while p < l {
246 while p < l && (out[p] == b' ' || out[p] == b'\t' || out[p] == b'\n') { p += 1; }
247 if p >= l { break; }
248 let st = p;
249 while p < l && out[p] != b' ' && out[p] != b'\t' && out[p] != b'\n' { p += 1; }
250 if !first {
251 }
254 let r = wb_push_str(w, out.as_ptr().add(st), p - st, false);
255 if r != 0 { return Err(r); }
256 first = false;
257 }
258 Ok(i)
259}
260
261#[no_mangle]
262pub unsafe extern "C" fn wordexp(s: *const c_char, p: *mut WordexpT, flags: c_int) -> c_int {
263 if s.is_null() || p.is_null() { return WRDE_SYNTAX_V; }
264 if flags & WRDE_REUSE_V != 0 { wordfree(p); }
265 let mut list = WordList { items: core::ptr::null_mut(), n: 0, cap: 0 };
266 let src = core::slice::from_raw_parts(s as *const u8, crate::strlen(s) + 1);
267 let mut i = 0usize;
268 loop {
269 while src[i] == b' ' || src[i] == b'\t' || src[i] == b'\n' { i += 1; }
270 if src[i] == 0 { break; }
271 let mut w = WordBuf { buf: [0u8; WORDEXP_CAP], len: 0, has_glob: false, quoted: false };
272 let mut done = false;
273 while !done {
274 let c = src[i];
275 match c {
276 0 => { done = true; }
277 b' ' | b'\t' | b'\n' => { i += 1; done = true; }
278 b'\'' => {
279 i += 1;
280 loop {
281 if src[i] == 0 {
282 wl_free(&mut list);
283 return WRDE_SYNTAX_V;
284 }
285 if src[i] == b'\'' { i += 1; break; }
286 let r = wb_push(&mut w, src[i], true);
287 if r != 0 { wl_free(&mut list); return r; }
288 i += 1;
289 }
290 w.quoted = true;
291 }
292 b'"' => {
293 i += 1;
294 loop {
295 if src[i] == 0 {
296 wl_free(&mut list);
297 return WRDE_SYNTAX_V;
298 }
299 if src[i] == b'"' { i += 1; break; }
300 if src[i] == b'\\' {
301 let n = src[i + 1];
302 if n == b'$' || n == b'`' || n == b'"' || n == b'\\' {
303 let r = wb_push(&mut w, n, true);
304 if r != 0 { wl_free(&mut list); return r; }
305 i += 2;
306 continue;
307 }
308 let r = wb_push(&mut w, src[i], true);
309 if r != 0 { wl_free(&mut list); return r; }
310 i += 1;
311 continue;
312 }
313 if src[i] == b'`' || (src[i] == b'$' && src[i + 1] == b'(') {
314 match command_sub(src, i, &mut w, flags) {
315 Ok(ni) => { i = ni; continue; }
316 Err(e) => { wl_free(&mut list); return e; }
317 }
318 }
319 if src[i] == b'$' {
320 match expand_var(src, i, &mut w, flags) {
321 Ok(ni) => { i = ni; continue; }
322 Err(e) => { wl_free(&mut list); return e; }
323 }
324 }
325 let r = wb_push(&mut w, src[i], true);
326 if r != 0 { wl_free(&mut list); return r; }
327 i += 1;
328 }
329 w.quoted = true;
330 }
331 b'\\' => {
332 i += 1;
333 if src[i] == 0 {
334 wl_free(&mut list);
335 return WRDE_SYNTAX_V;
336 }
337 let r = wb_push(&mut w, src[i], true);
338 if r != 0 { wl_free(&mut list); return r; }
339 i += 1;
340 }
341 b'$' => {
342 if src[i + 1] == b'(' {
343 match command_sub(src, i, &mut w, flags) {
344 Ok(ni) => { i = ni; }
345 Err(e) => { wl_free(&mut list); return e; }
346 }
347 } else {
348 match expand_var(src, i, &mut w, flags) {
349 Ok(ni) => { i = ni; }
350 Err(e) => { wl_free(&mut list); return e; }
351 }
352 }
353 }
354 b'`' => {
355 match command_sub(src, i, &mut w, flags) {
356 Ok(ni) => { i = ni; }
357 Err(e) => { wl_free(&mut list); return e; }
358 }
359 }
360 b'*' | b'?' | b'[' => {
361 w.has_glob = true;
362 let r = wb_push(&mut w, c, false);
363 if r != 0 { wl_free(&mut list); return r; }
364 i += 1;
365 }
366 _ => {
367 let r = wb_push(&mut w, c, false);
368 if r != 0 { wl_free(&mut list); return r; }
369 i += 1;
370 }
371 }
372 }
373 if w.len == 0 && !w.quoted { continue; }
374 w.buf[w.len] = 0;
375 if w.has_glob && !w.quoted {
376 let mut g: GlobT = core::mem::zeroed();
377 let r = crate::glob(w.buf.as_ptr() as *const c_char, 0x10 | 0x04, None, &mut g); if r == 0 {
379 for k in 0..g.gl_pathc {
380 let gp = *g.gl_pathv.add(k);
381 let l = crate::strlen(gp);
382 wl_push(&mut list, gp as *const u8, l);
383 }
384 crate::globfree(&mut g);
385 } else {
386 wl_push(&mut list, w.buf.as_ptr(), w.len);
387 }
388 } else {
389 wl_push(&mut list, w.buf.as_ptr(), w.len);
390 }
391 }
392
393 let old = if flags & WRDE_APPEND_V != 0 { (*p).we_wordc } else { 0 };
394 let offs = if flags & WRDE_DOOFFS_V != 0 { (*p).we_offs } else { 0 };
395 let total = offs + old + list.n + 1;
396 let pathv = crate::calloc(total, core::mem::size_of::<*mut c_char>()) as *mut *mut c_char;
397 if pathv.is_null() {
398 wl_free(&mut list);
399 return WRDE_NOSPACE_V;
400 }
401 for k in 0..offs { *pathv.add(k) = core::ptr::null_mut(); }
402 if flags & WRDE_APPEND_V != 0 && old != 0 && !(*p).we_wordv.is_null() {
403 for k in 0..old {
404 *pathv.add(offs + k) = *(*p).we_wordv.add(offs + k);
405 }
406 crate::free((*p).we_wordv as *mut c_void);
407 }
408 for k in 0..list.n {
409 *pathv.add(offs + old + k) = *list.items.add(k);
410 }
411 *pathv.add(offs + old + list.n) = core::ptr::null_mut();
412 crate::free(list.items as *mut c_void);
413 (*p).we_wordc = old + list.n;
414 (*p).we_wordv = pathv;
415 (*p).we_offs = offs;
416 0
417}
418
419#[no_mangle]
420pub unsafe extern "C" fn wordfree(p: *mut WordexpT) {
421 if p.is_null() || (*p).we_wordv.is_null() { return; }
422 let offs = (*p).we_offs;
423 for i in 0..(*p).we_wordc {
424 let q = *(*p).we_wordv.add(offs + i);
425 if !q.is_null() { crate::free(q as *mut c_void); }
426 }
427 crate::free((*p).we_wordv as *mut c_void);
428 (*p).we_wordv = core::ptr::null_mut();
429 (*p).we_wordc = 0;
430}