package filepath import "core:strings" import "core:os" import win32 "core:sys/windows" SEPARATOR :: '\\' SEPARATOR_STRING :: `\` LIST_SEPARATOR :: ';' @(private) reserved_names := [?]string{ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", } is_reserved_name :: proc(path: string) -> bool { if len(path) == 0 { return false } for reserved in reserved_names { if strings.equal_fold(path, reserved) { return true } } return false } is_UNC :: proc(path: string) -> bool { return volume_name_len(path) > 2 } is_abs :: proc(path: string) -> bool { if is_reserved_name(path) { return true } l := volume_name_len(path) if l == 0 { return false } path := path path = path[l:] if path == "" { return false } return is_slash(path[0]) } @(private) temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) { ta := context.temp_allocator name := name if name == "" { name = "." } p := win32.utf8_to_utf16(name, ta) buf := make([dynamic]u16, 100, ta) for { n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) if n == 0 { delete(buf) return "", os.Errno(win32.GetLastError()) } if n <= u32(len(buf)) { return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE } resize(&buf, len(buf)*2) } return } abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { full_path, err := temp_full_path(path) if err != 0 { return "", false } p := clean(full_path, allocator) return p, true } join :: proc(elems: []string, allocator := context.allocator) -> string { for e, i in elems { if e != "" { return join_non_empty(elems[i:], allocator) } } return "" } join_non_empty :: proc(elems: []string, allocator := context.allocator) -> string { context.allocator = allocator if len(elems[0]) == 2 && elems[0][1] == ':' { i := 1 for ; i < len(elems); i += 1 { if elems[i] != "" { break } } s := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) s = strings.concatenate({elems[0], s}, context.temp_allocator) return clean(s) } p := clean(strings.join(elems, SEPARATOR_STRING, context.temp_allocator)) if !is_UNC(p) { return p } head := clean(elems[0], context.temp_allocator) if is_UNC(head) { return p } delete(p) // It is not needed now tail := clean(strings.join(elems[1:], SEPARATOR_STRING, context.temp_allocator), context.temp_allocator) if head[len(head)-1] == SEPARATOR { return strings.concatenate({head, tail}) } return strings.concatenate({head, SEPARATOR_STRING, tail}) }