]> Kevux Git Server - fll/commitdiff
Progress: fss write programs synchronize.
authorKevin Day <thekevinday@gmail.com>
Tue, 29 Sep 2020 02:42:02 +0000 (21:42 -0500)
committerKevin Day <thekevinday@gmail.com>
Tue, 29 Sep 2020 02:42:02 +0000 (21:42 -0500)
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.

21 files changed:
level_0/f_fss/c/fss-common.h
level_1/fl_fss/c/private-fss.c
level_2/fll_fss/c/fss_basic_list.c
level_2/fll_fss/c/fss_basic_list.h
level_3/fss_basic_list_write/c/fss_basic_list_write.c
level_3/fss_basic_list_write/c/fss_basic_list_write.h
level_3/fss_basic_list_write/c/private-fss_basic_list_write.c [new file with mode: 0644]
level_3/fss_basic_list_write/c/private-fss_basic_list_write.h [new file with mode: 0644]
level_3/fss_basic_list_write/data/build/settings
level_3/fss_basic_write/c/fss_basic_write.c
level_3/fss_basic_write/c/fss_basic_write.h
level_3/fss_basic_write/c/main.c
level_3/fss_basic_write/c/private-fss_basic_write.c [new file with mode: 0644]
level_3/fss_basic_write/c/private-fss_basic_write.h [new file with mode: 0644]
level_3/fss_basic_write/data/build/settings
level_3/fss_extended_read/c/fss_extended_read.h
level_3/fss_extended_read/c/main.c
level_3/fss_extended_write/c/fss_extended_write.c
level_3/fss_extended_write/c/private-fss_extended_write.c [new file with mode: 0644]
level_3/fss_extended_write/c/private-fss_extended_write.h [new file with mode: 0644]
level_3/fss_extended_write/data/build/settings

index ad84a76ad2eff25dbfc0970c70281d7ff32ac715..4d183cecf0cd2c2e5f5a26b6b1f44d2ff15a2e8c 100644 (file)
@@ -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'
index 7fd97704a973ea26dbe5a8a991192c404f39f38f..62d6566564980d0d3ca7561dfd6c4f6a465d0bcd 100644 (file)
@@ -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);
index ddb64957720aae0b310142253e45ef3716e3d296..44a35fdbcf3bda4c5e2e355b9802fba4e533ea2a 100644 (file)
@@ -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;
index 7e600b8fa9f5c7ff39514daf2861537861c79945..1d07f19c5ee73c2714360e38b6e07fdc4d76fa0c 100644 (file)
@@ -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
index 3be7ab7dfe2a67617aacfcd4cab2546fbdd179ac..66cbe79b082ab001ba8c39a03236d4ae97206e6e 100644 (file)
@@ -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;
   }
index c5fab012688704c517291d051deca65553e9b89e..1a1850f3f5c4540ffeab9dc9dc985a3170474192 100644 (file)
@@ -28,8 +28,8 @@
 // fll-1 includes
 #include <level_1/color.h>
 #include <level_1/console.h>
-#include <level_1/status.h>
 #include <level_1/fss_basic_list.h>
+#include <level_1/status.h>
 #include <level_1/string.h>
 
 // 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 (file)
index 0000000..664b62d
--- /dev/null
@@ -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 (file)
index 0000000..4224ae4
--- /dev/null
@@ -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
index 62e08fb34b85790fd61c4c4f09148e983ebbe9f9..8348d98f2d07be24948eac8bcbc1431684222372 100644 (file)
@@ -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
index a5ab8807995e1d7b4547944ddb0b73947d2e1384..38e7ef72fae1babab82dfca8cf976bc115da50b4 100644 (file)
@@ -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++) {
index ade55a167e6110b888638047ab3e883760994c71..2bdfcb732921dcd3f8df70695375aae36ccc5e7e 100644 (file)
@@ -28,6 +28,7 @@
 // fll-1 includes
 #include <level_1/color.h>
 #include <level_1/console.h>
+#include <level_1/fss_basic.h>
 #include <level_1/status.h>
 #include <level_1/string.h>
 
@@ -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
index bd67e877879356715253d20937accf6de0846e5c..4b494418731a539a0cc33a3f02489cc25421b102 100644 (file)
@@ -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 (file)
index 0000000..cd35d00
--- /dev/null
@@ -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 (file)
index 0000000..f5a9cc2
--- /dev/null
@@ -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
index 6f07cfa0bf4b40ace858788bf6dc0c11d4137761..333aee4b52f6634379d0b618b6176d085e7b1739 100644 (file)
@@ -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
index b1413cc1737a1d3e0c3abd158d077715ff2f7291..b9bdce54d738ac9232bd412e23952513ac357ac3 100644 (file)
@@ -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, \
index 871b543082b804fbb9b28ebbff15aa1103ab3400..d1a3e10fb1097a603bcefdeccd99609852bdc0f4 100644 (file)
@@ -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;
index 3e351c1e4b5168edd888d51a7dca79019c3f392c..9d55cc2749ea07cd2827a0c519101da7790360dc 100644 (file)
@@ -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 (file)
index 0000000..9ba4fe2
--- /dev/null
@@ -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 (file)
index 0000000..4f06190
--- /dev/null
@@ -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
index 98649201787046ea9f1e50e94cd651c4f0bca913..04861e459fc43f7fc6fcb2d161bb215afbc9db86 100644 (file)
@@ -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