path_windows.odin 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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:], allocator)
  77. }
  78. }
  79. return ""
  80. }
  81. join_non_empty :: proc(elems: []string, allocator := context.allocator) -> string {
  82. context.allocator = allocator
  83. if len(elems[0]) == 2 && elems[0][1] == ':' {
  84. i := 1
  85. for ; i < len(elems); i += 1 {
  86. if elems[i] != "" {
  87. break
  88. }
  89. }
  90. s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator)
  91. s = strings.concatenate({elems[0], s}, context.temp_allocator)
  92. return clean(s)
  93. }
  94. p := clean(strings.join(elems, SEPARATOR_STRING, context.temp_allocator))
  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. }