mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-02 03:02:37 +00:00
180 lines
3.4 KiB
Odin
180 lines
3.4 KiB
Odin
package filepath
|
|
|
|
import "core:strings"
|
|
import "core:os"
|
|
import win32 "core:sys/windows"
|
|
|
|
SEPARATOR :: '\\';
|
|
SEPARATOR_STRING :: `\`;
|
|
LIST_SEPARATOR :: ';';
|
|
|
|
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[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), 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;
|
|
}
|
|
|
|
split_list :: proc(path: string, allocator := context.allocator) -> []string {
|
|
if path == "" {
|
|
return nil;
|
|
}
|
|
|
|
start: int;
|
|
quote: bool;
|
|
|
|
start, quote = 0, false;
|
|
count := 0;
|
|
|
|
for i := 0; i < len(path); i += 1 {
|
|
c := path[i];
|
|
switch {
|
|
case c == '"':
|
|
quote = !quote;
|
|
case c == LIST_SEPARATOR && !quote:
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
start, quote = 0, false;
|
|
list := make([]string, count, allocator);
|
|
index := 0;
|
|
for i := 0; i < len(path); i += 1 {
|
|
c := path[i];
|
|
switch {
|
|
case c == '"':
|
|
quote = !quote;
|
|
case c == LIST_SEPARATOR && !quote:
|
|
list[index] = path[start:i];
|
|
index += 1;
|
|
start = i + 1;
|
|
}
|
|
}
|
|
assert(index == count);
|
|
|
|
for s, i in list {
|
|
s, new := strings.replace_all(s, `"`, ``, allocator);
|
|
if !new {
|
|
s = strings.clone(s, allocator);
|
|
}
|
|
list[i] = s;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
|
|
|
|
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
|
for e, i in elems {
|
|
if e != "" {
|
|
return join_non_empty(elems[i:]);
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
join_non_empty :: proc(elems: []string) -> string {
|
|
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);
|
|
}
|
|
|
|
s := strings.join(elems, SEPARATOR_STRING, context.temp_allocator);
|
|
p := clean(s);
|
|
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});
|
|
}
|