path_windows.odin 2.9 KB

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