1const PATH_MAX_V: usize = 4096;
4
5static mut REALPATH_BUF: [c_char; PATH_MAX_V] = [0; PATH_MAX_V];
10
11unsafe fn rp_is_dir(path: *const c_char) -> bool {
12 let mut st: Stat = core::mem::zeroed();
13 if crate::lstat(path, &mut st as *mut Stat as *mut c_void) != 0 { return false; }
14 st.st_mode & 0o170000 == 0o040000
15}
16
17#[no_mangle]
18pub unsafe extern "C" fn realpath(
19 filename: *const c_char,
20 resolved: *mut c_char,
21) -> *mut c_char {
22 if filename.is_null() { ERRNO!() = EINVAL; return core::ptr::null_mut(); }
23 let flen = crate::strlen(filename);
24 if flen == 0 { ERRNO!() = 2; return core::ptr::null_mut(); }
25 if flen >= PATH_MAX_V { ERRNO!() = 36; return core::ptr::null_mut(); }
26
27 let mut out = [0u8; PATH_MAX_V + 1];
28 let mut olen;
29 let mut pend = [0u8; 2 * PATH_MAX_V + 1];
30 core::ptr::copy_nonoverlapping(filename as *const u8, pend.as_mut_ptr(), flen);
31 pend[flen] = 0;
32 let mut pi = 0usize;
33 let absolute = pend[0] == b'/';
34 if absolute {
35 pi = 1;
36 out[0] = b'/';
37 olen = 1;
38 } else {
39 let r = crate::getcwd(out.as_mut_ptr() as *mut c_char, PATH_MAX_V);
40 if r.is_null() { return core::ptr::null_mut(); }
41 olen = crate::strlen(out.as_ptr() as *const c_char);
42 if olen == 0 { out[0] = b'/'; olen = 1; }
43 }
44 let mut symlinks = 0;
45
46 loop {
47 while pend[pi] == b'/' { pi += 1; }
48 if pend[pi] == 0 { break; }
49 let start = pi;
50 while pend[pi] != 0 && pend[pi] != b'/' { pi += 1; }
51 let comp_len = pi - start;
52 let mut q = pi;
53 while pend[q] == b'/' { q += 1; }
54 let is_last = pend[q] == 0;
55 let followed_by_slash = pend[pi] == b'/';
56 let needs_dir = !is_last || followed_by_slash;
57
58 if comp_len == 1 && pend[start] == b'.' {
59 if needs_dir && is_last { }
60 continue;
61 }
62 if comp_len == 2 && pend[start] == b'.' && pend[start + 1] == b'.' {
63 while olen > 0 && out[olen - 1] != b'/' { olen -= 1; }
64 if olen > 0 { olen -= 1; }
65 continue;
66 }
67
68 if olen == 0 {
69 out[0] = b'/';
70 olen = 1;
71 }
72 if olen > 0 && out[olen - 1] != b'/' {
73 if olen + 1 >= PATH_MAX_V { ERRNO!() = 36; return core::ptr::null_mut(); }
74 out[olen] = b'/';
75 olen += 1;
76 }
77 if olen + comp_len >= PATH_MAX_V { ERRNO!() = 36; return core::ptr::null_mut(); }
78 core::ptr::copy_nonoverlapping(pend.as_ptr().add(start), out.as_mut_ptr().add(olen), comp_len);
79 olen += comp_len;
80 out[olen] = 0;
81
82 let mut st: Stat = core::mem::zeroed();
83 if crate::lstat(out.as_ptr() as *const c_char, &mut st as *mut Stat as *mut c_void) != 0 {
84 if ERRNO!() == 2 { ERRNO!() = 2; }
85 return core::ptr::null_mut();
86 }
87 let mode = st.st_mode;
88 if mode & 0o170000 == 0o120000 {
89 symlinks += 1;
90 if symlinks > 40 { ERRNO!() = 40; return core::ptr::null_mut(); }
91 let mut link = [0u8; PATH_MAX_V + 1];
92 let n = crate::readlink(out.as_ptr() as *const c_char, link.as_mut_ptr() as *mut c_char, PATH_MAX_V);
93 if n < 0 { return core::ptr::null_mut(); }
94 let n = n as usize;
95 if n == 0 { ERRNO!() = 2; return core::ptr::null_mut(); }
96 link[n] = 0;
97 while olen > 0 && out[olen - 1] != b'/' { olen -= 1; }
99 if olen > 0 { olen -= 1; }
100 let mut next = [0u8; 2 * PATH_MAX_V + 1];
101 let link_abs = link[0] == b'/';
102 let mut nl = n;
103 core::ptr::copy_nonoverlapping(link.as_ptr(), next.as_mut_ptr(), n);
104 let rest = &pend[pi..];
105 let rest_len = crate::strlen(rest.as_ptr() as *const c_char);
106 if rest_len > 0 {
107 if nl > 0 && next[nl - 1] != b'/' {
108 next[nl] = b'/';
109 nl += 1;
110 }
111 if nl + rest_len >= next.len() { ERRNO!() = 36; return core::ptr::null_mut(); }
112 core::ptr::copy_nonoverlapping(rest.as_ptr(), next.as_mut_ptr().add(nl), rest_len + 1);
113 nl += rest_len;
114 }
115 if link_abs { olen = 0; }
116 pend = next;
117 pi = 0;
118 let _ = nl;
119 continue;
120 }
121 if mode & 0o170000 != 0o040000 && needs_dir {
122 ERRNO!() = 20; return core::ptr::null_mut();
124 }
125 }
126
127 if olen == 0 {
128 out[0] = b'/';
129 olen = 1;
130 }
131 while olen > 1 && out[olen - 1] == b'/' { olen -= 1; }
133 out[olen] = 0;
134 let dst = if resolved.is_null() { REALPATH_BUF.as_mut_ptr() } else { resolved };
135 core::ptr::copy_nonoverlapping(out.as_ptr(), dst as *mut u8, olen + 1);
136 dst
137}
138
139#[repr(C)]
144pub struct GlobT {
145 pub gl_pathc: usize,
146 pub gl_pathv: *mut *mut c_char,
147 pub gl_offs: usize,
148 __dummy1: c_int,
149 __dummy2: [*mut c_void; 5],
150}
151
152const GLOB_ERR_V: c_int = 0x01;
153const GLOB_MARK_V: c_int = 0x02;
154const GLOB_NOSORT_V: c_int = 0x04;
155const GLOB_DOOFFS_V: c_int = 0x08;
156const GLOB_NOCHECK_V: c_int = 0x10;
157const GLOB_APPEND_V: c_int = 0x20;
158const GLOB_NOESCAPE_V: c_int = 0x40;
159const GLOB_PERIOD_V: c_int = 0x80;
160const GLOB_TILDE_V: c_int = 0x1000;
161const GLOB_TILDE_CHECK_V: c_int = 0x4000;
162
163const GLOB_NOSPACE_V: c_int = 1;
164const GLOB_ABORTED_V: c_int = 2;
165const GLOB_NOMATCH_V: c_int = 3;
166
167struct GlobList {
168 items: *mut *mut c_char,
169 n: usize,
170 cap: usize,
171}
172
173unsafe fn glob_list_push(list: *mut GlobList, s: *const c_char) {
174 if (*list).n == (*list).cap {
175 let ncap = if (*list).cap == 0 { 32 } else { (*list).cap * 2 };
176 let na = crate::realloc(
177 (*list).items as *mut c_void,
178 ncap * core::mem::size_of::<*mut c_char>(),
179 ) as *mut *mut c_char;
180 if na.is_null() { return; }
181 (*list).items = na;
182 (*list).cap = ncap;
183 }
184 let l = crate::strlen(s);
185 let copy = crate::malloc(l + 1) as *mut c_char;
186 if copy.is_null() { return; }
187 core::ptr::copy_nonoverlapping(s as *const u8, copy as *mut u8, l + 1);
188 *(*list).items.add((*list).n) = copy;
189 (*list).n += 1;
190}
191
192unsafe fn glob_list_push_n(list: *mut GlobList, s: *const u8, len: usize) {
193 if (*list).n == (*list).cap {
194 let ncap = if (*list).cap == 0 { 32 } else { (*list).cap * 2 };
195 let na = crate::realloc(
196 (*list).items as *mut c_void,
197 ncap * core::mem::size_of::<*mut c_char>(),
198 ) as *mut *mut c_char;
199 if na.is_null() { return; }
200 (*list).items = na;
201 (*list).cap = ncap;
202 }
203 let copy = crate::malloc(len + 1) as *mut c_char;
204 if copy.is_null() { return; }
205 core::ptr::copy_nonoverlapping(s, copy as *mut u8, len);
206 *copy.add(len) = 0;
207 *(*list).items.add((*list).n) = copy;
208 (*list).n += 1;
209}
210
211unsafe fn list_dir_names(path: *const c_char) -> (*mut *mut c_char, usize) {
212 let fd = crate::open(path, crate::O_DIRECTORY, 0);
213 if fd < 0 { return (core::ptr::null_mut(), 0); }
214 let mut cap = 32usize;
215 let mut count = 0usize;
216 let mut arr = crate::malloc(cap * core::mem::size_of::<*mut c_char>()) as *mut *mut c_char;
217 if arr.is_null() { crate::close(fd); return (core::ptr::null_mut(), 0); }
218 #[repr(align(8))]
219 struct Buf([u8; 8192]);
220 let mut buf = Buf([0u8; 8192]);
221 let name_off = core::mem::offset_of!(linux_dirent64, d_name);
222 loop {
223 let n = <Arch as Syscalls>::syscall3(
224 SYS_GETDENTS64,
225 fd as i64,
226 buf.0.as_mut_ptr() as i64,
227 buf.0.len() as i64,
228 ) as isize;
229 if n <= 0 { break; }
230 let mut off = 0usize;
231 while off + name_off < n as usize {
232 let ent = buf.0.as_ptr().add(off) as *const linux_dirent64;
233 let reclen = (*ent).d_reclen as usize;
234 if reclen == 0 { break; }
235 let name = buf.0.as_ptr().add(off + name_off) as *const c_char;
236 let nl = crate::strlen(name);
237 if count + 2 >= cap {
238 cap *= 2;
239 let na = crate::realloc(arr as *mut c_void, cap * core::mem::size_of::<*mut c_char>()) as *mut *mut c_char;
240 if na.is_null() { break; }
241 arr = na;
242 }
243 let copy = crate::malloc(nl + 1) as *mut c_char;
244 if copy.is_null() { break; }
245 core::ptr::copy_nonoverlapping(name as *const u8, copy as *mut u8, nl + 1);
246 *arr.add(count) = copy;
247 count += 1;
248 off += reclen;
249 }
250 }
251 crate::close(fd);
252 (arr, count)
253}
254
255unsafe fn free_names(arr: *mut *mut c_char, n: usize) {
256 if arr.is_null() { return; }
257 for i in 0..n {
258 if !(*arr.add(i)).is_null() { crate::free(*arr.add(i) as *mut c_void); }
259 }
260 crate::free(arr as *mut c_void);
261}
262
263fn glob_has_magic(comp: &[u8], noescape: bool) -> bool {
264 let mut i = 0;
265 while i < comp.len() {
266 match comp[i] {
267 b'\\' if !noescape => i += 1,
268 b'*' | b'?' | b'[' => return true,
269 _ => {}
270 }
271 i += 1;
272 }
273 false
274}
275
276unsafe fn glob_unescape(dst: &mut [u8], comp: &[u8], noescape: bool) -> usize {
277 let mut o = 0usize;
278 let mut i = 0usize;
279 while i < comp.len() {
280 if comp[i] == b'\\' && !noescape && i + 1 < comp.len() {
281 i += 1;
282 }
283 if o < dst.len() { dst[o] = comp[i]; o += 1; }
284 i += 1;
285 }
286 o
287}
288
289#[derive(Copy, Clone)]
290struct Comp {
291 start: usize,
292 end: usize,
293}
294
295unsafe fn glob_split(pat: &[u8], noescape: bool, comps: &mut [Comp; 128]) -> (usize, bool) {
296 let mut n = 0usize;
297 let mut i = 0usize;
298 let absolute = !pat.is_empty() && pat[0] == b'/';
299 let mut trailing = false;
300 while i < pat.len() {
301 while i < pat.len() && pat[i] == b'/' { i += 1; }
302 if i >= pat.len() { if i > 0 { trailing = pat[i - 1] == b'/'; } break; }
303 let start = i;
304 while i < pat.len() {
305 if pat[i] == b'\\' && !noescape && i + 1 < pat.len() { i += 2; continue; }
306 if pat[i] == b'/' { break; }
307 i += 1;
308 }
309 if n < comps.len() {
310 comps[n] = Comp { start, end: i };
311 n += 1;
312 }
313 }
314 let _ = trailing;
315 (n, absolute)
316}
317
318unsafe fn glob_walk(
319 base: &mut [u8; PATH_MAX_V + 1],
320 blen: &mut usize,
321 pat: &[u8],
322 comps: &[Comp],
323 ci: usize,
324 trailing: bool,
325 flags: c_int,
326 errfunc: Option<unsafe extern "C" fn(*const c_char, c_int) -> c_int>,
327 list: *mut GlobList,
328) -> c_int {
329 if ci >= comps.len() {
330 glob_list_push_n(list, base.as_ptr(), *blen);
331 return 0;
332 }
333 let comp = &pat[comps[ci].start..comps[ci].end];
334 let last = ci + 1 == comps.len();
335 let noescape = flags & GLOB_NOESCAPE_V != 0;
336
337 if !glob_has_magic(comp, noescape) {
338 let mut name = [0u8; PATH_MAX_V + 1];
339 let nlen = glob_unescape(&mut name, comp, noescape);
340 let mut newlen = *blen;
341 if newlen > 0 && base[newlen - 1] != b'/' {
342 if newlen + 1 >= base.len() { return GLOB_NOSPACE_V; }
343 base[newlen] = b'/';
344 newlen += 1;
345 }
346 if newlen + nlen >= base.len() { return GLOB_NOSPACE_V; }
347 core::ptr::copy_nonoverlapping(name.as_ptr(), base.as_mut_ptr().add(newlen), nlen);
348 newlen += nlen;
349 base[newlen] = 0;
350 let mut st: Stat = core::mem::zeroed();
351 let exists = crate::lstat(base.as_ptr() as *const c_char, &mut st as *mut Stat as *mut c_void) == 0;
352 let is_dir = exists && st.st_mode & 0o170000 == 0o040000;
353 if last {
354 if exists && (!trailing || is_dir) {
355 if flags & GLOB_ONLYDIR_V != 0 && !is_dir {
356 *blen = newlen;
357 return 0;
358 }
359 let mut end = newlen;
360 if (flags & GLOB_MARK_V != 0) && is_dir {
361 if end + 1 < base.len() {
362 base[end] = b'/';
363 end += 1;
364 }
365 }
366 glob_list_push_n(list, base.as_ptr(), end);
367 }
368 } else {
369 if !is_dir {
370 if flags & GLOB_ERR_V != 0 {
371 *blen = newlen;
372 return GLOB_ABORTED_V;
373 }
374 } else {
375 let old = *blen;
376 *blen = newlen;
377 let r = glob_walk(base, blen, pat, comps, ci + 1, trailing, flags, errfunc, list);
378 *blen = old;
379 if r != 0 { return r; }
380 }
381 }
382 *blen = newlen;
383 return 0;
384 }
385
386 let mut dirc = [0 as c_char; PATH_MAX_V + 1];
388 let dp: *const c_char;
389 if *blen == 0 {
390 dirc[0] = b'.' as c_char;
391 dirc[1] = 0;
392 dp = dirc.as_ptr();
393 } else {
394 core::ptr::copy_nonoverlapping(base.as_ptr(), dirc.as_mut_ptr() as *mut u8, *blen);
395 dirc[*blen] = 0;
396 dp = dirc.as_ptr();
397 }
398 let (names, count) = list_dir_names(dp);
399 if names.is_null() {
400 if flags & GLOB_ERR_V != 0 {
401 if let Some(f) = errfunc { let _ = f(dp, ERRNO!()); }
402 return GLOB_ABORTED_V;
403 }
404 return 0;
405 }
406 let mut patc = [0 as c_char; PATH_MAX_V + 1];
407 core::ptr::copy_nonoverlapping(comp.as_ptr(), patc.as_mut_ptr() as *mut u8, comp.len());
408 patc[comp.len()] = 0;
409 let mut fnm_flags = 0;
410 if noescape { fnm_flags |= FNM_NOESCAPE; }
411 if flags & GLOB_PERIOD_V == 0 { fnm_flags |= FNM_PERIOD; }
412 let old = *blen;
413 for i in 0..count {
414 let name = *names.add(i);
415 if flags & GLOB_PERIOD_V == 0 {
416 if *name == b'.' as c_char {
418 let second = *name.add(1);
419 if second == 0 || (second == b'.' as c_char && *name.add(2) == 0) { continue; }
420 }
421 }
422 if crate::fnmatch(patc.as_ptr(), name, fnm_flags) != 0 { continue; }
423 let nlen = crate::strlen(name);
424 let mut newlen = old;
425 if newlen > 0 && base[newlen - 1] != b'/' {
426 if newlen + 1 >= base.len() { free_names(names, count); return GLOB_NOSPACE_V; }
427 base[newlen] = b'/';
428 newlen += 1;
429 }
430 if newlen + nlen >= base.len() { free_names(names, count); return GLOB_NOSPACE_V; }
431 core::ptr::copy_nonoverlapping(name as *const u8, base.as_mut_ptr().add(newlen), nlen);
432 newlen += nlen;
433 base[newlen] = 0;
434 let mut st: Stat = core::mem::zeroed();
435 let exists = crate::lstat(base.as_ptr() as *const c_char, &mut st as *mut Stat as *mut c_void) == 0;
436 let is_dir = exists && st.st_mode & 0o170000 == 0o040000;
437 if last {
438 if flags & GLOB_ONLYDIR_V != 0 && !is_dir { continue; }
439 let mut end = newlen;
440 if (flags & GLOB_MARK_V != 0) && is_dir && end + 1 < base.len() {
441 base[end] = b'/';
442 end += 1;
443 }
444 glob_list_push_n(list, base.as_ptr(), end);
445 } else if is_dir {
446 *blen = newlen;
447 let r = glob_walk(base, blen, pat, comps, ci + 1, trailing, flags, errfunc, list);
448 *blen = old;
449 if r != 0 { free_names(names, count); return r; }
450 }
451 }
452 free_names(names, count);
453 *blen = old;
454 0
455}
456
457const GLOB_ONLYDIR_V: c_int = 0x200; unsafe fn glob_expand_tilde(pat: *const c_char, buf: *mut u8, cap: usize) -> Option<usize> {
460 let s = pat as *const u8;
461 if *s != b'~' { return None; }
462 let mut i = 1usize;
463 while *s.add(i) != 0 && *s.add(i) != b'/' { i += 1; }
464 let mut name = [0 as c_char; 256];
465 let nlen = i - 1;
466 if nlen >= name.len() { return None; }
467 core::ptr::copy_nonoverlapping(s.add(1), name.as_mut_ptr() as *mut u8, nlen);
468 name[nlen] = 0;
469 let pw = if nlen == 0 {
470 crate::getpwuid(crate::getuid())
471 } else {
472 crate::getpwnam(name.as_ptr())
473 };
474 if pw.is_null() { return None; }
475 let home = (*pw).pw_dir;
476 let hl = crate::strlen(home);
477 let rest = crate::strlen(s.add(i) as *const c_char);
478 if hl + rest + 1 > cap { return None; }
479 core::ptr::copy_nonoverlapping(home as *const u8, buf, hl);
480 core::ptr::copy_nonoverlapping(s.add(i), buf.add(hl), rest + 1);
481 Some(hl + rest)
482}
483
484#[no_mangle]
485pub unsafe extern "C" fn glob(
486 pattern: *const c_char,
487 flags: c_int,
488 errfunc: Option<unsafe extern "C" fn(*const c_char, c_int) -> c_int>,
489 pglob: *mut GlobT,
490) -> c_int {
491 if pattern.is_null() || pglob.is_null() { ERRNO!() = EINVAL; return GLOB_ABORTED_V; }
492
493 let mut expanded = [0u8; PATH_MAX_V + 1];
494 let mut pat_ptr = pattern as *const u8;
495 let mut pat_len = crate::strlen(pattern);
496 if flags & (GLOB_TILDE_V | GLOB_TILDE_CHECK_V) != 0 {
497 if let Some(n) = glob_expand_tilde(pattern, expanded.as_mut_ptr(), PATH_MAX_V) {
498 pat_ptr = expanded.as_ptr();
499 pat_len = n;
500 } else if flags & GLOB_TILDE_CHECK_V != 0 {
501 return GLOB_NOMATCH_V;
502 }
503 }
504 let pat = &core::slice::from_raw_parts(pat_ptr, pat_len)[..];
505
506 let mut comps = [Comp { start: 0, end: 0 }; 128];
507 let (ncomp, absolute) = glob_split(pat, flags & GLOB_NOESCAPE_V != 0, &mut comps);
508
509 let mut list = GlobList { items: core::ptr::null_mut(), n: 0, cap: 0 };
510 let mut base = [0u8; PATH_MAX_V + 1];
511 let mut blen = 0usize;
512 if absolute {
513 base[0] = b'/';
514 blen = 1;
515 }
516 let trailing = !pat.is_empty() && pat[pat.len() - 1] == b'/';
517 let r = if ncomp == 0 {
518 if absolute {
519 glob_list_push_n(&mut list, b"/".as_ptr(), 1);
520 0
521 } else {
522 GLOB_NOMATCH_V
523 }
524 } else {
525 glob_walk(&mut base, &mut blen, pat, &comps[..ncomp], 0, trailing, flags, errfunc, &mut list)
526 };
527 if r == GLOB_ABORTED_V {
528 for i in 0..list.n { crate::free(*list.items.add(i) as *mut c_void); }
529 crate::free(list.items as *mut c_void);
530 return r;
531 }
532 let mut matches = list.n;
533 if matches == 0 && flags & GLOB_NOCHECK_V != 0 {
534 glob_list_push(&mut list, pattern);
535 matches = list.n;
536 }
537 if matches == 0 && flags & GLOB_APPEND_V == 0 {
538 crate::free(list.items as *mut c_void);
539 return GLOB_NOMATCH_V;
540 }
541 if flags & GLOB_NOSORT_V == 0 && matches > 1 {
542 crate::qsort(
543 list.items as *mut c_void,
544 matches,
545 core::mem::size_of::<*mut c_char>(),
546 glob_cmp,
547 );
548 }
549
550 let old_n = if flags & GLOB_APPEND_V != 0 { (*pglob).gl_pathc } else { 0 };
551 let offs = if flags & GLOB_DOOFFS_V != 0 { (*pglob).gl_offs } else { 0 };
552 if old_n != 0 && !(*pglob).gl_pathv.is_null() && flags & GLOB_APPEND_V == 0 {
553 }
555 let total = offs + old_n + matches + 1;
556 let pathv = crate::calloc(total, core::mem::size_of::<*mut c_char>()) as *mut *mut c_char;
557 if pathv.is_null() {
558 for i in 0..list.n { crate::free(*list.items.add(i) as *mut c_void); }
559 crate::free(list.items as *mut c_void);
560 return GLOB_NOSPACE_V;
561 }
562 for i in 0..offs { *pathv.add(i) = core::ptr::null_mut(); }
563 if flags & GLOB_APPEND_V != 0 && old_n != 0 && !(*pglob).gl_pathv.is_null() {
564 for i in 0..old_n {
565 *pathv.add(offs + i) = *(*pglob).gl_pathv.add(offs + i);
566 }
567 crate::free((*pglob).gl_pathv as *mut c_void);
568 }
569 for i in 0..list.n {
570 *pathv.add(offs + old_n + i) = *list.items.add(i);
571 }
572 *pathv.add(offs + old_n + matches) = core::ptr::null_mut();
573 crate::free(list.items as *mut c_void);
574
575 (*pglob).gl_pathc = old_n + matches;
576 (*pglob).gl_pathv = pathv;
577 (*pglob).gl_offs = offs;
578 (*pglob).__dummy1 = 0;
579 0
580}
581
582unsafe extern "C" fn glob_cmp(a: *const c_void, b: *const c_void) -> c_int {
583 let sa = *(a as *const *const c_char);
584 let sb = *(b as *const *const c_char);
585 crate::strcmp(sa as *const u8, sb as *const u8)
586}
587
588#[no_mangle]
589pub unsafe extern "C" fn globfree(pglob: *mut GlobT) {
590 if pglob.is_null() || (*pglob).gl_pathv.is_null() { return; }
591 let offs = (*pglob).gl_offs;
592 for i in 0..(*pglob).gl_pathc {
593 let p = *(*pglob).gl_pathv.add(offs + i);
594 if !p.is_null() { crate::free(p as *mut c_void); }
595 }
596 crate::free((*pglob).gl_pathv as *mut c_void);
597 (*pglob).gl_pathv = core::ptr::null_mut();
598 (*pglob).gl_pathc = 0;
599}
600
601#[repr(C)]
606pub struct Ftw {
607 pub base: c_int,
608 pub level: c_int,
609}
610
611const FTW_F_V: c_int = 1;
612const FTW_D_V: c_int = 2;
613const FTW_DNR_V: c_int = 3;
614const FTW_NS_V: c_int = 4;
615const FTW_SL_V: c_int = 5;
616const FTW_DP_V: c_int = 6;
617const FTW_SLN_V: c_int = 7;
618
619const FTW_PHYS_V: c_int = 1;
620const FTW_MOUNT_V: c_int = 2;
621const FTW_DEPTH_V: c_int = 8;
622
623struct History {
624 chain: *mut History,
625 dev: u64,
626 ino: u64,
627 level: c_int,
628 base: c_int,
629}
630
631#[derive(Copy, Clone)]
632enum WalkCb {
633 N(Option<unsafe extern "C" fn(*const c_char, *const Stat, c_int, *mut Ftw) -> c_int>),
634 F(Option<unsafe extern "C" fn(*const c_char, *const Stat, c_int) -> c_int>),
635}
636
637unsafe fn walk_call(cb: WalkCb, path: *const c_char, st: *const Stat, type_: c_int, ftw: *mut Ftw) -> c_int {
638 match cb {
639 WalkCb::N(Some(f)) => f(path, st, type_, ftw),
640 WalkCb::F(Some(f)) => f(path, st, type_),
641 _ => 0,
642 }
643}
644
645unsafe fn nftw_do(
646 buf: *mut u8,
647 cap: usize,
648 len: usize,
649 cb: WalkCb,
650 fd_limit: c_int,
651 flags: c_int,
652 h: *mut History,
653 _base: c_int,
654 level: c_int,
655) -> c_int {
656 let path = buf as *const c_char;
657 let mut st: Stat = core::mem::zeroed();
658 let phys = flags & FTW_PHYS_V != 0;
659 let mut type_: c_int;
660 let mut stat_ok = true;
661 let rc = if phys { crate::lstat(path, &mut st as *mut Stat as *mut c_void) } else { crate::stat(path, &mut st) };
662 if rc != 0 {
663 if !phys && ERRNO!() == 2 && crate::lstat(path, &mut st as *mut Stat as *mut c_void) == 0 {
664 type_ = FTW_SLN_V;
665 } else if ERRNO!() != 13 {
666 return -1;
667 } else {
668 type_ = FTW_NS_V;
669 st = core::mem::zeroed();
670 stat_ok = false;
671 }
672 } else {
673 let mode = st.st_mode;
674 if mode & 0o170000 == 0o040000 {
675 type_ = if flags & FTW_DEPTH_V != 0 { FTW_DP_V } else { FTW_D_V };
676 } else if mode & 0o170000 == 0o120000 {
677 type_ = if phys { FTW_SL_V } else { FTW_SLN_V };
678 } else {
679 type_ = FTW_F_V;
680 }
681 }
682 if flags & FTW_MOUNT_V != 0 && !h.is_null() && stat_ok && type_ != FTW_NS_V && st.st_dev != (*h).dev {
683 return 0;
684 }
685 let parent_base = if h.is_null() {
686 let mut k = len;
687 while k > 0 && *buf.add(k - 1) == b'/' { k -= 1; }
688 while k > 0 && *buf.add(k - 1) != b'/' { k -= 1; }
689 k as c_int
690 } else {
691 (*h).base
692 };
693 let mut new = History {
694 chain: h,
695 dev: st.st_dev,
696 ino: st.st_ino,
697 level: level + 1,
698 base: parent_base,
699 };
700 let mut ftw = Ftw { base: parent_base, level: level };
701
702 let mut dfd: c_int = -1;
703 let mut open_err: c_int = 0;
704 if type_ == FTW_D_V || type_ == FTW_DP_V {
705 dfd = crate::open(path, 0, 0);
706 open_err = ERRNO!();
707 if dfd < 0 && open_err == 13 { type_ = FTW_DNR_V; }
708 if fd_limit == 0 && dfd >= 0 { crate::close(dfd); dfd = -1; }
709 }
710
711 if flags & FTW_DEPTH_V == 0 {
712 let r = walk_call(cb, path, &st, type_, &mut ftw);
713 if r != 0 { return r; }
714 }
715
716 let mut p = h;
717 while !p.is_null() {
718 if (*p).dev == st.st_dev && (*p).ino == st.st_ino { return 0; }
719 p = (*p).chain;
720 }
721
722 if (type_ == FTW_D_V || type_ == FTW_DP_V) && fd_limit > 0 {
723 if dfd < 0 {
724 ERRNO!() = open_err;
725 return -1;
726 }
727 #[repr(align(8))]
729 struct Buf([u8; 8192]);
730 let mut dbuf = Buf([0u8; 8192]);
731 let name_off = core::mem::offset_of!(linux_dirent64, d_name);
732 loop {
733 let n = <Arch as Syscalls>::syscall3(
734 SYS_GETDENTS64,
735 dfd as i64,
736 dbuf.0.as_mut_ptr() as i64,
737 dbuf.0.len() as i64,
738 ) as isize;
739 if n <= 0 { break; }
740 let mut off = 0usize;
741 while off + name_off < n as usize {
742 let ent = dbuf.0.as_ptr().add(off) as *const linux_dirent64;
743 let reclen = (*ent).d_reclen as usize;
744 if reclen == 0 { break; }
745 let name = dbuf.0.as_ptr().add(off + name_off) as *const c_char;
746 off += reclen;
747 if *name == b'.' as c_char {
748 let s = *name.add(1);
749 if s == 0 || (s == b'.' as c_char && *name.add(2) == 0) { continue; }
750 }
751 let nl = crate::strlen(name);
752 if len + 1 + nl >= cap {
753 ERRNO!() = 36;
754 crate::close(dfd);
755 return -1;
756 }
757 *buf.add(len) = b'/';
758 core::ptr::copy_nonoverlapping(name as *const u8, buf.add(len + 1), nl + 1);
759 let r = nftw_do(buf, cap, len + 1 + nl, cb, fd_limit - 1, flags, &mut new, parent_base, level + 1);
760 if r != 0 { crate::close(dfd); return r; }
761 }
762 }
763 crate::close(dfd);
764 } else if dfd >= 0 {
765 crate::close(dfd);
766 }
767 *buf.add(len) = 0;
768
769 if flags & FTW_DEPTH_V != 0 {
770 let mut f2 = Ftw { base: parent_base, level: level };
771 let r = walk_call(cb, path, &st, type_, &mut f2);
772 if r != 0 { return r; }
773 }
774 let _ = &mut new;
775 0
776}
777
778#[no_mangle]
779pub unsafe extern "C" fn nftw(
780 path: *const c_char,
781 f: Option<unsafe extern "C" fn(*const c_char, *const Stat, c_int, *mut Ftw) -> c_int>,
782 fd_limit: c_int,
783 flags: c_int,
784) -> c_int {
785 if path.is_null() || f.is_none() { ERRNO!() = EINVAL; return -1; }
786 if fd_limit <= 0 { return 0; }
787 let l = crate::strlen(path);
788 if l >= PATH_MAX_V { ERRNO!() = 36; return -1; }
789 let mut buf = [0u8; PATH_MAX_V + 1];
790 core::ptr::copy_nonoverlapping(path as *const u8, buf.as_mut_ptr(), l + 1);
791 nftw_do(buf.as_mut_ptr(), buf.len(), l, WalkCb::N(f), fd_limit, flags, core::ptr::null_mut(), 0, 0)
792}
793
794#[no_mangle]
795pub unsafe extern "C" fn ftw(
796 path: *const c_char,
797 f: Option<unsafe extern "C" fn(*const c_char, *const Stat, c_int) -> c_int>,
798 fd_limit: c_int,
799) -> c_int {
800 if path.is_null() || f.is_none() { ERRNO!() = EINVAL; return -1; }
801 if fd_limit <= 0 { return 0; }
802 let l = crate::strlen(path);
803 if l >= PATH_MAX_V { ERRNO!() = 36; return -1; }
804 let mut buf = [0u8; PATH_MAX_V + 1];
805 core::ptr::copy_nonoverlapping(path as *const u8, buf.as_mut_ptr(), l + 1);
806 nftw_do(buf.as_mut_ptr(), buf.len(), l, WalkCb::F(f), fd_limit, FTW_PHYS_V, core::ptr::null_mut(), 0, 0)
807}