walk.odin 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #+build !wasi
  2. #+build !js
  3. package filepath
  4. import "core:os"
  5. import "core:slice"
  6. // Walk_Proc is the type of the procedure called for each file or directory visited by 'walk'
  7. // The 'path' parameter contains the parameter to walk as a prefix (this is the same as info.fullpath except on 'root')
  8. // The 'info' parameter is the os.File_Info for the named path
  9. //
  10. // If there was a problem walking to the file or directory named by path, the incoming error will describe the problem
  11. // and the procedure can decide how to handle that error (and walk will not descend into that directory)
  12. // In the case of an error, the info argument will be 0
  13. // If an error is returned, processing stops
  14. // The sole exception is if 'skip_dir' is returned as true:
  15. // when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
  16. // when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
  17. Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Error, user_data: rawptr) -> (err: os.Error, skip_dir: bool)
  18. // walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
  19. // All errors that happen visiting files and directories are filtered by walk_proc
  20. // The files are walked in lexical order to make the output deterministic
  21. // NOTE: Walking large directories can be inefficient due to the lexical sort
  22. // NOTE: walk does not follow symbolic links
  23. // NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
  24. walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Error {
  25. info, err := os.lstat(root, context.temp_allocator)
  26. defer os.file_info_delete(info, context.temp_allocator)
  27. skip_dir: bool
  28. if err != nil {
  29. err, skip_dir = walk_proc(info, err, user_data)
  30. } else {
  31. err, skip_dir = _walk(info, walk_proc, user_data)
  32. }
  33. return nil if skip_dir else err
  34. }
  35. @(private)
  36. _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Error, skip_dir: bool) {
  37. if !info.is_dir {
  38. if info.fullpath == "" && info.name == "" {
  39. // ignore empty things
  40. return
  41. }
  42. return walk_proc(info, nil, user_data)
  43. }
  44. fis: []os.File_Info
  45. err1: os.Error
  46. fis, err = read_dir(info.fullpath, context.temp_allocator)
  47. defer os.file_info_slice_delete(fis, context.temp_allocator)
  48. err1, skip_dir = walk_proc(info, err, user_data)
  49. if err != nil || err1 != nil || skip_dir {
  50. err = err1
  51. return
  52. }
  53. for fi in fis {
  54. err, skip_dir = _walk(fi, walk_proc, user_data)
  55. if err != nil || skip_dir {
  56. if !fi.is_dir || !skip_dir {
  57. return
  58. }
  59. }
  60. }
  61. return
  62. }
  63. @(private)
  64. read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
  65. f := os.open(dir_name, os.O_RDONLY) or_return
  66. defer os.close(f)
  67. fis = os.read_dir(f, -1, allocator) or_return
  68. slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
  69. return a.name < b.name
  70. })
  71. return
  72. }