From 44c055c0c55000a63b747c4ea7bcc8ea435428b5 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Mon, 28 Sep 2020 21:42:02 -0500 Subject: [PATCH] Progress: fss write programs synchronize. Work on making the fss write programs function more similar to how the iki_write program functions. There needs to be more work done in the fss_basic_list write at level_1, but that is being left to be worked on for another commit. Looking at the code once again reminds me that the FSS read and write code needs to be reviewed in more depth, but I am going to put that off for now. Other minor fixes and cleanups. --- level_0/f_fss/c/fss-common.h | 2 +- level_1/fl_fss/c/private-fss.c | 2 +- level_2/fll_fss/c/fss_basic_list.c | 4 +- level_2/fll_fss/c/fss_basic_list.h | 5 +- .../fss_basic_list_write/c/fss_basic_list_write.c | 458 +++++++++++++++----- .../fss_basic_list_write/c/fss_basic_list_write.h | 22 +- .../c/private-fss_basic_list_write.c | 89 ++++ .../c/private-fss_basic_list_write.h | 76 ++++ level_3/fss_basic_list_write/data/build/settings | 2 +- level_3/fss_basic_write/c/fss_basic_write.c | 449 +++++++++++++++---- level_3/fss_basic_write/c/fss_basic_write.h | 9 +- level_3/fss_basic_write/c/main.c | 2 +- .../fss_basic_write/c/private-fss_basic_write.c | 89 ++++ .../fss_basic_write/c/private-fss_basic_write.h | 76 ++++ level_3/fss_basic_write/data/build/settings | 2 +- level_3/fss_extended_read/c/fss_extended_read.h | 2 +- level_3/fss_extended_read/c/main.c | 2 +- level_3/fss_extended_write/c/fss_extended_write.c | 479 +++++++++++++++------ .../c/private-fss_extended_write.c | 89 ++++ .../c/private-fss_extended_write.h | 76 ++++ level_3/fss_extended_write/data/build/settings | 2 +- 21 files changed, 1600 insertions(+), 337 deletions(-) create mode 100644 level_3/fss_basic_list_write/c/private-fss_basic_list_write.c create mode 100644 level_3/fss_basic_list_write/c/private-fss_basic_list_write.h create mode 100644 level_3/fss_basic_write/c/private-fss_basic_write.c create mode 100644 level_3/fss_basic_write/c/private-fss_basic_write.h create mode 100644 level_3/fss_extended_write/c/private-fss_extended_write.c create mode 100644 level_3/fss_extended_write/c/private-fss_extended_write.h diff --git a/level_0/f_fss/c/fss-common.h b/level_0/f_fss/c/fss-common.h index ad84a76..4d183ce 100644 --- a/level_0/f_fss/c/fss-common.h +++ b/level_0/f_fss/c/fss-common.h @@ -31,7 +31,7 @@ extern "C" { #define f_fss_basic_list_open ':' #define f_fss_basic_list_close '\0' #define f_fss_extended_list_open '{' - #define f_fss_extended_list_close '}' + #define f_fss_extended_list_close '}' // also requires '\n'. #define f_fss_type_header_open '#' #define f_fss_type_header_part1 ' ' #define f_fss_type_header_part2 'f' diff --git a/level_1/fl_fss/c/private-fss.c b/level_1/fl_fss/c/private-fss.c index 7fd9770..62d6566 100644 --- a/level_1/fl_fss/c/private-fss.c +++ b/level_1/fl_fss/c/private-fss.c @@ -472,7 +472,7 @@ extern "C" { } } - // seek to the end of the line when no valid object is found-> + // seek to the end of the line when no valid object is found. while (range->start < buffer->used && range->start <= range->stop && buffer->string[range->start] != f_string_eol[0]) { status = f_utf_buffer_increment(*buffer, range, 1); diff --git a/level_2/fll_fss/c/fss_basic_list.c b/level_2/fll_fss/c/fss_basic_list.c index ddb6495..44a35fd 100644 --- a/level_2/fll_fss/c/fss_basic_list.c +++ b/level_2/fll_fss/c/fss_basic_list.c @@ -134,7 +134,7 @@ extern "C" { #endif // _di_fll_fss_basic_list_read_ #ifndef _di_fll_fss_basic_list_write_ - f_return_status fll_fss_basic_list_write(const f_string_static_t object, const f_string_statics_t contents, f_string_dynamic_t *buffer) { + f_return_status fll_fss_basic_list_write(const f_string_static_t object, const f_string_statics_t contents, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) { #ifndef _di_level_2_parameter_checking_ if (!buffer) return F_status_set_error(F_parameter); if (contents.used > contents.size) return F_status_set_error(F_parameter); @@ -147,7 +147,7 @@ extern "C" { range.start = 0; range.stop = object.used - 1; - status = fl_fss_basic_list_object_write(object, &range, buffer); + status = fl_fss_basic_list_object_write(object, quoted, &range, buffer); if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { return status; diff --git a/level_2/fll_fss/c/fss_basic_list.h b/level_2/fll_fss/c/fss_basic_list.h index 7e600b8..1d07f19 100644 --- a/level_2/fll_fss/c/fss_basic_list.h +++ b/level_2/fll_fss/c/fss_basic_list.h @@ -67,6 +67,9 @@ extern "C" { * A string representing the object. * @param contents * An array of strings representing multiple content to write. + * @param quoted + * If 0, then double quotes are auto-inserted, when required. + * Otherwise, this is the type of quote to wrap the object in when writing. * @param buffer * The buffer to write to. * @@ -86,7 +89,7 @@ extern "C" { * Errors (with error bit) from: fl_string_dynamic_size_increase(). */ #ifndef _di_fll_fss_basic_list_write_ - extern f_return_status fll_fss_basic_list_write(const f_string_static_t object, const f_string_statics_t contents, f_string_dynamic_t *buffer); + extern f_return_status fll_fss_basic_list_write(const f_string_static_t object, const f_string_statics_t contents, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer); #endif // _di_fll_fss_basic_list_write_ #ifdef __cplusplus diff --git a/level_3/fss_basic_list_write/c/fss_basic_list_write.c b/level_3/fss_basic_list_write/c/fss_basic_list_write.c index 3be7ab7..66cbe79 100644 --- a/level_3/fss_basic_list_write/c/fss_basic_list_write.c +++ b/level_3/fss_basic_list_write/c/fss_basic_list_write.c @@ -1,4 +1,5 @@ #include "fss_basic_list_write.h" +#include "private-fss_basic_list_write.h" #ifdef __cplusplus extern "C" { @@ -100,156 +101,409 @@ extern "C" { return status; } - f_array_length_t counter = 0; - bool object = (data->parameters[fss_basic_list_write_parameter_object].result == f_console_result_found); + f_file_t output = f_file_t_initialize; - f_string_dynamic_t buffer = f_string_dynamic_t_initialize; - f_string_range_t range = f_string_range_t_initialize; - - if (data->process_pipe) { - f_file_t file = f_file_t_initialize; - f_string_dynamic_t input = f_string_dynamic_t_initialize; + output.id = f_type_descriptor_output; + output.stream = f_type_output; + output.flag = f_file_flag_create | f_file_flag_write_only | f_file_flag_append; - file.id = f_type_descriptor_input; + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_list_write_parameter_file].result == f_console_result_additional) { + if (data->parameters[fss_basic_list_write_parameter_file].additional.used > 1) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_file); + fl_color_print(data->error.to.stream, data->context.set.error, "' may only be specified once.%c", f_string_eol[0]); + } - status = f_file_read(file, &input); + status = F_status_set_error(F_parameter); + } + else { + const f_string_length_t location = data->parameters[fss_basic_list_write_parameter_file].additional.array[0]; - if (F_status_is_error(status)) { - status = F_status_set_fine(status); + output.id = -1; + output.stream = 0; + status = f_file_stream_open(arguments.argv[location], 0, &output); - if (status == F_parameter) { - fl_color_print(data->error.to.stream, data->context.set.error, "%sInvalid parameter when calling f_file_open()%c", fll_error_print_error, f_string_eol[0]); - } - else if (status == F_file_found_not) { - fl_color_print(data->error.to.stream, data->context.set.error, "%sUnable to find the file '%s'%c", fll_error_print_error, "-", f_string_eol[0]); + if (F_status_is_error(status)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.argv[location], "open", fll_error_file_type_file); + } } - else if (status == F_file_open) { - fl_color_print(data->error.to.stream, data->context.set.error, "%sUnable to open the file '%s'%c", fll_error_print_error, "-", f_string_eol[0]); + } + else if (data->parameters[fss_basic_list_write_parameter_file].result == f_console_result_found) { + fss_basic_list_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_list_write_long_file); + status = F_status_set_error(F_parameter); + } + } + + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_list_write_parameter_object].locations.used || data->parameters[fss_basic_list_write_parameter_content].locations.used) { + if (data->parameters[fss_basic_list_write_parameter_object].locations.used) { + if (data->parameters[fss_basic_list_write_parameter_object].locations.used != data->parameters[fss_basic_list_write_parameter_object].additional.used) { + fss_basic_list_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_list_write_long_object); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_list_write_parameter_content].locations.used != data->parameters[fss_basic_list_write_parameter_content].additional.used) { + fss_basic_list_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_list_write_long_content); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_list_write_parameter_object].locations.used != data->parameters[fss_basic_list_write_parameter_content].locations.used) { + fss_basic_list_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_list_write_parameter_content].locations.used && data->parameters[fss_basic_list_write_parameter_partial].locations.used) { + if (data->parameters[fss_basic_list_write_parameter_content].result == f_console_result_additional) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_partial); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter only allows either the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter or the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter, but not both.%c", f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + } + } } - else if (status == F_file_descriptor) { - fl_color_print(data->error.to.stream, data->context.set.error, "%sFile descriptor error while trying to open the file '%s'%c", fll_error_print_error, "-", f_string_eol[0]); + else if (data->parameters[fss_basic_list_write_parameter_content].locations.used) { + if (data->parameters[fss_basic_list_write_parameter_content].locations.used != data->parameters[fss_basic_list_write_parameter_content].additional.used) { + fss_basic_list_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_list_write_long_content); + status = F_status_set_error(F_parameter); + } + else if (!data->parameters[fss_basic_list_write_parameter_partial].locations.used) { + fss_basic_list_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); + } } - else { - fl_color_print(data->error.to.stream, data->context.set.error, "%sAn unhandled error (%u) has occurred while calling f_file_open()%c", fll_error_print_error, status, f_string_eol[0]); + } + else if (!data->process_pipe) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThis requires either piped data or the use of the '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter with the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter.%c", f_string_eol[0]); } - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_list_write_delete_data(data); - return F_status_set_error(status); + status = F_status_set_error(F_parameter); } + } + + f_fss_quoted_t quoted = f_fss_delimit_quote_double; + + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_list_write_parameter_double].result == f_console_result_found) { + if (data->parameters[fss_basic_list_write_parameter_single].result == f_console_result_found) { + if (data->parameters[fss_basic_list_write_parameter_double].location < data->parameters[fss_basic_list_write_parameter_single].location) { + quoted = f_fss_delimit_quote_single; + } + } + } + else if (data->parameters[fss_basic_list_write_parameter_single].result == f_console_result_found) { + quoted = f_fss_delimit_quote_single; + } + } + + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + f_string_dynamic_t object = f_string_dynamic_t_initialize; + f_string_dynamic_t content = f_string_dynamic_t_initialize; + + if (F_status_is_error_not(status)) { + f_string_dynamic_t escaped = f_string_dynamic_t_initialize; + + if (data->process_pipe) { + f_file_t input = f_file_t_initialize; + + input.id = f_type_descriptor_input; + input.size_read = 1; + + bool object_ended = F_false; + + f_string_length_t previous = 0; + f_string_range_t range = f_string_range_t_initialize; - if (input.used) { range.start = 0; - range.stop = input.used - 1; - if (object) { - status = fl_fss_basic_list_object_write(input, &range, &buffer); + if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { + for (f_status_t status_pipe = F_none; ; ) { - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos || status == F_data_not_eol) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_list_write_delete_data(data); - return F_status_set_error(status); - } + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); - // this should remove both the closing newline and the colon. - if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { - buffer.used -= 2; - } + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } + + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (data->parameters[fss_basic_list_write_parameter_object].result == f_console_result_found) { + object.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + else { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + + status = fss_basic_list_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; + } + + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for } else { - status = fl_fss_basic_list_content_write(input, &range, &buffer); + for (f_status_t status_pipe = F_none; ; ) { - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos || status == F_data_not_eol) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_list_write_delete_data(data); - return F_status_set_error(status); - } + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); + + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } + + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + if (object_ended && previous == range.start) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has incorrectly placed newlines.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (object_ended) { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &content); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + + status = fss_basic_list_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + object_ended = F_false; + } + else { + object.used = 0; + + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + + object_ended = F_true; + } + + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; + } - if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { - buffer.used--; + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for + + if (F_status_is_error_not(status) && object_ended) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has an object without content.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); } } } - f_macro_string_dynamic_t_delete_simple(input); - } - else if (data->parameters[fss_basic_list_write_parameter_string].result == f_console_result_additional) { - f_string_dynamic_t input = f_string_dynamic_t_initialize; + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { + if (data->parameters[fss_basic_list_write_parameter_object].result == f_console_result_found) { + content.used = 0; - input.string = arguments.argv[data->parameters[fss_basic_list_write_parameter_string].additional.array[0]]; - input.used = strlen(input.string); + for (f_array_length_t i = 0; i < data->parameters[fss_basic_list_write_parameter_object].additional.used; i++) { - if (input.used) { - range.start = 0; - range.stop = input.used - 1; + object.string = arguments.argv[data->parameters[fss_basic_list_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; - if (object) { - status = fl_fss_basic_list_object_write(input, &range, &buffer); + status = fss_basic_list_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos || status == F_data_not_eol) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_list_write_delete_data(data); - return F_status_set_error(status); + fprintf(output.stream, "%c", f_string_eol[0]); + } // for } + else { + object.used = 0; - // this should remove both the closing newline and the colon. - if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { - buffer.used -= 2; - } - } - else { - status = fl_fss_basic_list_content_write(input, &range, &buffer); + for (f_array_length_t i = 0; i < data->parameters[fss_basic_list_write_parameter_object].additional.used; i++) { - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos || status == F_data_not_eol) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_list_write_delete_data(data); - return F_status_set_error(status); - } + content.string = arguments.argv[data->parameters[fss_basic_list_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; + + status = fss_basic_list_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; - if (data->parameters[fss_basic_list_write_parameter_partial].result == f_console_result_found) { - buffer.used--; + fprintf(output.stream, "%c", f_string_eol[0]); + } // for } } - } - - status = F_none; - } + else { + for (f_array_length_t i = 0; i < data->parameters[fss_basic_list_write_parameter_object].additional.used; i++) { - if (data->parameters[fss_basic_list_write_parameter_file].result == f_console_result_additional) { - f_file_t output = f_file_t_initialize; + object.string = arguments.argv[data->parameters[fss_basic_list_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; - output.flag = f_file_flag_append_wo; + content.string = arguments.argv[data->parameters[fss_basic_list_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; - status = f_file_open(arguments.argv[data->parameters[fss_basic_list_write_parameter_file].additional.array[0]], 0, &output); + status = fss_basic_list_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_flag_append_wo", F_true, file.string, "open", fll_error_file_type_file); + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_list_write_delete_data(data); - return status; + // ensure there is always a newline at the end, unless in quiet mode. + if (F_status_is_error_not(status) && data->error.verbosity != f_console_verbosity_quiet && data->parameters[fss_basic_list_write_parameter_file].result == f_console_result_none) { + fprintf(f_type_output, "%c", f_string_eol[0]); + } } - status = f_file_write(output, buffer, 0); - f_file_stream_close(F_true, &output); + f_macro_string_dynamic_t_delete_simple(escaped); - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_close", F_true, file.string, "close", fll_error_file_type_file); + // object and content, though being a "dynamic" type, is being used statically, so clear them up to avoid invalid free(). + object.string = 0; + object.used = 0; + object.size = 0; - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_list_write_delete_data(data); - return status; + content.string = 0; + content.used = 0; + content.size = 0; + } + + if (data->parameters[fss_basic_list_write_parameter_file].result == f_console_result_additional) { + if (output.id != -1) { + f_file_stream_close(F_true, &output); } } - else { - f_print_dynamic(f_type_output, buffer); + + // ensure a newline is always put at the end of the program execution, unless in quiet mode. + if (data->error.verbosity != f_console_verbosity_quiet) { + if (F_status_is_error(status)) { + fprintf(data->error.to.stream, "%c", f_string_eol[0]); + } } f_macro_string_dynamic_t_delete_simple(buffer); - + f_macro_string_dynamic_t_delete_simple(object); + f_macro_string_dynamic_t_delete_simple(content); fss_basic_list_write_delete_data(data); return status; } diff --git a/level_3/fss_basic_list_write/c/fss_basic_list_write.h b/level_3/fss_basic_list_write/c/fss_basic_list_write.h index c5fab01..1a1850f 100644 --- a/level_3/fss_basic_list_write/c/fss_basic_list_write.h +++ b/level_3/fss_basic_list_write/c/fss_basic_list_write.h @@ -28,8 +28,8 @@ // fll-1 includes #include #include -#include #include +#include #include // fll-2 includes @@ -89,21 +89,21 @@ extern "C" { #define fss_basic_list_write_console_parameter_t_initialize \ { \ - f_console_parameter_t_initialize(f_console_standard_short_help, f_console_standard_long_help, 0, F_false, f_console_type_normal), \ - f_console_parameter_t_initialize(f_console_standard_short_light, f_console_standard_long_light, 0, F_false, f_console_type_inverse), \ - f_console_parameter_t_initialize(f_console_standard_short_dark, f_console_standard_long_dark, 0, F_false, f_console_type_inverse), \ + f_console_parameter_t_initialize(f_console_standard_short_help, f_console_standard_long_help, 0, 0, f_console_type_normal), \ + f_console_parameter_t_initialize(f_console_standard_short_light, f_console_standard_long_light, 0, 0, f_console_type_inverse), \ + f_console_parameter_t_initialize(f_console_standard_short_dark, f_console_standard_long_dark, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_no_color, f_console_standard_long_no_color, 0, F_false, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_quiet, f_console_standard_long_quiet, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_normal, f_console_standard_long_normal, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_verbose, f_console_standard_long_verbose, 0, 0, f_console_type_inverse), \ f_console_parameter_t_initialize(f_console_standard_short_debug, f_console_standard_long_debug, 0, 0, f_console_type_inverse), \ - f_console_parameter_t_initialize(f_console_standard_short_version, f_console_standard_long_version, 0, F_false, f_console_type_inverse), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_file, fss_basic_list_write_long_file, 0, F_true, f_console_type_normal), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_content, fss_basic_list_write_long_content, 0, F_true, f_console_type_normal), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_double, fss_basic_list_write_long_double, 0, F_true, f_console_type_normal), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_object, fss_basic_list_write_long_object, 0, F_false, f_console_type_normal), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_partial, fss_basic_list_write_long_partial, 0, F_true, f_console_type_normal), \ - f_console_parameter_t_initialize(fss_basic_list_write_short_single, fss_basic_list_write_long_single, 0, F_true, f_console_type_normal), \ + f_console_parameter_t_initialize(f_console_standard_short_version, f_console_standard_long_version, 0, 0, f_console_type_inverse), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_file, fss_basic_list_write_long_file, 0, 1, f_console_type_normal), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_content, fss_basic_list_write_long_content, 0, 1, f_console_type_normal), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_double, fss_basic_list_write_long_double, 0, 0, f_console_type_normal), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_object, fss_basic_list_write_long_object, 0, 1, f_console_type_normal), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_partial, fss_basic_list_write_long_partial, 0, 0, f_console_type_normal), \ + f_console_parameter_t_initialize(fss_basic_list_write_short_single, fss_basic_list_write_long_single, 0, 0, f_console_type_normal), \ } #define fss_basic_list_write_total_parameters 15 diff --git a/level_3/fss_basic_list_write/c/private-fss_basic_list_write.c b/level_3/fss_basic_list_write/c/private-fss_basic_list_write.c new file mode 100644 index 0000000..664b62d --- /dev/null +++ b/level_3/fss_basic_list_write/c/private-fss_basic_list_write.c @@ -0,0 +1,89 @@ +#include "fss_basic_list_write.h" +#include "private-fss_basic_list_write.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_fss_basic_list_write_error_parameter_same_times_print_ + void fss_basic_list_write_error_parameter_same_times_print(const fss_basic_list_write_data_t data) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sMust specify both the '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_object); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter and the '"); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_content); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter the same number of times when not specifying the "); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_list_write_long_partial); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter.%c", f_string_eol[0]); + } +#endif // _di_fss_basic_list_write_error_parameter_same_times_print_ + +#ifndef _di_fss_basic_list_write_error_parameter_value_missing_print_ + void fss_basic_list_write_error_parameter_value_missing_print(const fss_basic_list_write_data_t data, const f_string_t symbol, const f_string_t parameter) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", symbol, parameter); + fl_color_print(data.error.to.stream, data.context.set.error, "' was specified, but no value was given.%c", f_string_eol[0]); + } +#endif // _di_fss_basic_list_write_error_parameter_value_missing_print_ + +#ifndef _di_fss_basic_list_write_process_ + f_return_status fss_basic_list_write_process(const fss_basic_list_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) { + f_status_t status = F_none; + + f_string_range_t range = f_macro_string_range_t_initialize(object.used); + + buffer->used = 0; + + if (data.parameters[fss_basic_list_write_parameter_object].result == f_console_result_found) { + + status = fl_fss_basic_list_object_write(object, quoted, &range, buffer); + + if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_object_write", F_true); + return F_status_set_error(status); + } + } + + if (data.parameters[fss_basic_list_write_parameter_content].result == f_console_result_found) { + if (content.used) { + range.start = 0; + range.stop = content.used; + } + else { + range.start = 1; + range.stop = 0; + } + + status = fl_fss_basic_list_content_write(content, &range, buffer); + + if (F_status_is_error(status) || data.parameters[fss_basic_list_write_parameter_content].result == f_console_result_found) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_content_write", F_true); + return F_status_set_error(status); + } + } + + status = fl_string_dynamic_terminate(buffer); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate", F_true); + return F_status_set_error(status); + } + + f_print_dynamic(output.stream, *buffer); + + return status; + } +#endif // _di_fss_basic_list_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_3/fss_basic_list_write/c/private-fss_basic_list_write.h b/level_3/fss_basic_list_write/c/private-fss_basic_list_write.h new file mode 100644 index 0000000..4224ae4 --- /dev/null +++ b/level_3/fss_basic_list_write/c/private-fss_basic_list_write.h @@ -0,0 +1,76 @@ +/** + * FLL - Level 3 + * + * Project: FSS + * API Version: 0.5 + * Licenses: lgplv2.1 + */ +#ifndef _PRIVATE_fss_basic_list_write_h +#define _PRIVATE_fss_basic_list_write_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print an message about the object and content parameters not being specified the same number of times. + * + * @param data + * The program data. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_list_write_error_parameter_same_times_print_ + void fss_basic_list_write_error_parameter_same_times_print(const fss_basic_list_write_data_t data) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_list_write_error_parameter_same_times_print_ + +/** + * Print an message about a parameter missing a value. + * + * @param data + * The program data. + * @param symbol + * The console symbol, such as "--" in "--help". + * @param parameter + * The parameter name, such as "help" in "--help". + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_list_write_error_parameter_value_missing_print_ + void fss_basic_list_write_error_parameter_value_missing_print(const fss_basic_list_write_data_t data, const f_string_t symbol, const f_string_t parameter) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_list_write_error_parameter_value_missing_print_ + +/** + * Process a given object and content, printing the FSS if valid or an error if invalid. + * + * @param data + * The program data. + * @param output + * The file to output to. + * @param object + * The object to validate and print. + * @param content + * The content to escape and print. + * @param quoted + * The quote character to use. + * This is either single our double quote. + * @param buffer + * The buffer array used as a cache to construct the output before printing. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_list_write_process_ + extern f_return_status fss_basic_list_write_process(const fss_basic_list_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_list_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _PRIVATE_fss_basic_list_write_h diff --git a/level_3/fss_basic_list_write/data/build/settings b/level_3/fss_basic_list_write/data/build/settings index 62e08fb..8348d98 100644 --- a/level_3/fss_basic_list_write/data/build/settings +++ b/level_3/fss_basic_list_write/data/build/settings @@ -22,7 +22,7 @@ build_libraries -lc build_libraries-individual -lfll_program -lfll_fss -lfll_file -lfl_directory -lfll_execute -lfl_environment -lf_signal -lf_path -lfll_error -lfl_string -lfl_status -lfl_fss -lfl_conversion -lfl_console -lf_conversion -lfl_color -lf_print -lf_pipe -lf_fss -lf_file -lf_environment -lf_directory -lf_console -lf_utf -lf_memory build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll -build_sources_library fss_basic_list_write.c +build_sources_library fss_basic_list_write.c private-fss_basic_list_write.c build_sources_program main.c build_sources_headers fss_basic_list_write.h build_sources_script diff --git a/level_3/fss_basic_write/c/fss_basic_write.c b/level_3/fss_basic_write/c/fss_basic_write.c index a5ab880..38e7ef7 100644 --- a/level_3/fss_basic_write/c/fss_basic_write.c +++ b/level_3/fss_basic_write/c/fss_basic_write.c @@ -1,4 +1,5 @@ #include "fss_basic_write.h" +#include "private-fss_basic_write.h" #ifdef __cplusplus extern "C" { @@ -35,7 +36,7 @@ extern "C" { #endif // _di_fss_basic_write_print_help_ #ifndef _di_fss_basic_write_main_ - f_return_status fss_basic_write_main(const f_console_arguments_t arguments, fss_basic_write_data *data) { + f_return_status fss_basic_write_main(const f_console_arguments_t arguments, fss_basic_write_data_t *data) { f_status_t status = F_none; { @@ -52,7 +53,7 @@ extern "C" { if (F_status_is_error(status)) { fss_basic_write_delete_data(data); - return status; + return F_status_set_error(status); } } @@ -100,136 +101,416 @@ extern "C" { return status; } - f_array_length_t counter = 0; - bool object = data->parameters[fss_basic_write_parameter_object].result == f_console_result_found; - - f_string_dynamic_t buffer = f_string_dynamic_t_initialize; - f_string_range_t range = f_string_range_t_initialize; + f_file_t output = f_file_t_initialize; - if (data->process_pipe) { - f_file_t file = f_file_t_initialize; - f_string_dynamic_t input = f_string_dynamic_t_initialize; + output.id = f_type_descriptor_output; + output.stream = f_type_output; + output.flag = f_file_flag_create | f_file_flag_write_only | f_file_flag_append; - file.id = f_type_descriptor_input; + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_write_parameter_file].result == f_console_result_additional) { + if (data->parameters[fss_basic_write_parameter_file].additional.used > 1) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_file); + fl_color_print(data->error.to.stream, data->context.set.error, "' may only be specified once.%c", f_string_eol[0]); + } - status = f_file_read(file, &input); + status = F_status_set_error(F_parameter); + } + else { + const f_string_length_t location = data->parameters[fss_basic_write_parameter_file].additional.array[0]; - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read", F_true, file.string, "read", fll_error_file_type_file); + output.id = -1; + output.stream = 0; + status = f_file_stream_open(arguments.argv[location], 0, &output); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_write_delete_data(data); - return F_status_set_error(status); + if (F_status_is_error(status)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.argv[location], "open", fll_error_file_type_file); + } + } } + else if (data->parameters[fss_basic_write_parameter_file].result == f_console_result_found) { + fss_basic_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_write_long_file); + status = F_status_set_error(F_parameter); + } + } - if (input.used) { - range.start = 0; - range.stop = input.used - 1; - - if (object) { - status = fl_fss_basic_object_write(input, 0, &range, &buffer); - - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_write_delete_data(data); - return F_status_set_error(status); + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_write_parameter_object].locations.used || data->parameters[fss_basic_write_parameter_content].locations.used) { + if (data->parameters[fss_basic_write_parameter_object].locations.used) { + if (data->parameters[fss_basic_write_parameter_object].locations.used != data->parameters[fss_basic_write_parameter_object].additional.used) { + fss_basic_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_write_long_object); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_write_parameter_content].locations.used != data->parameters[fss_basic_write_parameter_content].additional.used) { + fss_basic_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_write_long_content); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_write_parameter_object].locations.used != data->parameters[fss_basic_write_parameter_content].locations.used) { + fss_basic_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_basic_write_parameter_content].locations.used && data->parameters[fss_basic_write_parameter_partial].locations.used) { + if (data->parameters[fss_basic_write_parameter_content].result == f_console_result_additional) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_partial); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter only allows either the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter or the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter, but not both.%c", f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + } } } - else { - status = fl_fss_basic_content_write(input, &range, &buffer); - - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_basic_write_delete_data(data); - return F_status_set_error(status); + else if (data->parameters[fss_basic_write_parameter_content].locations.used) { + if (data->parameters[fss_basic_write_parameter_content].locations.used != data->parameters[fss_basic_write_parameter_content].additional.used) { + fss_basic_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_basic_write_long_content); + status = F_status_set_error(F_parameter); + } + else if (!data->parameters[fss_basic_write_parameter_partial].locations.used) { + fss_basic_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); } } - - if (data->parameters[fss_basic_write_parameter_partial].result == f_console_result_found) { - buffer.used--; + } + else if (!data->process_pipe) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThis requires either piped data or the use of the '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter with the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter.%c", f_string_eol[0]); } + + status = F_status_set_error(F_parameter); } + } - f_macro_string_dynamic_t_delete_simple(input); + f_fss_quoted_t quoted = f_fss_delimit_quote_double; + + if (F_status_is_error_not(status)) { + if (data->parameters[fss_basic_write_parameter_double].result == f_console_result_found) { + if (data->parameters[fss_basic_write_parameter_single].result == f_console_result_found) { + if (data->parameters[fss_basic_write_parameter_double].location < data->parameters[fss_basic_write_parameter_single].location) { + quoted = f_fss_delimit_quote_single; + } + } + } + else if (data->parameters[fss_basic_write_parameter_single].result == f_console_result_found) { + quoted = f_fss_delimit_quote_single; + } } - else if (data->parameters[fss_basic_write_parameter_string].result == f_console_result_additional) { - f_string_dynamic_t input = f_string_dynamic_t_initialize; - input.string = arguments.argv[data->parameters[fss_basic_write_parameter_string].additional.array[0]]; - input.used = strnlen(input.string, f_console_length_size); + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + f_string_dynamic_t object = f_string_dynamic_t_initialize; + f_string_dynamic_t content = f_string_dynamic_t_initialize; + + if (F_status_is_error_not(status)) { + f_string_dynamic_t escaped = f_string_dynamic_t_initialize; + + if (data->process_pipe) { + f_file_t input = f_file_t_initialize; + + input.id = f_type_descriptor_input; + input.size_read = 1; + + bool object_ended = F_false; + + f_string_length_t previous = 0; + f_string_range_t range = f_string_range_t_initialize; - if (input.used) { range.start = 0; - range.stop = input.used - 1; - if (object) { - status = fl_fss_basic_object_write(input, 0, &range, &buffer); + if (data->parameters[fss_basic_write_parameter_partial].result == f_console_result_found) { + for (f_status_t status_pipe = F_none; ; ) { + + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_write_delete_data(data); - return F_status_set_error(status); - } + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } + + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (data->parameters[fss_basic_write_parameter_object].result == f_console_result_found) { + object.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + else { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + + status = fss_basic_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; + } + + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for } else { - status = fl_fss_basic_content_write(input, &range, &buffer); + for (f_status_t status_pipe = F_none; ; ) { + + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); + + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } + + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + if (object_ended && previous == range.start) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has incorrectly placed newlines.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (object_ended) { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &content); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + + status = fss_basic_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + object_ended = F_false; + } + else { + object.used = 0; + + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + + object_ended = F_true; + } + + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; + } + + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for + + if (F_status_is_error_not(status) && object_ended) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has an object without content.%c", fll_error_print_error, f_string_eol[0]); + } - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_write_delete_data(data); - return F_status_set_error(status); + status = F_status_set_error(F_parameter); } } + } + if (F_status_is_error_not(status)) { if (data->parameters[fss_basic_write_parameter_partial].result == f_console_result_found) { - buffer.used--; - } - } + if (data->parameters[fss_basic_write_parameter_object].result == f_console_result_found) { + content.used = 0; - status = F_none; - } + for (f_array_length_t i = 0; i < data->parameters[fss_basic_write_parameter_object].additional.used; i++) { - if (data->parameters[fss_basic_write_parameter_file].result == f_console_result_additional) { - f_file_t output = f_file_t_initialize; + object.string = arguments.argv[data->parameters[fss_basic_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; + + status = fss_basic_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; - output.flag = f_file_flag_append_wo; + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + else { + object.used = 0; - status = f_file_open(arguments.argv[data->parameters[fss_basic_write_parameter_file].additional.array[0]], 0, &output); + for (f_array_length_t i = 0; i < data->parameters[fss_basic_write_parameter_object].additional.used; i++) { - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_open", F_true, file.string, "open", fll_error_file_type_file); + content.string = arguments.argv[data->parameters[fss_basic_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; + + status = fss_basic_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + } + else { + for (f_array_length_t i = 0; i < data->parameters[fss_basic_write_parameter_object].additional.used; i++) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_write_delete_data(data); - return status; + object.string = arguments.argv[data->parameters[fss_basic_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; + + content.string = arguments.argv[data->parameters[fss_basic_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; + + status = fss_basic_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + + // ensure there is always a newline at the end, unless in quiet mode. + if (F_status_is_error_not(status) && data->error.verbosity != f_console_verbosity_quiet && data->parameters[fss_basic_write_parameter_file].result == f_console_result_none) { + fprintf(f_type_output, "%c", f_string_eol[0]); + } } - status = f_file_write(output, buffer, 0); - f_file_stream_close(F_true, &output); + f_macro_string_dynamic_t_delete_simple(escaped); - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_close", F_true, file.string, "close", fll_error_file_type_file); + // object and content, though being a "dynamic" type, is being used statically, so clear them up to avoid invalid free(). + object.string = 0; + object.used = 0; + object.size = 0; - f_macro_string_dynamic_t_delete_simple(buffer); - fss_basic_write_delete_data(data); - return status; + content.string = 0; + content.used = 0; + content.size = 0; + } + + if (data->parameters[fss_basic_write_parameter_file].result == f_console_result_additional) { + if (output.id != -1) { + f_file_stream_close(F_true, &output); } } - else { - f_print_dynamic(f_type_output, buffer); + + // ensure a newline is always put at the end of the program execution, unless in quiet mode. + if (data->error.verbosity != f_console_verbosity_quiet) { + if (F_status_is_error(status)) { + fprintf(data->error.to.stream, "%c", f_string_eol[0]); + } } f_macro_string_dynamic_t_delete_simple(buffer); - + f_macro_string_dynamic_t_delete_simple(object); + f_macro_string_dynamic_t_delete_simple(content); fss_basic_write_delete_data(data); return status; } #endif // _di_fss_basic_write_main_ #ifndef _di_fss_basic_write_delete_data_ - f_return_status fss_basic_write_delete_data(fss_basic_write_data *data) { + f_return_status fss_basic_write_delete_data(fss_basic_write_data_t *data) { f_status_t status = F_none; for (f_string_length_t i = 0; i < fss_basic_write_total_parameters; i++) { diff --git a/level_3/fss_basic_write/c/fss_basic_write.h b/level_3/fss_basic_write/c/fss_basic_write.h index ade55a1..2bdfcb7 100644 --- a/level_3/fss_basic_write/c/fss_basic_write.h +++ b/level_3/fss_basic_write/c/fss_basic_write.h @@ -28,6 +28,7 @@ // fll-1 includes #include #include +#include #include #include @@ -119,9 +120,9 @@ extern "C" { fll_error_print_t error; f_color_context_t context; - } fss_basic_write_data; + } fss_basic_write_data_t; - #define fss_basic_write_data_initialize \ + #define fss_basic_write_data_t_initialize \ { \ fss_basic_write_console_parameter_t_initialize, \ f_string_lengths_t_initialize, \ @@ -164,7 +165,7 @@ extern "C" { * @see fss_basic_write_delete_data() */ #ifndef _di_fss_basic_write_main_ - extern f_return_status fss_basic_write_main(const f_console_arguments_t arguments, fss_basic_write_data *data); + extern f_return_status fss_basic_write_main(const f_console_arguments_t arguments, fss_basic_write_data_t *data); #endif // _di_fss_basic_write_main_ /** @@ -182,7 +183,7 @@ extern "C" { * @see fss_basic_write_main() */ #ifndef _di_fss_basic_write_delete_data_ - extern f_return_status fss_basic_write_delete_data(fss_basic_write_data *data); + extern f_return_status fss_basic_write_delete_data(fss_basic_write_data_t *data); #endif // _di_fss_basic_write_delete_data_ #ifdef __cplusplus diff --git a/level_3/fss_basic_write/c/main.c b/level_3/fss_basic_write/c/main.c index bd67e87..4b49441 100644 --- a/level_3/fss_basic_write/c/main.c +++ b/level_3/fss_basic_write/c/main.c @@ -2,7 +2,7 @@ int main(const unsigned long argc, const f_string_t *argv) { const f_console_arguments_t arguments = { argc, argv }; - fss_basic_write_data data = fss_basic_write_data_initialize; + fss_basic_write_data_t data = fss_basic_write_data_t_initialize; if (F_status_is_error(fss_basic_write_main(arguments, &data))) { return 1; diff --git a/level_3/fss_basic_write/c/private-fss_basic_write.c b/level_3/fss_basic_write/c/private-fss_basic_write.c new file mode 100644 index 0000000..cd35d00 --- /dev/null +++ b/level_3/fss_basic_write/c/private-fss_basic_write.c @@ -0,0 +1,89 @@ +#include "fss_basic_write.h" +#include "private-fss_basic_write.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_fss_basic_write_error_parameter_same_times_print_ + void fss_basic_write_error_parameter_same_times_print(const fss_basic_write_data_t data) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sMust specify both the '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_object); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter and the '"); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_content); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter the same number of times when not specifying the "); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_basic_write_long_partial); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter.%c", f_string_eol[0]); + } +#endif // _di_fss_basic_write_error_parameter_same_times_print_ + +#ifndef _di_fss_basic_write_error_parameter_value_missing_print_ + void fss_basic_write_error_parameter_value_missing_print(const fss_basic_write_data_t data, const f_string_t symbol, const f_string_t parameter) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", symbol, parameter); + fl_color_print(data.error.to.stream, data.context.set.error, "' was specified, but no value was given.%c", f_string_eol[0]); + } +#endif // _di_fss_basic_write_error_parameter_value_missing_print_ + +#ifndef _di_fss_basic_write_process_ + f_return_status fss_basic_write_process(const fss_basic_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) { + f_status_t status = F_none; + + f_string_range_t range = f_macro_string_range_t_initialize(object.used); + + buffer->used = 0; + + if (data.parameters[fss_basic_write_parameter_object].result == f_console_result_found) { + + status = fl_fss_basic_object_write(object, quoted, &range, buffer); + + if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_object_write", F_true); + return F_status_set_error(status); + } + } + + if (data.parameters[fss_basic_write_parameter_content].result == f_console_result_found) { + if (content.used) { + range.start = 0; + range.stop = content.used; + } + else { + range.start = 1; + range.stop = 0; + } + + status = fl_fss_basic_content_write(content, &range, buffer); + + if (F_status_is_error(status) || data.parameters[fss_basic_write_parameter_content].result == f_console_result_found) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_content_write", F_true); + return F_status_set_error(status); + } + } + + status = fl_string_dynamic_terminate(buffer); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate", F_true); + return F_status_set_error(status); + } + + f_print_dynamic(output.stream, *buffer); + + return status; + } +#endif // _di_fss_basic_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_3/fss_basic_write/c/private-fss_basic_write.h b/level_3/fss_basic_write/c/private-fss_basic_write.h new file mode 100644 index 0000000..f5a9cc2 --- /dev/null +++ b/level_3/fss_basic_write/c/private-fss_basic_write.h @@ -0,0 +1,76 @@ +/** + * FLL - Level 3 + * + * Project: FSS + * API Version: 0.5 + * Licenses: lgplv2.1 + */ +#ifndef _PRIVATE_fss_basic_write_h +#define _PRIVATE_fss_basic_write_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print an message about the object and content parameters not being specified the same number of times. + * + * @param data + * The program data. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_write_error_parameter_same_times_print_ + void fss_basic_write_error_parameter_same_times_print(const fss_basic_write_data_t data) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_write_error_parameter_same_times_print_ + +/** + * Print an message about a parameter missing a value. + * + * @param data + * The program data. + * @param symbol + * The console symbol, such as "--" in "--help". + * @param parameter + * The parameter name, such as "help" in "--help". + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_write_error_parameter_value_missing_print_ + void fss_basic_write_error_parameter_value_missing_print(const fss_basic_write_data_t data, const f_string_t symbol, const f_string_t parameter) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_write_error_parameter_value_missing_print_ + +/** + * Process a given object and content, printing the FSS if valid or an error if invalid. + * + * @param data + * The program data. + * @param output + * The file to output to. + * @param object + * The object to validate and print. + * @param content + * The content to escape and print. + * @param quoted + * The quote character to use. + * This is either single our double quote. + * @param buffer + * The buffer array used as a cache to construct the output before printing. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_basic_write_process_ + extern f_return_status fss_basic_write_process(const fss_basic_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) f_gcc_attribute_visibility_internal; +#endif // _di_fss_basic_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _PRIVATE_fss_basic_write_h diff --git a/level_3/fss_basic_write/data/build/settings b/level_3/fss_basic_write/data/build/settings index 6f07cfa..333aee4 100644 --- a/level_3/fss_basic_write/data/build/settings +++ b/level_3/fss_basic_write/data/build/settings @@ -22,7 +22,7 @@ build_libraries -lc build_libraries-individual -lfll_program -lfll_fss -lfll_file -lfl_directory -lfll_execute -lfl_environment -lf_signal -lf_path -lfll_error -lfl_string -lfl_status -lfl_fss -lfl_conversion -lfl_console -lf_conversion -lfl_color -lf_print -lf_pipe -lf_fss -lf_file -lf_environment -lf_directory -lf_console -lf_utf -lf_memory build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll -build_sources_library fss_basic_write.c +build_sources_library fss_basic_write.c private-fss_basic_write.c build_sources_program main.c build_sources_headers fss_basic_write.h build_sources_script diff --git a/level_3/fss_extended_read/c/fss_extended_read.h b/level_3/fss_extended_read/c/fss_extended_read.h index b1413cc..b9bdce5 100644 --- a/level_3/fss_extended_read/c/fss_extended_read.h +++ b/level_3/fss_extended_read/c/fss_extended_read.h @@ -147,7 +147,7 @@ extern "C" { f_color_context_t context; } fss_extended_read_data_t; - #define fss_extended_read_data_initialize \ + #define fss_extended_read_data_t_initialize \ { \ fss_extended_read_console_parameter_t_initialize, \ f_string_lengths_t_initialize, \ diff --git a/level_3/fss_extended_read/c/main.c b/level_3/fss_extended_read/c/main.c index 871b543..d1a3e10 100644 --- a/level_3/fss_extended_read/c/main.c +++ b/level_3/fss_extended_read/c/main.c @@ -2,7 +2,7 @@ int main(const unsigned long argc, const f_string_t *argv) { const f_console_arguments_t arguments = { argc, argv }; - fss_extended_read_data_t data = fss_extended_read_data_initialize; + fss_extended_read_data_t data = fss_extended_read_data_t_initialize; if (f_pipe_input_exists()) { data.process_pipe = F_true; diff --git a/level_3/fss_extended_write/c/fss_extended_write.c b/level_3/fss_extended_write/c/fss_extended_write.c index 3e351c1..9d55cc2 100644 --- a/level_3/fss_extended_write/c/fss_extended_write.c +++ b/level_3/fss_extended_write/c/fss_extended_write.c @@ -1,4 +1,5 @@ #include "fss_extended_write.h" +#include "private-fss_extended_write.h" #ifdef __cplusplus extern "C" { @@ -52,7 +53,7 @@ extern "C" { if (F_status_is_error(status)) { fss_extended_write_delete_data(data); - return status; + return F_status_set_error(status); } } @@ -90,191 +91,419 @@ extern "C" { fss_extended_write_print_help(data->output, data->context); fss_extended_write_delete_data(data); - return F_none; + return status; } if (data->parameters[fss_extended_write_parameter_version].result == f_console_result_found) { fll_program_print_version(data->output, fss_extended_write_version); fss_extended_write_delete_data(data); - return F_none; + return status; } - f_array_length_t counter = 0; - bool object = data->parameters[fss_extended_write_parameter_object].result == f_console_result_found; - - f_string_dynamic_t buffer = f_string_dynamic_t_initialize; - f_string_range_t range = f_string_range_t_initialize; + f_file_t output = f_file_t_initialize; - if (data->process_pipe) { - f_file_t file = f_file_t_initialize; - f_string_dynamic_t input = f_string_dynamic_t_initialize; + output.id = f_type_descriptor_output; + output.stream = f_type_output; + output.flag = f_file_flag_create | f_file_flag_write_only | f_file_flag_append; - file.id = f_type_descriptor_input; + if (F_status_is_error_not(status)) { + if (data->parameters[fss_extended_write_parameter_file].result == f_console_result_additional) { + if (data->parameters[fss_extended_write_parameter_file].additional.used > 1) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_file); + fl_color_print(data->error.to.stream, data->context.set.error, "' may only be specified once.%c", f_string_eol[0]); + } - status = f_file_read(file, &input); + status = F_status_set_error(F_parameter); + } + else { + const f_string_length_t location = data->parameters[fss_extended_write_parameter_file].additional.array[0]; - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read", F_true, file.string, "read", fll_error_file_type_file); + output.id = -1; + output.stream = 0; + status = f_file_stream_open(arguments.argv[location], 0, &output); - f_macro_string_dynamic_t_delete_simple(input); - fss_extended_write_delete_data(data); - return status; + if (F_status_is_error(status)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.argv[location], "open", fll_error_file_type_file); + } + } } + else if (data->parameters[fss_extended_write_parameter_file].result == f_console_result_found) { + fss_extended_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_extended_write_long_file); + status = F_status_set_error(F_parameter); + } + } - if (input.used) { - range.start = 0; - range.stop = input.used - 1; - - if (object) { - status = fl_fss_extended_object_write(input, 0, &range, &buffer); - - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_extended_write_delete_data(data); - return F_status_set_error(status); + if (F_status_is_error_not(status)) { + if (data->parameters[fss_extended_write_parameter_object].locations.used || data->parameters[fss_extended_write_parameter_content].locations.used) { + if (data->parameters[fss_extended_write_parameter_object].locations.used) { + if (data->parameters[fss_extended_write_parameter_object].locations.used != data->parameters[fss_extended_write_parameter_object].additional.used) { + fss_extended_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_extended_write_long_object); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_extended_write_parameter_content].locations.used != data->parameters[fss_extended_write_parameter_content].additional.used) { + fss_extended_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_extended_write_long_content); + status = F_status_set_error(F_parameter); } + else if (data->parameters[fss_extended_write_parameter_object].locations.used != data->parameters[fss_extended_write_parameter_content].locations.used) { + fss_extended_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); + } + else if (data->parameters[fss_extended_write_parameter_content].locations.used && data->parameters[fss_extended_write_parameter_partial].locations.used) { + if (data->parameters[fss_extended_write_parameter_content].result == f_console_result_additional) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_partial); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter only allows either the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter or the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter, but not both.%c", f_string_eol[0]); + } - if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_found) { - buffer.used--; + status = F_status_set_error(F_parameter); + } } } - else { - status = fl_fss_extended_content_write(input, 0, &range, &buffer); - - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_extended_write_delete_data(data); - return F_status_set_error(status); + else if (data->parameters[fss_extended_write_parameter_content].locations.used) { + if (data->parameters[fss_extended_write_parameter_content].locations.used != data->parameters[fss_extended_write_parameter_content].additional.used) { + fss_extended_write_error_parameter_value_missing_print(*data, f_console_symbol_long_enable, fss_extended_write_long_content); + status = F_status_set_error(F_parameter); } - - // remove the last whitespace separator before possibly appending the newline. - if (buffer.used) { - buffer.used--; + else if (!data->parameters[fss_extended_write_parameter_partial].locations.used) { + fss_extended_write_error_parameter_same_times_print(*data); + status = F_status_set_error(F_parameter); } + } + } + else if (!data->process_pipe) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThis requires either piped data or the use of the '", fll_error_print_error); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_object); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter with the '"); + fl_color_print(data->error.to.stream, data->context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_content); + fl_color_print(data->error.to.stream, data->context.set.error, "' parameter.%c", f_string_eol[0]); + } - if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_none) { - if (buffer.used == buffer.size) { - f_macro_string_dynamic_t_resize(status, buffer, buffer.used + f_fss_default_allocation_step_string); + status = F_status_set_error(F_parameter); + } + } - if (F_status_is_error(status)) { - f_macro_string_dynamic_t_delete_simple(buffer); - f_macro_string_dynamic_t_delete_simple(input); - fss_extended_write_delete_data(data); - return status; - } - } + f_fss_quoted_t quoted = f_fss_delimit_quote_double; - buffer.string[buffer.used] = f_fss_extended_close; - buffer.used++; + if (F_status_is_error_not(status)) { + if (data->parameters[fss_extended_write_parameter_double].result == f_console_result_found) { + if (data->parameters[fss_extended_write_parameter_single].result == f_console_result_found) { + if (data->parameters[fss_extended_write_parameter_double].location < data->parameters[fss_extended_write_parameter_single].location) { + quoted = f_fss_delimit_quote_single; } } } - - f_macro_string_dynamic_t_delete_simple(input); + else if (data->parameters[fss_extended_write_parameter_single].result == f_console_result_found) { + quoted = f_fss_delimit_quote_single; + } } - else if (data->parameters[fss_extended_write_parameter_string].result == f_console_result_additional) { - const f_console_parameter_t *parameter = &data->parameters[fss_extended_write_parameter_string]; - f_string_dynamic_t input = f_string_dynamic_t_initialize; - if (object) { - input.string = arguments.argv[parameter->additional.array[0]]; - input.used = strnlen(input.string, f_console_length_size); + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + f_string_dynamic_t object = f_string_dynamic_t_initialize; + f_string_dynamic_t content = f_string_dynamic_t_initialize; - if (input.used) { - range.start = 0; - range.stop = input.used - 1; + if (F_status_is_error_not(status)) { + f_string_dynamic_t escaped = f_string_dynamic_t_initialize; - status = fl_fss_extended_object_write(input, 0, &range, &buffer); + if (data->process_pipe) { + f_file_t input = f_file_t_initialize; - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_extended_write_delete_data(data); - return F_status_set_error(status); - } + input.id = f_type_descriptor_input; + input.size_read = 1; - if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_found) { - buffer.used--; - } - } - } - else { - for (f_string_length_t i = 0; i < parameter->additional.used; i++) { - input.string = arguments.argv[parameter->additional.array[i]]; - input.used = strnlen(input.string, f_console_length_size); + bool object_ended = F_false; + + f_string_length_t previous = 0; + f_string_range_t range = f_string_range_t_initialize; + + range.start = 0; - if (input.used) { - range.start = 0; - range.stop = input.used - 1; + if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_found) { + for (f_status_t status_pipe = F_none; ; ) { + + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); + + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } - status = fl_fss_extended_content_write(input, 0, &range, &buffer); + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } - if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_extended_write_delete_data(data); - return F_status_set_error(status); + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (data->parameters[fss_extended_write_parameter_object].result == f_console_result_found) { + object.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + else { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + } + + status = fss_extended_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; } - } - } // for - // remove the last whitespace separator before possibly appending the newline. - if (buffer.used) { - buffer.used--; + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for } + else { + for (f_status_t status_pipe = F_none; ; ) { + + if (status_pipe != F_none_eof) { + status_pipe = f_file_read(input, &buffer); - if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_none) { - if (buffer.used == buffer.size) { - f_macro_string_dynamic_t_resize(status, buffer, buffer.used + f_fss_default_allocation_step_string); + if (F_status_is_error(status_pipe)) { + fll_error_file_print(data->error, F_status_set_fine(status), "f_file_read_to", F_true, "-", "read", fll_error_file_type_pipe); + + status = F_status_set_error(F_pipe); + break; + } + + if (!buffer.used) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has no data.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = buffer.used - 1; + } + + previous = range.start; + status = fl_string_dynamic_seek_line(buffer.string, &range); if (F_status_is_error(status)) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_extended_write_delete_data(data); - return status; + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_seek_line", F_true); + break; + } + + if (status == F_data_not_stop) { + status = F_status_set_error(F_parameter); + + fll_error_print(data->error, F_parameter, "fl_string_dynamic_seek_line", F_true); + break; + } + + if (object_ended && previous == range.start) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has incorrectly placed newlines.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + break; + } + + range.stop = range.start - 1; + range.start = previous; + + if (object_ended) { + content.used = 0; + + if (buffer.used) { + status = fl_string_dynamic_partial_append_nulless(buffer, range, &content); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + } + + status = fss_extended_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + + object_ended = F_false; + } + else { + object.used = 0; + + status = fl_string_dynamic_partial_append_nulless(buffer, range, &object); + + if (F_status_is_error(status)) { + fll_error_print(data->error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true); + break; + } + + object_ended = F_true; } - } - buffer.string[buffer.used] = f_fss_extended_close; - buffer.used++; + // restore the range, positioned after the newline. + range.start = range.stop + 2; + range.stop = buffer.used - 1; + + // only clear the buffer and reset the start when the entire buffer has been processed. + if (range.start > range.stop) { + range.start = 0; + buffer.used = 0; + } + + if (status_pipe == F_none_eof && !buffer.used && !object_ended) break; + } // for + + if (F_status_is_error_not(status) && object_ended) { + if (data->error.verbosity != f_console_verbosity_quiet) { + fl_color_print(data->error.to.stream, data->context.set.error, "%sThe pipe has an object without content.%c", fll_error_print_error, f_string_eol[0]); + } + + status = F_status_set_error(F_parameter); + } } } - status = F_none; - } + if (F_status_is_error_not(status)) { + if (data->parameters[fss_extended_write_parameter_partial].result == f_console_result_found) { + if (data->parameters[fss_extended_write_parameter_object].result == f_console_result_found) { + content.used = 0; - if (data->parameters[fss_extended_write_parameter_file].result == f_console_result_additional) { - f_file_t output = f_file_t_initialize; + for (f_array_length_t i = 0; i < data->parameters[fss_extended_write_parameter_object].additional.used; i++) { - output.flag = f_file_flag_append_wo; + object.string = arguments.argv[data->parameters[fss_extended_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; - status = f_file_open(arguments.argv[data->parameters[fss_extended_write_parameter_file].additional.array[0]], 0, &output); + status = fss_extended_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_open", F_true, file.string, "open", fll_error_file_type_file); + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + else { + object.used = 0; + + for (f_array_length_t i = 0; i < data->parameters[fss_extended_write_parameter_object].additional.used; i++) { - f_macro_string_dynamic_t_delete_simple(buffer); - fss_extended_write_delete_data(data); - return status; + content.string = arguments.argv[data->parameters[fss_extended_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; + + status = fss_extended_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + } + else { + for (f_array_length_t i = 0; i < data->parameters[fss_extended_write_parameter_object].additional.used; i++) { + + object.string = arguments.argv[data->parameters[fss_extended_write_parameter_object].additional.array[i]]; + object.used = strnlen(object.string, f_console_length_size); + object.size = object.used; + + content.string = arguments.argv[data->parameters[fss_extended_write_parameter_content].additional.array[i]]; + content.used = strnlen(content.string, f_console_length_size); + content.size = content.used; + + status = fss_extended_write_process(*data, output, object, content, quoted, &buffer); + if (F_status_is_error(status)) break; + + fprintf(output.stream, "%c", f_string_eol[0]); + } // for + } + + // ensure there is always a newline at the end, unless in quiet mode. + if (F_status_is_error_not(status) && data->error.verbosity != f_console_verbosity_quiet && data->parameters[fss_extended_write_parameter_file].result == f_console_result_none) { + fprintf(f_type_output, "%c", f_string_eol[0]); + } } - status = f_file_write(output, buffer, 0); - f_file_stream_close(F_true, &output); + f_macro_string_dynamic_t_delete_simple(escaped); - if (F_status_is_error(status)) { - fll_error_file_print(data->error, F_status_set_fine(status), "f_file_close", F_true, file.string, "close", fll_error_file_type_file); + // object and content, though being a "dynamic" type, is being used statically, so clear them up to avoid invalid free(). + object.string = 0; + object.used = 0; + object.size = 0; + + content.string = 0; + content.used = 0; + content.size = 0; + } - f_macro_string_dynamic_t_delete_simple(buffer); - fss_extended_write_delete_data(data); - return status; + if (data->parameters[fss_extended_write_parameter_file].result == f_console_result_additional) { + if (output.id != -1) { + f_file_stream_close(F_true, &output); } } - else { - f_print_dynamic(f_type_output, buffer); + + // ensure a newline is always put at the end of the program execution, unless in quiet mode. + if (data->error.verbosity != f_console_verbosity_quiet) { + if (F_status_is_error(status)) { + fprintf(data->error.to.stream, "%c", f_string_eol[0]); + } } f_macro_string_dynamic_t_delete_simple(buffer); - + f_macro_string_dynamic_t_delete_simple(object); + f_macro_string_dynamic_t_delete_simple(content); fss_extended_write_delete_data(data); return status; } diff --git a/level_3/fss_extended_write/c/private-fss_extended_write.c b/level_3/fss_extended_write/c/private-fss_extended_write.c new file mode 100644 index 0000000..9ba4fe2 --- /dev/null +++ b/level_3/fss_extended_write/c/private-fss_extended_write.c @@ -0,0 +1,89 @@ +#include "fss_extended_write.h" +#include "private-fss_extended_write.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_fss_extended_write_error_parameter_same_times_print_ + void fss_extended_write_error_parameter_same_times_print(const fss_extended_write_data_t data) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sMust specify both the '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_object); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter and the '"); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_content); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter the same number of times when not specifying the "); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", f_console_symbol_long_enable, fss_extended_write_long_partial); + fl_color_print(data.error.to.stream, data.context.set.error, "' parameter.%c", f_string_eol[0]); + } +#endif // _di_fss_extended_write_error_parameter_same_times_print_ + +#ifndef _di_fss_extended_write_error_parameter_value_missing_print_ + void fss_extended_write_error_parameter_value_missing_print(const fss_extended_write_data_t data, const f_string_t symbol, const f_string_t parameter) { + + if (data.error.verbosity == f_console_verbosity_quiet) { + return; + } + + fl_color_print(data.error.to.stream, data.context.set.error, "%sThe parameter '", fll_error_print_error); + fl_color_print(data.error.to.stream, data.context.set.notable, "%s%s", symbol, parameter); + fl_color_print(data.error.to.stream, data.context.set.error, "' was specified, but no value was given.%c", f_string_eol[0]); + } +#endif // _di_fss_extended_write_error_parameter_value_missing_print_ + +#ifndef _di_fss_extended_write_process_ + f_return_status fss_extended_write_process(const fss_extended_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) { + f_status_t status = F_none; + + f_string_range_t range = f_macro_string_range_t_initialize(object.used); + + buffer->used = 0; + + if (data.parameters[fss_extended_write_parameter_object].result == f_console_result_found) { + + status = fl_fss_extended_object_write(object, quoted, &range, buffer); + + if (F_status_is_error(status) || status == F_data_not_stop || status == F_data_not_eos) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_object_write", F_true); + return F_status_set_error(status); + } + } + + if (data.parameters[fss_extended_write_parameter_content].result == f_console_result_found) { + if (content.used) { + range.start = 0; + range.stop = content.used; + } + else { + range.start = 1; + range.stop = 0; + } + + status = fl_fss_extended_content_write(content, quoted, &range, buffer); + + if (F_status_is_error(status) || data.parameters[fss_extended_write_parameter_content].result == f_console_result_found) { + fll_error_print(data.error, F_status_set_fine(status), "fl_fss_basic_content_write", F_true); + return F_status_set_error(status); + } + } + + status = fl_string_dynamic_terminate(buffer); + + if (F_status_is_error(status)) { + fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate", F_true); + return F_status_set_error(status); + } + + f_print_dynamic(output.stream, *buffer); + + return status; + } +#endif // _di_fss_extended_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_3/fss_extended_write/c/private-fss_extended_write.h b/level_3/fss_extended_write/c/private-fss_extended_write.h new file mode 100644 index 0000000..4f06190 --- /dev/null +++ b/level_3/fss_extended_write/c/private-fss_extended_write.h @@ -0,0 +1,76 @@ +/** + * FLL - Level 3 + * + * Project: FSS + * API Version: 0.5 + * Licenses: lgplv2.1 + */ +#ifndef _PRIVATE_fss_extended_write_h +#define _PRIVATE_fss_extended_write_h + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Print an message about the object and content parameters not being specified the same number of times. + * + * @param data + * The program data. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_extended_write_error_parameter_same_times_print_ + void fss_extended_write_error_parameter_same_times_print(const fss_extended_write_data_t data) f_gcc_attribute_visibility_internal; +#endif // _di_fss_extended_write_error_parameter_same_times_print_ + +/** + * Print an message about a parameter missing a value. + * + * @param data + * The program data. + * @param symbol + * The console symbol, such as "--" in "--help". + * @param parameter + * The parameter name, such as "help" in "--help". + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_extended_write_error_parameter_value_missing_print_ + void fss_extended_write_error_parameter_value_missing_print(const fss_extended_write_data_t data, const f_string_t symbol, const f_string_t parameter) f_gcc_attribute_visibility_internal; +#endif // _di_fss_extended_write_error_parameter_value_missing_print_ + +/** + * Process a given object and content, printing the FSS if valid or an error if invalid. + * + * @param data + * The program data. + * @param output + * The file to output to. + * @param object + * The object to validate and print. + * @param content + * The content to escape and print. + * @param quoted + * The quote character to use. + * This is either single our double quote. + * @param buffer + * The buffer array used as a cache to construct the output before printing. + * + * @return + * F_none on success. + * F_failure (with error bit) for any othe failure. + */ +#ifndef _di_fss_extended_write_process_ + extern f_return_status fss_extended_write_process(const fss_extended_write_data_t data, const f_file_t output, const f_string_static_t object, const f_string_static_t content, const f_fss_quoted_t quoted, f_string_dynamic_t *buffer) f_gcc_attribute_visibility_internal; +#endif // _di_fss_extended_write_process_ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _PRIVATE_fss_extended_write_h diff --git a/level_3/fss_extended_write/data/build/settings b/level_3/fss_extended_write/data/build/settings index 9864920..04861e4 100644 --- a/level_3/fss_extended_write/data/build/settings +++ b/level_3/fss_extended_write/data/build/settings @@ -22,7 +22,7 @@ build_libraries -lc build_libraries-individual -lfll_program -lfll_fss -lfll_file -lfl_directory -lfll_execute -lfl_environment -lf_signal -lf_path -lfll_error -lfl_string -lfl_status -lfl_fss -lfl_conversion -lf_conversion -lfl_color -lf_print -lf_pipe -lf_fss -lf_file -lf_environment -lf_directory -lf_console -lf_utf -lf_memory build_libraries-level -lfll_2 -lfll_1 -lfll_0 build_libraries-monolithic -lfll -build_sources_library fss_extended_write.c +build_sources_library fss_extended_write.c private-fss_extended_write.c build_sources_program main.c build_sources_headers fss_extended_write.h build_sources_script -- 1.8.3.1