// Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// //~ allen: Path Helper Functions internal StringMatchFlags path_match_flags_from_os(OperatingSystem os) { StringMatchFlags flags = StringMatchFlag_SlashInsensitive; switch(os) { default:{}break; case OperatingSystem_Windows: { flags |= StringMatchFlag_CaseInsensitive; }break; case OperatingSystem_Linux: case OperatingSystem_Mac: { // NOTE(rjf): no-op }break; } return flags; } internal String8 path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src) { Temp scratch = scratch_begin(&arena, 1); // rjf: gather path parts String8 dst_name = str8_skip_last_slash(dst); String8 src_folder = str8_chop_last_slash(src); String8 dst_folder = str8_chop_last_slash(dst); String8List src_folders = str8_split_path(scratch.arena, src_folder); String8List dst_folders = str8_split_path(scratch.arena, dst_folder); // rjf: count # of backtracks to get from src -> dest U64 num_backtracks = src_folders.node_count; for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first; src_n != 0 && bp_n != 0; src_n = src_n->next, bp_n = bp_n->next) { if(str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context()))) { num_backtracks -= 1; } else { break; } } // rjf: only build relative string if # of backtracks is not the entire `src`. // if getting to `dst` from `src` requires erasing the entire `src`, then the // only possible way to get to `dst` from `src` is via absolute path. String8 dst_path = {0}; if(num_backtracks >= src_folders.node_count) { dst_path = path_normalized_from_string(arena, dst); } else { // rjf: build backtrack parts String8List dst_path_strs = {0}; for(U64 idx = 0; idx < num_backtracks; idx += 1) { str8_list_push(scratch.arena, &dst_path_strs, str8_lit("..")); } // rjf: build parts of dst which are unique from src { B32 unique_from_src = 0; for(String8Node *src_n = src_folders.first, *bp_n = dst_folders.first; bp_n != 0; bp_n = bp_n->next) { if(!unique_from_src && (src_n == 0 || !str8_match(src_n->string, bp_n->string, path_match_flags_from_os(operating_system_from_context())))) { unique_from_src = 1; } if(unique_from_src) { str8_list_push(scratch.arena, &dst_path_strs, bp_n->string); } if(src_n != 0) { src_n = src_n->next; } } } // rjf: build file name str8_list_push(scratch.arena, &dst_path_strs, dst_name); // rjf: join StringJoin join = {0}; { join.sep = str8_lit("/"); } dst_path = str8_list_join(arena, &dst_path_strs, &join); } scratch_end(scratch); return dst_path; } internal String8 path_absolute_dst_from_relative_dst_src(Arena *arena, String8 dst, String8 src) { String8 result = dst; PathStyle dst_style = path_style_from_str8(dst); if(dst_style == PathStyle_Relative) { Temp scratch = scratch_begin(&arena, 1); String8 dst_from_src_absolute = push_str8f(scratch.arena, "%S/%S", src, dst); String8 dst_from_src_absolute_normalized = path_normalized_from_string(arena, dst_from_src_absolute); result = dst_from_src_absolute_normalized; scratch_end(scratch); } return result; } internal String8List path_normalized_list_from_string(Arena *arena, String8 path_string, PathStyle *style_out){ // analyze path PathStyle path_style = path_style_from_str8(path_string); String8List path = str8_split_path(arena, path_string); // prepend current path to convert relative -> absolute PathStyle path_style_full = path_style; if (path.node_count != 0 && path_style == PathStyle_Relative){ String8 current_path_string = os_get_current_path(arena); PathStyle current_path_style = path_style_from_str8(current_path_string); Assert(current_path_style != PathStyle_Relative); String8List current_path = str8_split_path(arena, current_path_string); str8_list_concat_in_place(¤t_path, &path); path = current_path; path_style_full = current_path_style; } // resolve dots str8_path_list_resolve_dots_in_place(&path, path_style_full); // return if (style_out != 0){ *style_out = path_style_full; } return(path); } internal String8 path_normalized_from_string(Arena *arena, String8 path_string){ Temp scratch = scratch_begin(&arena, 1); PathStyle style = PathStyle_Relative; String8List path = path_normalized_list_from_string(scratch.arena, path_string, &style); String8 result = str8_path_list_join_by_style(arena, &path, style); scratch_end(scratch); return(result); } internal B32 path_match_normalized(String8 left, String8 right) { B32 result = 0; { Temp scratch = scratch_begin(0, 0); String8 left_normalized = path_normalized_from_string(scratch.arena, left); String8 right_normalized = path_normalized_from_string(scratch.arena, right); result = str8_match(left_normalized, right_normalized, StringMatchFlag_CaseInsensitive); scratch_end(scratch); } return result; }