From 7ef7892b419f96a375a6a14a34bbdc2c3d97045b Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Mon, 17 Jan 2022 11:44:14 -0600 Subject: [PATCH] Bugfix: The f_console project after writing unit tests. Rename has_values to values_total to better communicate the intent of the property. Expand out the macros across lines. In f_console_identify() the strnlen() function is not properly handling when the character pointer is NULL, resulting in a segfault. While this might be considered a bug in strnlen(), just make sure that a NULL pointer is not sent to strnlen(). Miscellaneous code structure cleanups. Replace allocation macros with actual functions. --- level_0/f_console/c/console-common.h | 51 ++++++- level_0/f_console/c/console.c | 272 ++++++++++++++++++----------------- level_0/f_console/c/console.h | 90 ++++++------ 3 files changed, 228 insertions(+), 185 deletions(-) diff --git a/level_0/f_console/c/console-common.h b/level_0/f_console/c/console-common.h index 6b4e8c6..949bad4 100644 --- a/level_0/f_console/c/console-common.h +++ b/level_0/f_console/c/console-common.h @@ -251,7 +251,7 @@ extern "C" { * symbol_short: The single character string, such as 'h' in '-h'. * symbol_long: The multi-character string, such as 'help' in '--help'. * symbol_other: The special meaning parameter, such as 'all' in 'make all'. - * has_values: Designates that a parameter will have a given number of values arguments, such as 'blue' in '--color blue'. + * values_total: Designates that a parameter will have a given number of values arguments, such as 'blue' in '--color blue'. * type: One of the f_console_type_* codes, defining how this parameter is to be processed. * result: A code representing that the parameter is found and how it is found ('-h' vs '--help'). * location: The last location in argv[] where this parameter is found. @@ -266,7 +266,7 @@ extern "C" { const char *symbol_long; const char *symbol_other; - const uint8_t has_values; + const uint8_t values_total; const uint8_t type; f_array_length_t result; @@ -277,11 +277,48 @@ extern "C" { f_array_lengths_t values; } f_console_parameter_t; - #define f_console_parameter_t_initialize { 0, 0, 0, 0, 0, f_array_length_t_initialize, f_array_length_t_initialize, f_array_length_t_initialize, f_array_lengths_t_initialize, f_array_lengths_t_initialize, f_array_lengths_t_initialize } - - #define macro_f_console_parameter_t_initialize(symbol_short, symbol_long, symbol_other, has_values, type_value) { symbol_short, symbol_long, symbol_other, has_values, type_value, f_console_result_none_e, 0, 0, f_array_lengths_t_initialize, f_array_lengths_t_initialize, f_array_lengths_t_initialize } - - #define macro_f_console_parameter_t_initialize2(symbol_short, symbol_long, symbol_other, has_values, type_value, result, location, location_sub, locations, locations_sub, values) { symbol_short, symbol_long, symbol_other, has_values, type_value, result, total, location, location_sub, locations, locations_sub, values } + #define f_console_parameter_t_initialize { \ + 0, \ + 0, \ + 0, \ + 0, \ + 0, \ + f_array_length_t_initialize, \ + f_array_length_t_initialize, \ + f_array_length_t_initialize, \ + f_array_lengths_t_initialize, \ + f_array_lengths_t_initialize, \ + f_array_lengths_t_initialize, \ + } + + #define macro_f_console_parameter_t_initialize(symbol_short, symbol_long, symbol_other, values_total, type_value) { \ + symbol_short, \ + symbol_long, \ + symbol_other, \ + values_total, \ + type_value, \ + f_console_result_none_e, \ + 0, \ + 0, \ + f_array_lengths_t_initialize, \ + f_array_lengths_t_initialize, \ + f_array_lengths_t_initialize, \ + } + + #define macro_f_console_parameter_t_initialize2(symbol_short, symbol_long, symbol_other, values_total, type_value, result, location, location_sub, locations, locations_sub, values) { \ + symbol_short, \ + symbol_long, \ + symbol_other, \ + values_total, \ + type_value, \ + result, \ + total, \ + location, \ + location_sub, \ + locations, \ + locations_sub, \ + values \ + } #endif // _di_f_console_parameter_t_ /** diff --git a/level_0/f_console/c/console.c b/level_0/f_console/c/console.c index e9a6093..f4b82b9 100644 --- a/level_0/f_console/c/console.c +++ b/level_0/f_console/c/console.c @@ -10,6 +10,12 @@ extern "C" { if (!result) return F_status_set_error(F_parameter); #endif // _di_level_0_parameter_checking_f + if (!input) { + *result = f_console_none_e; + + return F_data_not; + } + const f_array_length_t length = strnlen(input, 3); if (!length) { @@ -48,6 +54,104 @@ extern "C" { } #endif // _di_f_console_identify_ +#ifndef _di_f_console_parameter_prioritize_left_ + f_status_t f_console_parameter_prioritize_left(const f_console_parameters_t parameters, const f_console_parameter_ids_t choices, f_console_parameter_id_t *decision) { + #ifndef _di_level_0_parameter_checking_ + if (!decision) return F_status_set_error(F_parameter); + if (!choices.id) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (!choices.used) { + return F_data_not; + } + + if (!parameters.used) { + return F_data_not; + } + + f_array_length_t location = 0; + f_array_length_t location_sub = 0; + f_console_parameter_id_t priority = 0; + + for (f_array_length_t i = 0; i < choices.used; ++i) { + + if (choices.id[i] > parameters.used) { + return F_status_set_error(F_parameter); + } + + if (parameters.parameter[choices.id[i]].result == f_console_result_found_e) { + if (!location || parameters.parameter[choices.id[i]].location < location) { + location = parameters.parameter[choices.id[i]].location; + location_sub = parameters.parameter[choices.id[i]].location_sub; + priority = choices.id[i]; + } + else if (parameters.parameter[choices.id[i]].location == location && parameters.parameter[choices.id[i]].location_sub < location_sub) { + location_sub = parameters.parameter[choices.id[i]].location_sub; + priority = choices.id[i]; + } + } + } // for + + // The first parameter location (argc = 0) is the program name, therefore if the location is 0, then no matches were found. + if (!location) { + return F_data_not; + } + + *decision = priority; + + return F_none; + } +#endif // _di_f_console_parameter_prioritize_left_ + +#ifndef _di_f_console_parameter_prioritize_right_ + f_status_t f_console_parameter_prioritize_right(const f_console_parameters_t parameters, const f_console_parameter_ids_t choices, f_console_parameter_id_t *decision) { + #ifndef _di_level_0_parameter_checking_ + if (!decision) return F_status_set_error(F_parameter); + if (!choices.id) return F_status_set_error(F_parameter); + #endif // _di_level_0_parameter_checking_ + + if (!choices.used) { + return F_data_not; + } + + if (!parameters.used) { + return F_data_not; + } + + f_array_length_t location = 0; + f_array_length_t location_sub = 0; + f_console_parameter_id_t priority = 0; + + for (f_array_length_t i = 0; i < choices.used; ++i) { + + if (choices.id[i] > parameters.used) { + return F_status_set_error(F_parameter); + } + + if (parameters.parameter[choices.id[i]].result == f_console_result_found_e) { + if (!location || parameters.parameter[choices.id[i]].location > location) { + location = parameters.parameter[choices.id[i]].location; + location_sub = parameters.parameter[choices.id[i]].location_sub; + priority = choices.id[i]; + } + else if (parameters.parameter[choices.id[i]].location == location && parameters.parameter[choices.id[i]].location_sub > location_sub) { + location_sub = parameters.parameter[choices.id[i]].location_sub; + priority = choices.id[i]; + } + } + } // for + + // The first parameter location (argc = 0) is the program name, therefore if the location is 0, then no matches were found. + if (!location) { + return F_data_not; + } + + *decision = priority; + + return F_none; + } +#endif // _di_f_console_parameter_prioritize_right_ + #ifndef _di_f_console_parameter_process_ f_status_t f_console_parameter_process(const f_console_arguments_t arguments, f_console_parameters_t parameters, f_array_lengths_t *remaining) { #ifndef _di_level_0_parameter_checking_ @@ -58,7 +162,8 @@ extern "C" { f_console_id_t result = 0; bool found = F_false; - unsigned long location = 1; // Parameter 0 represents the program name so skip it. + // Parameter 0 represents the program name so skip it. + unsigned long location = 1; f_array_length_t sub_location = 0; f_array_length_t increment_by = 0; @@ -75,17 +180,16 @@ extern "C" { uint8_t width = 0; - // loop through and read all parameters. while (location < arguments.argc) { // Additional parameters must always follow what requests them. if (needs_value.used > 0) { i = needs_value.array[0]; - macro_f_array_lengths_t_increase(status, F_memory_default_allocation_small_d, parameters.parameter[i].values) + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, ¶meters.parameter[i].values); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } @@ -101,6 +205,7 @@ extern "C" { } // for ++location; + continue; } @@ -108,7 +213,7 @@ extern "C" { argument_length = strnlen(arguments.argv[location], f_console_parameter_size); - // process the current parameter. + // Process the current parameter. if (result == f_console_short_enable_e || result == f_console_short_disable_e) { increment_by = 1; sub_location = 1; @@ -152,6 +257,7 @@ extern "C" { if (!parameters.parameter[i].symbol_short) continue; width = macro_f_utf_byte_width_is(arguments.argv[location][sub_location]); + if (width > 0) { increment_by = width; } @@ -167,7 +273,7 @@ extern "C" { status = f_utf_char_to_character(arguments.argv[location] + sub_location, width_max, &character_argument_utf); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } @@ -177,20 +283,16 @@ extern "C" { status = f_utf_char_to_character((f_string_t) parameters.parameter[i].symbol_short, width_max, &character_console_utf); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } - if (character_argument_utf != character_console_utf) { - continue; - } + if (character_argument_utf != character_console_utf) continue; } } else if (result == console_long) { - if (!parameters.parameter[i].symbol_long) { - continue; - } + if (!parameters.parameter[i].symbol_long) continue; if (strncmp(&arguments.argv[location][sub_location], parameters.parameter[i].symbol_long, increment_by + 1) != 0) { continue; @@ -200,18 +302,18 @@ extern "C" { continue; } - macro_f_array_lengths_t_increase(status, F_memory_default_allocation_small_d, parameters.parameter[i].locations) + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, ¶meters.parameter[i].locations); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } - macro_f_array_lengths_t_increase(status, F_memory_default_allocation_small_d, parameters.parameter[i].locations_sub) + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, ¶meters.parameter[i].locations_sub); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } @@ -232,18 +334,18 @@ extern "C" { parameters.parameter[i].locations_sub.array[parameters.parameter[i].locations_sub.used++] = 0; } - if (parameters.parameter[i].has_values) { - if (needs_value.used + parameters.parameter[i].has_values > needs_value.size) { - macro_f_array_lengths_t_resize(status, needs_value, needs_value.used + parameters.parameter[i].has_values); + if (parameters.parameter[i].values_total) { + if (needs_value.used + parameters.parameter[i].values_total > needs_value.size) { + status = f_type_array_lengths_resize(needs_value.used + parameters.parameter[i].values_total, &needs_value); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } } - for (values = 0; values < parameters.parameter[i].has_values; ++values) { + for (values = 0; values < parameters.parameter[i].values_total; ++values) { needs_value.array[needs_value.used++] = i; } // for } @@ -263,18 +365,18 @@ extern "C" { if (strncmp(arguments.argv[location], parameters.parameter[i].symbol_other, argument_length + 1) != 0) continue; - macro_f_array_lengths_t_increase(status, F_memory_default_allocation_small_d, parameters.parameter[i].locations) + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, ¶meters.parameter[i].locations); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } - macro_f_array_lengths_t_increase(status, F_memory_default_allocation_small_d, parameters.parameter[i].locations_sub) + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, ¶meters.parameter[i].locations_sub); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } @@ -286,35 +388,37 @@ extern "C" { parameters.parameter[i].location = location; parameters.parameter[i].location_sub = 0; - if (parameters.parameter[i].has_values) { - if (needs_value.used + parameters.parameter[i].has_values > needs_value.size) { - macro_f_array_lengths_t_resize(status, needs_value, needs_value.used + parameters.parameter[i].has_values); + if (parameters.parameter[i].values_total) { + if (needs_value.used + parameters.parameter[i].values_total > needs_value.size) { + status = f_type_array_lengths_resize(needs_value.used + parameters.parameter[i].values_total, &needs_value); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } } - for (values = 0; values < parameters.parameter[i].has_values; ++values) { + for (values = 0; values < parameters.parameter[i].values_total; ++values) { needs_value.array[needs_value.used++] = i; } // for } found = F_true; + break; } // for } if (!found) { - // populate list of remaining parameters.parameter not associated with anything. + // Populate list of remaining parameters.parameter that are not associated with anything. if (remaining->used == remaining->size) { - macro_f_memory_structure_increment(status, (*remaining), 1, F_memory_default_allocation_small_d, macro_f_array_lengths_t_resize, F_array_too_large); + status = f_type_array_lengths_increase(F_memory_default_allocation_small_d, remaining); if (F_status_is_error(status)) { - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); + return status; } } @@ -332,110 +436,12 @@ extern "C" { status = F_none; } - macro_f_array_lengths_t_delete_simple(needs_value); + f_type_array_lengths_resize(0, &needs_value); return status; } #endif // _di_f_console_parameter_process_ -#ifndef _di_f_console_parameter_prioritize_left_ - f_status_t f_console_parameter_prioritize_left(const f_console_parameters_t parameters, const f_console_parameter_ids_t choices, f_console_parameter_id_t *decision) { - #ifndef _di_level_0_parameter_checking_ - if (!decision) return F_status_set_error(F_parameter); - if (!choices.id) return F_status_set_error(F_parameter); - #endif // _di_level_0_parameter_checking_ - - if (!choices.used) { - return F_data_not; - } - - if (!parameters.used) { - return F_data_not; - } - - f_array_length_t location = 0; - f_array_length_t location_sub = 0; - f_console_parameter_id_t priority = 0; - - for (f_array_length_t i = 0; i < choices.used; ++i) { - - if (choices.id[i] > parameters.used) { - return F_status_set_error(F_parameter); - } - - if (parameters.parameter[choices.id[i]].result == f_console_result_found_e) { - if (parameters.parameter[choices.id[i]].location < location) { - location = parameters.parameter[choices.id[i]].location; - location_sub = parameters.parameter[choices.id[i]].location_sub; - priority = choices.id[i]; - } - else if (parameters.parameter[choices.id[i]].location == location && parameters.parameter[choices.id[i]].location_sub < location_sub) { - location_sub = parameters.parameter[choices.id[i]].location_sub; - priority = choices.id[i]; - } - } - } // for - - // The first parameter location (argc = 0) is the program name, therefore if the location is 0, then no matches were found. - if (!location) { - return F_data_not; - } - - *decision = priority; - - return F_none; - } -#endif // _di_f_console_parameter_prioritize_left_ - -#ifndef _di_f_console_parameter_prioritize_right_ - f_status_t f_console_parameter_prioritize_right(const f_console_parameters_t parameters, const f_console_parameter_ids_t choices, f_console_parameter_id_t *decision) { - #ifndef _di_level_0_parameter_checking_ - if (!decision) return F_status_set_error(F_parameter); - if (!choices.id) return F_status_set_error(F_parameter); - #endif // _di_level_0_parameter_checking_ - - if (!choices.used) { - return F_data_not; - } - - if (!parameters.used) { - return F_data_not; - } - - f_array_length_t location = 0; - f_array_length_t location_sub = 0; - f_console_parameter_id_t priority = 0; - - for (f_array_length_t i = 0; i < choices.used; ++i) { - - if (choices.id[i] > parameters.used) { - return F_status_set_error(F_parameter); - } - - if (parameters.parameter[choices.id[i]].result == f_console_result_found_e) { - if (parameters.parameter[choices.id[i]].location > location) { - location = parameters.parameter[choices.id[i]].location; - location_sub = parameters.parameter[choices.id[i]].location_sub; - priority = choices.id[i]; - } - else if (parameters.parameter[choices.id[i]].location == location && parameters.parameter[choices.id[i]].location_sub > location_sub) { - location_sub = parameters.parameter[choices.id[i]].location_sub; - priority = choices.id[i]; - } - } - } // for - - // The first parameter location (argc = 0) is the program name, therefore if the location is 0, then no matches were found. - if (!location) { - return F_data_not; - } - - *decision = priority; - - return F_none; - } -#endif // _di_f_console_parameter_prioritize_right_ - #ifdef __cplusplus } // extern "C" #endif diff --git a/level_0/f_console/c/console.h b/level_0/f_console/c/console.h index e40491a..d669de9 100644 --- a/level_0/f_console/c/console.h +++ b/level_0/f_console/c/console.h @@ -32,7 +32,7 @@ extern "C" { #endif /** - * Determine the type code the given input parameter represents. + * Determine the type code represented by the given input parameter. * * @param input * The input parameter to process. @@ -48,50 +48,6 @@ extern "C" { #endif // _di_f_console_identify_ /** - * Process console parameters. - * - * Short parameters are processed as follows: - * - Begin with either '-' or '+'. - * - "Empty" parameters are allow, such that '-' or '+' are valid parameters. - * - Are one character long. - * - May be grouped as a single parameter, such as "tar -xcf" and "tar -x -c -f" are equivalent. - * - Additional parameters must immediately follow the parameter or grouped parameters, such as "tar -xfc file.tar.gz" or "tar -x -f file.tar.gz -c". - * - * Long parameters are processed as follows: - * - Begin with either '--' or '++'. - * - "Empty" parameters are allow, such that '--' or '++' are valid parameters. - * - Are any length long so long as it is less than f_console_length_size. - * - May not be grouped and must be separated from any subsequent parameter, such as: "tar --extract --create --file". - * - Additional parameters must immediately follow the parameter, such as "tar --extract --file file.tar.gz --create". - * - * Other parameters are processed as follows: - * - Anything that does not begin with '-', '+', '--', or '++'. - * - Are any length long so long as it is less than f_console_length_size. - * - May not be grouped and must be separated from any subsequent parameter, such as: "tar extract create file". - * - Additional parameters must immediately follow the parameter, such as "tar extract file file.tar.gz create". - * - * @param arguments - * The parameters passed to the process. - * @param parameters - * The console parameters to look for. - * This will be updated by this function with the results. - * @param remaining - * A list of remaining parameters not associated with anything. - * - * @return - * F_none on success. - * F_data_not if "values" parameters were expected but not found. - * - * F_array_too_large (with error bit) if a buffer would exceed max length. - * F_failure (with error bit) if width is not long enough to convert when processing arguments as UTF-8. - * F_parameter (with error bit) if a parameter is invalid. - * F_utf (with error bit) if character is an invalid UTF-8 character, when processing arguments. - */ -#ifndef _di_f_console_parameter_process_ - extern f_status_t f_console_parameter_process(const f_console_arguments_t arguments, f_console_parameters_t parameters, f_array_lengths_t *remaining); -#endif // _di_f_console_parameter_process_ - -/** * Given a set of parameter choices, determine which one has the highest priority. * * The priority is determined by viewing the parameters from left to right. @@ -153,6 +109,50 @@ extern "C" { extern f_status_t f_console_parameter_prioritize_right(const f_console_parameters_t parameters, const f_console_parameter_ids_t choices, f_console_parameter_id_t *decision); #endif // _di_f_console_parameter_prioritize_right_ +/** + * Process console parameters. + * + * Short parameters are processed as follows: + * - Begin with either '-' or '+'. + * - "Empty" parameters are allow, such that '-' or '+' are valid parameters. + * - Are one character long. + * - May be grouped as a single parameter, such as "tar -xcf" and "tar -x -c -f" are equivalent. + * - Additional parameters must immediately follow the parameter or grouped parameters, such as "tar -xfc file.tar.gz" or "tar -x -f file.tar.gz -c". + * + * Long parameters are processed as follows: + * - Begin with either '--' or '++'. + * - "Empty" parameters are allow, such that '--' or '++' are valid parameters. + * - Are any length long so long as it is less than f_console_length_size. + * - May not be grouped and must be separated from any subsequent parameter, such as: "tar --extract --create --file". + * - Additional parameters must immediately follow the parameter, such as "tar --extract --file file.tar.gz --create". + * + * Other parameters are processed as follows: + * - Anything that does not begin with '-', '+', '--', or '++'. + * - Are any length long so long as it is less than f_console_length_size. + * - May not be grouped and must be separated from any subsequent parameter, such as: "tar extract create file". + * - Additional parameters must immediately follow the parameter, such as "tar extract file file.tar.gz create". + * + * @param arguments + * The parameters passed to the process. + * @param parameters + * The console parameters to look for. + * This will be updated by this function with the results. + * @param remaining + * A list of remaining parameters not associated with anything. + * + * @return + * F_none on success. + * F_data_not if "values" parameters were expected but not found. + * + * F_array_too_large (with error bit) if a buffer would exceed max length. + * F_failure (with error bit) if width is not long enough to convert when processing arguments as UTF-8. + * F_parameter (with error bit) if a parameter is invalid. + * F_utf (with error bit) if character is an invalid UTF-8 character, when processing arguments. + */ +#ifndef _di_f_console_parameter_process_ + extern f_status_t f_console_parameter_process(const f_console_arguments_t arguments, f_console_parameters_t parameters, f_array_lengths_t *remaining); +#endif // _di_f_console_parameter_process_ + #ifdef __cplusplus } // extern "C" #endif -- 1.8.3.1