path_windows.odin 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. package filepath
  2. import "core:strings"
  3. import "core:os"
  4. import win32 "core:sys/windows"
  5. SEPARATOR :: '\\';
  6. SEPARATOR_STRING :: `\`;
  7. LIST_SEPARATOR :: ';';
  8. @(private)
  9. reserved_names := [?]string{
  10. "CON", "PRN", "AUX", "NUL",
  11. "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
  12. "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
  13. };
  14. is_reserved_name :: proc(path: string) -> bool {
  15. if len(path) == 0 {
  16. return false;
  17. }
  18. for reserved in reserved_names {
  19. if strings.equal_fold(path, reserved) {
  20. return true;
  21. }
  22. }
  23. return false;
  24. }
  25. is_UNC :: proc(path: string) -> bool {
  26. return volume_name_len(path) > 2;
  27. }
  28. is_abs :: proc(path: string) -> bool {
  29. if is_reserved_name(path) {
  30. return true;
  31. }
  32. l := volume_name_len(path);
  33. if l == 0 {
  34. return false;
  35. }
  36. path := path;
  37. path = path[l:];
  38. if path == "" {
  39. return false;
  40. }
  41. return is_slash(path[0]);
  42. }
  43. @(private)
  44. temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
  45. ta := context.temp_allocator;
  46. name := name;
  47. if name == "" {
  48. name = ".";
  49. }
  50. p := win32.utf8_to_utf16(name, ta);
  51. buf := make([dynamic]u16, 100, ta);
  52. for {
  53. n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil);
  54. if n == 0 {
  55. delete(buf);
  56. return "", os.Errno(win32.GetLastError());
  57. }
  58. if n <= u32(len(buf)) {
  59. return win32.utf16_to_utf8(buf[:n], ta), os.ERROR_NONE;
  60. }
  61. resize(&buf, len(buf)*2);
  62. }
  63. return;
  64. }
  65. abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
  66. full_path, err := temp_full_path(path);
  67. if err != 0 {
  68. return "", false;
  69. }
  70. p := clean(full_path, allocator);
  71. return p, true;
  72. }
  73. join :: proc(elems: ..string, allocator := context.allocator) -> string {
  74. for e, i in elems {
  75. if e != "" {
  76. return join_non_empty(elems[i:]);
  77. }
  78. }
  79. return "";
  80. }
  81. join_non_empty :: proc(elems: []string) -> string {
  82. if len(elems[0]) == 2 && elems[0][1] == ':' {
  83. i := 1;
  84. for ; i < len(elems); i += 1 {
  85. if elems[i] != "" {
  86. break;
  87. }
  88. }
  89. s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator);
  90. s = strings.concatenate({elems[0], s}, context.temp_allocator);
  91. return clean(s);
  92. }
  93. s := strings.join(elems, SEPARATOR_STRING, context.temp_allocator);
  94. p := clean(s);
  95. if !is_UNC(p) {
  96. return p;
  97. }
  98. head := clean(elems[0], context.temp_allocator);
  99. if is_UNC(head) {
  100. return p;
  101. }
  102. delete(p); // It is not needed now
  103. tail := clean(strings.join(elems[1:], SEPARATOR_STRING, context.temp_allocator), context.temp_allocator);
  104. if head[len(head)-1] == SEPARATOR {
  105. return strings.concatenate({head, tail});
  106. }
  107. return strings.concatenate({head, SEPARATOR_STRING, tail});
  108. }