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