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