From fa09b647629497867b44a01b30e6f358d40fa0fb Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Sun, 11 Jan 2026 20:47:31 -0600 Subject: [PATCH] Bugfix: Static builds with '../' relative paths for objects do not build correctly. Add new function that handles converting the relative path with '../' with one that has no '../'. Use this to build the object paths by adding the full relative directory path for the build files. Note that this could be a problem with possible directory structure conflicts in some situations. Utilize a new cache `cache_relative` for the processing of the relative path. Add additional debug reporting to improve the error reporting details with the programs, like `ar`, fail. Add note how future FLL versions (0.9 and later) should have something like `fl_path_canonical()`. I need to also follow this commit up with an investigation to see if any of these `../` can get outside the project root. There is a need for a project root parameter for situations where different paths are specified, such as specifying `--sources`. Alternatively, I might have to restrict all `../` to the sources directory and then move all of my `tests/` directory files within the `sources/` directoreis for all projects. This also gives me reason to consider adding IKI support to enhance the build settings. I am on the fence on actually doing this or not. --- level_3/fake/c/main/build/library.c | 126 +++++++++++- level_3/fake/c/main/common.c | 189 ++++++++++++++++++ level_3/fake/c/main/common.h | 39 ++++ level_3/fake/c/main/common/type.c | 1 + level_3/fake/c/main/common/type.h | 3 + level_3/fake/c/main/make/operate_process.c | 6 +- level_3/fake/c/main/make/operate_process.h | 6 +- .../fake/c/main/make/operate_process_type.c | 10 +- level_3/fake/c/main/make/print/error.c | 13 +- level_3/fake/c/main/make/print/error.h | 6 +- 10 files changed, 376 insertions(+), 23 deletions(-) diff --git a/level_3/fake/c/main/build/library.c b/level_3/fake/c/main/build/library.c index 3ef6720eb..b857e2e8e 100644 --- a/level_3/fake/c/main/build/library.c +++ b/level_3/fake/c/main/build/library.c @@ -520,10 +520,16 @@ extern "C" { for (j = 0; j < sources[i]->used; ++j) { + if (!sources[i]->array[j].used) continue; + fake_string_dynamic_reset(&main->cache_1); fake_string_dynamic_reset(&main->cache_2); + fake_string_dynamic_reset(&main->cache_argument); + + fake_build_path_source_string(data, data_build, &data_build->setting.path_sources_library, &main->cache_2); + if (F_status_is_error(main->setting.state.status)) return 0; - fake_build_get_file_name_without_extension(data, sources[i]->array[j], &main->cache_1); + main->setting.state.status = f_file_name_directory(sources[i]->array[j], &main->cache_argument); if (F_status_is_error(main->setting.state.status)) { fake_print_error(&main->program.error, F_status_debug_source_d); @@ -531,12 +537,51 @@ extern "C" { return 0; } - main->setting.state.status = f_file_name_directory(sources[i]->array[j], &main->cache_2); + fake_path_relative_within(data, main->cache_2, main->cache_argument, &main->cache_1); - if (F_status_is_error(main->setting.state.status)) { - fake_print_error(&main->program.error, F_status_debug_source_d); + if (main->setting.state.status == F_relative_not) { + fake_string_dynamic_reset(&main->cache_1); + fake_string_dynamic_reset(&main->cache_2); + + fake_build_get_file_name_without_extension(data, sources[i]->array[j], &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + + main->setting.state.status = f_file_name_directory(sources[i]->array[j], &main->cache_2); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + return 0; + } + } + else if (F_status_is_error(main->setting.state.status)) { return 0; + } else { + fake_string_dynamic_reset(&main->cache_2); + + main->setting.state.status = f_file_name_directory(main->cache_1, &main->cache_2); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + + main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + + fake_build_get_file_name_without_extension(data, sources[i]->array[j], &main->cache_1); + if (F_status_is_error(main->setting.state.status)) return 0; } fake_string_dynamic_reset(&main->cache_argument); @@ -673,10 +718,10 @@ extern "C" { fake_string_dynamic_reset(&main->cache_argument); fake_string_dynamics_reset_to(&main->cache_arguments, minimum); - fake_build_path_source_string(data, data_build, &data_build->setting.path_sources_library, &main->cache_1); + fake_build_path_source_string(data, data_build, &data_build->setting.path_sources_library, &main->cache_2); if (F_status_is_error(main->setting.state.status)) return 0; - main->setting.state.status = f_string_dynamic_append_nulless(sources[i]->array[j], &main->cache_1); + main->setting.state.status = f_file_name_directory(sources[i]->array[j], &main->cache_argument); if (F_status_is_error(main->setting.state.status)) { fake_print_error(&main->program.error, F_status_debug_source_d); @@ -684,15 +729,60 @@ extern "C" { return 0; } - fake_build_get_file_name_without_extension(data, sources[i]->array[j], &main->cache_2); + fake_path_relative_within(data, main->cache_2, main->cache_argument, &main->cache_1); - if (F_status_is_error(main->setting.state.status)) { - fake_print_error(&main->program.error, F_status_debug_source_d); + if (main->setting.state.status == F_relative_not) { + fake_string_dynamic_reset(&main->cache_1); + + main->setting.state.status = f_string_dynamic_append_nulless(main->cache_2, &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + main->setting.state.status = f_string_dynamic_append_nulless(sources[i]->array[j], &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + } + else if (F_status_is_error(main->setting.state.status)) { return 0; + } else { + fake_string_dynamic_reset(&main->cache_argument); + + main->setting.state.status = f_file_name_directory(main->cache_1, &main->cache_argument); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + + main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + + main->setting.state.status = f_file_name_base(sources[i]->array[j], &main->cache_1); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } } - main->setting.state.status = f_file_name_directory(sources[i]->array[j], &main->cache_argument); + fake_string_dynamic_reset(&main->cache_2); + + fake_build_get_file_name_without_extension(data, main->cache_1, &main->cache_2); if (F_status_is_error(main->setting.state.status)) { fake_print_error(&main->program.error, F_status_debug_source_d); @@ -701,6 +791,14 @@ extern "C" { } if (main->cache_argument.used) { + main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &main->cache_argument); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + main->setting.state.status = f_string_dynamic_prepend(data->path_build_objects_static, &main->cache_argument); if (F_status_is_error(main->setting.state.status)) { @@ -717,6 +815,14 @@ extern "C" { return 0; } + main->setting.state.status = f_string_dynamic_terminate_after(&main->cache_argument); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return 0; + } + main->setting.state.status = f_directory_exists(main->cache_argument); if (main->setting.state.status == F_false) { diff --git a/level_3/fake/c/main/common.c b/level_3/fake/c/main/common.c index 0fe00c097..0b265af7c 100644 --- a/level_3/fake/c/main/common.c +++ b/level_3/fake/c/main/common.c @@ -4,6 +4,195 @@ extern "C" { #endif +#ifndef _di_fake_path_relative_within_ + void fake_path_relative_within(fake_data_t * const data, const f_string_static_t within, const f_string_static_t path, f_string_dynamic_t * const relative) { + + if (!data || !data->main || !relative) return; + + fake_main_t * const main = data->main; + + f_number_unsigned_t at = 0; + + relative->used = 0; + + while (at < path.used && !path.string[at]) ++at; + + if (path.string[at] == f_path_separator_s.string[0]) { + main->setting.state.status = F_status_set_error(F_absolute); + + return; + } + + if (path.string[at] != f_path_separator_current_s.string[0]) { + main->setting.state.status = F_relative_not; + + return; + } + + { + f_number_unsigned_t original = at++; + + while (at < path.used && !path.string[at]) ++at; + + if (path.string[at] != f_path_separator_current_s.string[0]) { + main->setting.state.status = F_relative_not; + + return; + } + + ++at; + + while (at < path.used && !path.string[at]) ++at; + + if (path.string[at] != f_path_separator_s.string[0]) { + main->setting.state.status = F_relative_not; + + return; + } + + at = original; + } + + fake_string_dynamic_reset(&main->cache_relative); + fake_string_dynamic_reset(relative); + + main->setting.state.status = f_string_dynamic_append_nulless(within, &main->cache_relative); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return; + } + + main->setting.state.status = f_string_dynamic_append_assure(f_path_separator_s, &main->cache_relative); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return; + } + + uint8_t previous_1 = f_path_separator_s.string[0]; + uint8_t previous_2 = 0; + + f_number_unsigned_t size_chunk = 0; + f_number_unsigned_t position = 0; + + main->setting.state.status = f_string_dynamic_append(main->cache_relative, relative); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return; + } + + for (at = 0; at < path.used; ++at) { + + if (!size_chunk && path.string[at] == f_path_separator_current_s.string[0]) { + if (!previous_1 || previous_1 == f_path_separator_s.string[0]) { + previous_1 = f_path_separator_current_s.string[0]; + previous_2 = 0; + + continue; + } + + if (previous_1 == f_path_separator_current_s.string[0]) { + if (previous_2) { + previous_1 = 0; + previous_2 = 0; + size_chunk = 3; + position = at - 2; + } + else { + previous_2 = f_path_separator_current_s.string[0]; + } + } + } + else if (path.string[at] == f_path_separator_s.string[0]) { + if (previous_1 == f_path_separator_s.string[0]) { + size_chunk = 0; + position = 0; + + continue; + } + + if (previous_1 == f_path_separator_current_s.string[0]) { + if (previous_2 == f_path_separator_current_s.string[0]) { + if (relative->used > 1) { + for (--relative->used; relative->used > 0; --relative->used) { + if (relative->string[relative->used - 1] == f_path_separator_s.string[0]) break; + } // for + } + } + } + else { + if (++size_chunk) { + main->setting.state.status = f_string_append_nulless(path.string + position, size_chunk, relative); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return; + } + } + } + + previous_1 = f_path_separator_s.string[0]; + previous_2 = 0; + size_chunk = 0; + position = 0; + } + else { + if (!size_chunk) { + position = at; + + if (previous_2) { + position -= 2; + size_chunk = 2; + } + else if (previous_1 && previous_1 != f_path_separator_s.string[0]) { + --position; + size_chunk = 1; + } + } + + if (previous_1) { + previous_1 = 0; + previous_2 = 0; + } + + ++size_chunk; + } + } // for + + if (previous_2 == f_path_separator_current_s.string[0]) { + if (relative->used > 1) { + for (--relative->used; relative->used > 0; --relative->used) { + if (relative->string[relative->used - 1] == f_path_separator_s.string[0]) break; + } // for + } + } + else if (!(previous_1 == f_path_separator_current_s.string[0] || previous_1 == f_path_separator_s.string[0])) { + if (size_chunk) { + main->setting.state.status = f_string_append_nulless(path.string + position, size_chunk, relative); + + if (F_status_is_error(main->setting.state.status)) { + fake_print_error(&main->program.error, F_status_debug_source_d); + + return; + } + } + } + + // Assure there is no trailing forward slash, unless it is the first slash. + if (relative->used > 1 && relative->string[relative->used - 1] == f_path_separator_s.string[0]) { + --relative->used; + } + + main->setting.state.status = F_okay; + } +#endif // _di_fake_path_relative_within_ + #ifndef _di_fake_setting_load_ void fake_setting_load(const f_console_arguments_t arguments, fake_main_t * const main) { diff --git a/level_3/fake/c/main/common.h b/level_3/fake/c/main/common.h index 53617bfd7..09261feb2 100644 --- a/level_3/fake/c/main/common.h +++ b/level_3/fake/c/main/common.h @@ -16,6 +16,45 @@ extern "C" { #endif +/** + * For paths that are prefixed with '../', convert them into a path within a given directory. + * + * Relative paths are assumed to be relative to the "within" canonical path, such as: 'sources/c' rather than either '../../sources/c' or '/tmp/project/sources/c' where '/tmp/project/' is the "within" canonical path. + * + * If the '../' path traverses outside of the "within" path, then this is considered an error. + * + * @todo Some of this behavior needs to be into fl_path project as fl_path_canonical_relative() for 0.9 and greater (cannot be done in 0.8 due to API freeze). + * + * @param data + * The program data. + * + * This alters data.main.cache_relative. + * + * This alters data.main.setting.state.status: + * F_okay on success. + * F_relative_not on success and the path is not prefixed with '../'. + * + * F_absolute (with error bit) If path is an absolute path. + * F_too_small (with error bit) If the resulting relative path would end up outside the "within" directory path. + * + * Errors (with error bit) from: fl_path_canonical(). + * @param within + * This must already be a canonical path. + * The directory in which non-absolute paths are relative to. + * This should not be "/" because then the resulting relative path would just be a normal full path (not relative). + * This need not be NULL terminated. + * @param path + * The source path to determine what the relative canonical file path is. + * NULLs within the path are not copied to the relative canonical file path. + * This need not be NULL terminated. + * @param relative + * The (allocated) relative canonical file path. + * This will be NULL terminated at relative.used + 1. + */ +#ifndef _di_fake_path_relative_within_ + extern void fake_path_relative_within(fake_data_t * const data, const f_string_static_t within, const f_string_static_t path, f_string_dynamic_t * const relative); +#endif // _di_fake_path_relative_within_ + /** * Perform the standard program setting load process. * diff --git a/level_3/fake/c/main/common/type.c b/level_3/fake/c/main/common/type.c index 32b705e27..b32ebf2bd 100644 --- a/level_3/fake/c/main/common/type.c +++ b/level_3/fake/c/main/common/type.c @@ -228,6 +228,7 @@ extern "C" { f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache_1.string, &main->cache_1.used, &main->cache_1.size); f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache_2.string, &main->cache_2.used, &main->cache_2.size); f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache_argument.string, &main->cache_argument.used, &main->cache_argument.size); + f_memory_array_resize(0, sizeof(f_char_t), (void **) &main->cache_relative.string, &main->cache_relative.used, &main->cache_relative.size); f_memory_arrays_resize(0, sizeof(f_string_dynamic_t), (void **) &main->cache_arguments.array, &main->cache_arguments.used, &main->cache_arguments.size, &f_string_dynamics_delete_callback); diff --git a/level_3/fake/c/main/common/type.h b/level_3/fake/c/main/common/type.h index 6968e17ba..4a7417315 100644 --- a/level_3/fake/c/main/common/type.h +++ b/level_3/fake/c/main/common/type.h @@ -138,6 +138,7 @@ extern "C" { * - cache_2: A string cache. * - cache_argument: A string cache for some argument. * - cache_argument: A string cache for some path. + * - cache_relative: A string cache for building relative canonical paths. * - cache_arguments: An array of strings cache for arguments. * - cache_map: A string map cache. * - cache_iki: IKI data cache. @@ -153,6 +154,7 @@ extern "C" { f_string_dynamic_t cache_1; f_string_dynamic_t cache_2; f_string_dynamic_t cache_argument; + f_string_dynamic_t cache_relative; f_string_dynamics_t cache_arguments; f_string_map_t cache_map; f_iki_data_t cache_iki; @@ -166,6 +168,7 @@ extern "C" { .cache_1 = f_string_dynamic_t_initialize, \ .cache_2 = f_string_dynamic_t_initialize, \ .cache_argument = f_string_dynamic_t_initialize, \ + .cache_relative = f_string_dynamic_t_initialize, \ .cache_arguments = f_string_dynamics_t_initialize, \ .cache_map = f_string_map_t_initialize, \ .cache_iki = f_iki_data_t_initialize, \ diff --git a/level_3/fake/c/main/make/operate_process.c b/level_3/fake/c/main/make/operate_process.c index a1cb9bca8..1d95e95a7 100644 --- a/level_3/fake/c/main/make/operate_process.c +++ b/level_3/fake/c/main/make/operate_process.c @@ -346,14 +346,14 @@ extern "C" { } } - fake_make_operate_process_return(data_make, return_code); + fake_make_operate_process_return(data_make, return_code, F_status_debug_source_d); return; } #endif // _di_fake_make_operate_process_execute_ #ifndef _di_fake_make_operate_process_return_ - void fake_make_operate_process_return(fake_make_data_t * const data_make, const int return_code) { + void fake_make_operate_process_return(fake_make_data_t * const data_make, const int return_code, const f_string_t debug) { if (!data_make || !data_make->main) return; @@ -386,7 +386,7 @@ extern "C" { return; } - fake_make_print_error_program_failed(&main->program.error, return_code); + fake_make_print_error_program_failed(&main->program.error, return_code, debug); main->setting.state.status = (data_make->setting_make.fail == fake_make_operation_fail_exit_e) ? F_status_set_error(F_failure) : F_failure; diff --git a/level_3/fake/c/main/make/operate_process.h b/level_3/fake/c/main/make/operate_process.h index b797d8ee1..a7406bac1 100644 --- a/level_3/fake/c/main/make/operate_process.h +++ b/level_3/fake/c/main/make/operate_process.h @@ -209,13 +209,17 @@ extern "C" { * Errors (with error bit) from: f_string_dynamic_append(). * @param return_code * The return code to process. + * @param debug + * (optional) The debug details, such as file, line number, and function. + * + * Set to NULL to disable. * * @see f_conversion_number_signed_to_string() * @see f_string_append() * @see f_string_dynamic_append() */ #ifndef _di_fake_make_operate_process_return_ - extern void fake_make_operate_process_return(fake_make_data_t * const data_make, const int return_code); + extern void fake_make_operate_process_return(fake_make_data_t * const data_make, const int return_code, const f_string_t debug); #endif // _di_fake_make_operate_process_return_ /** diff --git a/level_3/fake/c/main/make/operate_process_type.c b/level_3/fake/c/main/make/operate_process_type.c index 79f7106de..650c97b77 100644 --- a/level_3/fake/c/main/make/operate_process_type.c +++ b/level_3/fake/c/main/make/operate_process_type.c @@ -39,7 +39,7 @@ extern "C" { fake_build_operate(data_make->data, main->cache_arguments.used ? &main->cache_arguments : 0, F_false, data_make->setting_make.stage); if (F_status_set_fine(main->setting.state.status) == F_interrupt) return; - fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0); + fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0, F_status_debug_source_d); } #endif // _di_fake_make_operate_process_type_build_ @@ -53,7 +53,7 @@ extern "C" { fake_clean_operate(data_make->data); if (F_status_set_fine(main->setting.state.status) == F_interrupt) return; - fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0); + fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0, F_status_debug_source_d); } #endif // _di_fake_make_operate_process_type_clean_ @@ -67,7 +67,7 @@ extern "C" { const int result = fake_execute(data_make->data, data_make->environment, data_make->setting_build.build_compiler); if (main->setting.state.status == F_child) return result; - fake_make_operate_process_return(data_make, result); + fake_make_operate_process_return(data_make, result, F_status_debug_source_d); return result; } @@ -1361,7 +1361,7 @@ extern "C" { const int result = fake_execute(data_make->data, data_make->environment, data_make->setting_build.build_indexer); if (main->setting.state.status == F_child) return result; - fake_make_operate_process_return(data_make, result); + fake_make_operate_process_return(data_make, result, F_status_debug_source_d); return result; } @@ -1893,7 +1893,7 @@ extern "C" { fake_skeleton_operate(data_make->data); if (F_status_set_fine(main->setting.state.status) == F_interrupt) return; - fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0); + fake_make_operate_process_return(data_make, F_status_is_error(main->setting.state.status) ? 1 : 0, F_status_debug_source_d); } #endif // _di_fake_make_operate_process_type_skeleton_ diff --git a/level_3/fake/c/main/make/print/error.c b/level_3/fake/c/main/make/print/error.c index 688b9aa54..e53b102df 100644 --- a/level_3/fake/c/main/make/print/error.c +++ b/level_3/fake/c/main/make/print/error.c @@ -282,7 +282,7 @@ extern "C" { #endif // _di_fake_make_print_error_pop_last_path_ #ifndef _di_fake_make_print_error_program_failed_ - f_status_t fake_make_print_error_program_failed(fl_print_t * const print, const int return_code) { + f_status_t fake_make_print_error_program_failed(fl_print_t * const print, const int return_code, const f_string_t debug) { if (!print) return F_status_set_error(F_output_not); if (print->verbosity < f_console_verbosity_error_e) return F_output_not; @@ -290,8 +290,15 @@ extern "C" { f_file_stream_lock(print->to); fl_print_format("%[%QFailed with return code %]", print->to, print->context, print->prefix, print->context); - fl_print_format("%[%i%]", print->to, print->notable, return_code, print->notable); - fl_print_format("%[.%]%r", print->to, print->context, print->context, f_string_eol_s); + fl_print_format("%[%i%]%[", print->to, print->notable, return_code, print->notable, print->context, print->context); + + if (debug && *debug) { + fl_print_format(macro_fll_error_s(019_debug_open), print->to, print->context); + fl_print_format(f_string_format_S_single_s.string, print->to, print->notable, debug, print->notable); + fl_print_format(macro_fll_error_s(020_debug_close), print->to, print->context); + } + + fl_print_format(".%]%r", print->to, print->context, f_string_eol_s); f_file_stream_unlock(print->to); diff --git a/level_3/fake/c/main/make/print/error.h b/level_3/fake/c/main/make/print/error.h index f82bccd4a..af5860e2f 100644 --- a/level_3/fake/c/main/make/print/error.h +++ b/level_3/fake/c/main/make/print/error.h @@ -425,6 +425,10 @@ extern "C" { * This does not alter print.custom.setting.state.status. * @param return_code * The return code from the program. + * @param debug + * (optional) The debug details, such as file, line number, and function. + * + * Set to NULL to disable. * * @return * F_okay on success. @@ -433,7 +437,7 @@ extern "C" { * F_output_not (with error bit) if setting is NULL. */ #ifndef _di_fake_make_print_error_program_failed_ - extern f_status_t fake_make_print_error_program_failed(fl_print_t * const print, const int return_code); + extern f_status_t fake_make_print_error_program_failed(fl_print_t * const print, const int return_code, const f_string_t debug); #endif // _di_fake_make_print_error_program_failed_ /** -- 2.47.3