]> Kevux Git Server - fll/commitdiff
Feature: The featureless make program now supports the "write" operation.
authorKevin Day <thekevinday@gmail.com>
Sun, 10 Jul 2022 22:10:52 +0000 (17:10 -0500)
committerKevin Day <thekevinday@gmail.com>
Sun, 10 Jul 2022 22:17:18 +0000 (17:17 -0500)
This is an oversight on my part.
There should be an operation to write to a file.

There are two forms of this new "write" operation.
1) Truncate a file (deletes all data within a file).
2) Append to a file.

A file is created if it does not already exist in both cases.

The "write" operation supports some standard escape sequences as well as some non-standard ones.

Standard Escape Sequences:
- "\f": Form Feed.
- "\n": New Line.
- "\r": Carriage Return.
- "\t": Tab.
- "\v": Vertical Tab.
- "\\": Backslash Character (may require additional slashes in certain circumstances.)
- "\0": NULL Character.

Non-Standard Escape Sequences:
- "\U+": Unicode Sequence (followed by a valid Unicode sequence with a minimum 4 hexidecimal digits and a maximum of 6 hexidecimal digits).
- "\U-": Terminate a Unicode Sequence, allowing for "\U+000A\U-5" to be equivalent to "\n5".

12 files changed:
level_3/fake/c/private-common.c
level_3/fake/c/private-common.h
level_3/fake/c/private-make-operate.c
level_3/fake/c/private-make-operate_process.c
level_3/fake/c/private-make-operate_process.h
level_3/fake/c/private-make-operate_process_type.c
level_3/fake/c/private-make-operate_process_type.h
level_3/fake/c/private-make-operate_validate.c
level_3/fake/c/private-print.c
level_3/fake/c/private-print.h
level_3/fake/documents/fakefile.txt
level_3/fake/specifications/fakefile.txt

index ee3045636aa4e7349841e2d6986cb79ad0baddac..4f647312823bc92a88baf9aba28697c6e4119a00 100644 (file)
@@ -207,6 +207,7 @@ extern "C" {
   const f_string_static_t fake_make_operation_to_s = macro_f_string_static_t_initialize(FAKE_make_operation_to_s, 0, FAKE_make_operation_to_s_length);
   const f_string_static_t fake_make_operation_top_s = macro_f_string_static_t_initialize(FAKE_make_operation_top_s, 0, FAKE_make_operation_top_s_length);
   const f_string_static_t fake_make_operation_touch_s = macro_f_string_static_t_initialize(FAKE_make_operation_touch_s, 0, FAKE_make_operation_touch_s_length);
+  const f_string_static_t fake_make_operation_write_s = macro_f_string_static_t_initialize(FAKE_make_operation_write_s, 0, FAKE_make_operation_write_s_length);
 
   const f_string_static_t fake_make_operation_argument_environment_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_environment_s, 0, FAKE_make_operation_argument_environment_s_length);
   const f_string_static_t fake_make_operation_argument_failure_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_failure_s, 0, FAKE_make_operation_argument_failure_s_length);
index 0b5191bd7d4cb75daba141b61af05771ec527303..f710bafddc8ad64f0e9f994f27f86dcabe94e825 100644 (file)
@@ -1208,6 +1208,7 @@ extern "C" {
   #define FAKE_make_operation_to_s        "to"
   #define FAKE_make_operation_top_s       "top"
   #define FAKE_make_operation_touch_s     "touch"
+  #define FAKE_make_operation_write_s     "write"
 
   #define FAKE_make_operation_and_s_length       3
   #define FAKE_make_operation_break_s_length     5
@@ -1243,6 +1244,7 @@ extern "C" {
   #define FAKE_make_operation_to_s_length        2
   #define FAKE_make_operation_top_s_length       3
   #define FAKE_make_operation_touch_s_length     5
+  #define FAKE_make_operation_write_s_length     5
 
   extern const f_string_static_t fake_make_operation_and_s;
   extern const f_string_static_t fake_make_operation_break_s;
@@ -1278,6 +1280,7 @@ extern "C" {
   extern const f_string_static_t fake_make_operation_to_s;
   extern const f_string_static_t fake_make_operation_top_s;
   extern const f_string_static_t fake_make_operation_touch_s;
+  extern const f_string_static_t fake_make_operation_write_s;
 
   enum {
     fake_make_operation_type_none_e = 0,
@@ -1315,9 +1318,11 @@ extern "C" {
     fake_make_operation_type_to_e,
     fake_make_operation_type_top_e,
     fake_make_operation_type_touch_e,
+    fake_make_operation_type_write_e,
   };
 
-  #define fake_make_operation_total_d 34
+  // Total does not include "fake_make_operation_type_none_e".
+  #define fake_make_operation_total_d 35
 
   #define FAKE_make_operation_argument_environment_s    "environment"
   #define FAKE_make_operation_argument_failure_s        "failure"
index 5c174d2db1ae86c1959a9eed9788a3682ef91dbb..a1a96bdc529e2e59c510c0afecff4d61e5da5c06 100644 (file)
@@ -1082,6 +1082,7 @@ extern "C" {
       fake_make_operation_to_s,
       fake_make_operation_top_s,
       fake_make_operation_touch_s,
+      fake_make_operation_write_s,
     };
 
     const uint8_t operations_type[] = {
@@ -1119,6 +1120,7 @@ extern "C" {
       fake_make_operation_type_to_e,
       fake_make_operation_type_top_e,
       fake_make_operation_type_touch_e,
+      fake_make_operation_type_write_e,
     };
 
     fake_state_process_t state_process = fake_state_process_t_initialize;
index c00f8cc114e3524443f9ce0d491e267f56f15871..13029eaefbe5a5b9025d328ec4b84b16c834456a 100644 (file)
@@ -455,10 +455,176 @@ extern "C" {
       *status = fake_make_operate_process_type_touch(data_make, arguments);
     }
 
+    if (state_process->operation == fake_make_operation_type_write_e) {
+      *status = fake_make_operate_process_type_write(data_make, arguments);
+    }
+
     return 0;
   }
 #endif // _di_fake_make_operate_process_
 
+#ifndef _di_fake_make_operate_process_buffer_escape_
+  f_status_t fake_make_operate_process_buffer_escape(fake_make_data_t * const data_make, const f_string_static_t source, f_string_dynamic_t * const destination) {
+
+    destination->used = 0;
+
+    if (!source.used) return F_data_not;
+
+    f_status_t status = f_string_dynamic_increase_by(source.used, destination);
+    if (F_status_is_error(status)) return status;
+
+    for (f_array_length_t i = 0; i < source.used; ++i) {
+
+      // NULL characters are from delimited characters and must be skipped.
+      if (!source.string[i]) continue;
+
+      if (source.string[i] == f_string_ascii_slash_backward_s.string[0]) {
+
+        // A slash by itself at the end of the string is invalid.
+        if (++i >= source.used) break;
+
+        status = f_string_dynamic_increase_by(F_memory_default_allocation_small_d, destination);
+        if (F_status_is_error(status)) return status;
+
+        if (source.string[i] == f_string_ascii_slash_backward_s.string[0]) {
+          destination->string[destination->used++] = f_string_ascii_slash_backward_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_n_s.string[0]) {
+          destination->string[destination->used++] = f_string_ascii_feed_line_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_r_s.string[0]) {
+          destination->string[destination->used++] = f_string_ascii_return_carriage_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_t_s.string[0]) {
+          destination->string[destination->used++] = f_string_ascii_tab_horizontal_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_v_s.string[0]) {
+          destination->string[destination->used++] = f_string_ascii_tab_vertical_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_0_s.string[0]) {
+          destination->string[destination->used++] = f_string_null_s.string[0];
+        }
+        else if (source.string[i] == f_string_ascii_U_s.string[0]) {
+
+          // At the end of the string before a \U+XXXX sequence is completed is invalid.
+          if (++i >= source.used) break;
+
+          if (source.string[i] == f_string_ascii_plus_s.string[0]) {
+
+            // At the end of the string before a \U+XXXX sequence is completed is invalid.
+            if (i + 4 >= source.used) break;
+
+            ++i;
+
+            // The max Unicode sequence length is "U+XXXXXX".
+            char buffer_string[9] = { f_string_ascii_U_s.string[0], f_string_ascii_plus_s.string[0], 0, 0, 0, 0, 0, 0, 0 };
+            f_string_static_t buffer = macro_f_string_static_t_initialize(buffer_string, 0, 2);
+
+            for (uint8_t j = 2; i < source.used && j < 8; ) {
+
+              if (!isdigit(source.string[i])) {
+                if (!(source.string[i] == f_string_ascii_A_s.string[0] ||
+                      source.string[i] == f_string_ascii_B_s.string[0] ||
+                      source.string[i] == f_string_ascii_C_s.string[0] ||
+                      source.string[i] == f_string_ascii_D_s.string[0] ||
+                      source.string[i] == f_string_ascii_E_s.string[0] ||
+                      source.string[i] == f_string_ascii_F_s.string[0])) {
+
+                  if (!(source.string[i] == f_string_ascii_a_s.string[0] ||
+                        source.string[i] == f_string_ascii_b_s.string[0] ||
+                        source.string[i] == f_string_ascii_c_s.string[0] ||
+                        source.string[i] == f_string_ascii_d_s.string[0] ||
+                        source.string[i] == f_string_ascii_e_s.string[0] ||
+                        source.string[i] == f_string_ascii_f_s.string[0])) {
+
+                    --i;
+
+                    break;
+                  }
+                }
+              }
+
+              buffer_string[j++] = source.string[i++];
+              ++buffer.used;
+            } // for
+
+            if (buffer.used > 2) {
+              f_utf_char_t codepoint = 0;
+
+              status = f_utf_unicode_string_to(buffer.string, buffer.used, &codepoint);
+
+              if (F_status_is_error(status)) {
+                status = F_status_set_fine(status);
+
+                if (!(status == F_failure || status == F_utf_not || status == F_complete_not_utf || status == F_utf_fragment || status == F_valid_not)) {
+                  status = F_status_set_error(status);
+
+                  break;
+                }
+              }
+              else {
+
+                // Reserve 4-bytes (the max size of a Unicode UTF-8 sequence).
+                status = f_string_dynamic_increase_by(4, destination);
+                if (F_status_is_error(status)) return status;
+
+                if (!codepoint) {
+                  destination->string[destination->used++] = f_string_null_s.string[0];
+                }
+                else {
+                  {
+                    f_string_t address = destination->string + destination->used;
+
+                    status = f_utf_unicode_from(codepoint, 4, &address);
+                  }
+
+                  if (F_status_is_error(status)) {
+                    destination->string[destination->used] = 0;
+                  }
+                  else {
+                    if (codepoint < 0x80) {
+                      destination->used += 1;
+                    }
+                    else if (codepoint < 0x800) {
+                      destination->used += 2;
+                    }
+                    else if (codepoint < 0x10000) {
+                      destination->used += 3;
+                    }
+                    else {
+                      destination->used += 4;
+                    }
+                  }
+                }
+              }
+            }
+          }
+          else if (source.string[i] == f_string_ascii_minus_s.string[0]) {
+
+            // The "\U-" designates the termination of a Unicode sequence.
+          }
+          else {
+
+            // No plus found, so only the "\U" is considered invalid.
+            // This character is to be printed.
+            --i;
+          }
+        }
+      }
+      else {
+        status = f_string_dynamic_increase_by(F_memory_default_allocation_small_d, destination);
+        if (F_status_is_error(status)) return status;
+
+        destination->string[destination->used++] = source.string[i];
+      }
+    } // for
+
+    if (F_status_is_error(status)) return status;
+
+    return F_none;
+  }
+#endif // _di_fake_make_operate_process_buffer_escape_
+
 #ifndef _di_fake_make_operate_process_execute_
   f_status_t fake_make_operate_process_execute(fake_make_data_t * const data_make, const f_string_static_t program, const f_string_statics_t arguments, const bool as_shell) {
 
index 04d97210ea3543b1adf42056f40d6092626c0f01..5d00e3617c8cca5e86e746397454739c73784bc4 100644 (file)
@@ -41,6 +41,52 @@ extern "C" {
 #endif // _di_fake_make_operate_process_
 
 /**
+ * Process the given string, converting escape sequences into code.
+ *
+ * The following escape sequences are supported for printing special characters:
+ *   - "\f": Form Feed.
+ *   - "\n": New Line.
+ *   - "\r": Carriage Return.
+ *   - "\t": Tab.
+ *   - "\v": Vertical Tab.
+ *   - "\\": Backslash Character (may require additional slashes in certain circumstances.)
+ *   - "\0": NULL Character.
+ *   - "\U+": Unicode Sequence (followed by a valid Unicode sequence with a minimum 4 hexidecimal digits and a maximum of 6 hexidecimal digits).
+ *   - "\U-": Terminate a Unicode Sequence, allowing for "\U+000A\U-5" to be equivalent to "\n5".
+ *
+ * If the Unicode is invalid, then nothing is copied for that character (the invalid character is skipped when printing).
+ * Example Unicodes\:
+ *   - "\U+000A": Prints a new line, equivalent to "\n".
+ *   - "\U+2E19": Prints the Unicode feather-like character "⸙".
+ *
+ * Only ASCII alpha-numeric hexidecimal digits are allowed in the Unicode sequence (upper or lower case).
+ *
+ * Invalid or unknown escape sequences are not copied.
+ *
+ * @param data_make
+ *   All make related setting data, including data from the fakefile and the build settings file.
+ * @param source
+ *   The source string to process and esacpe.
+ * @param destination
+ *   The processed and escaped string.
+ *
+ * @return
+ *   F_none on success.
+ *   F_data_not if source.used is 0.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_increase_by().
+ *   Errors (with error bit) from: f_utf_unicode_from().
+ *   Errors (with error bit) from: f_utf_unicode_string_to().
+ *
+ * @see f_string_dynamic_increase_by()
+ * @see f_utf_unicode_from()
+ * @see f_utf_unicode_string_to()
+ */
+#ifndef _di_fake_make_operate_process_buffer_escape_
+  extern f_status_t fake_make_operate_process_buffer_escape(fake_make_data_t * const data_make, const f_string_static_t source, f_string_dynamic_t * const destination) F_attribute_visibility_internal_d;
+#endif // _di_fake_make_operate_process_buffer_escape_
+
+/**
  * Execute either the run operation or the shell operation.
  *
  * @param data_make
index 1a5c9af68ea1657150ed11b08bff843dd177c9f7..4b9d5f46006aa3aca389288ed6d863ac798f8125 100644 (file)
@@ -3,6 +3,7 @@
 #include "private-fake.h"
 #include "private-clean.h"
 #include "private-make.h"
+#include "private-make-operate_process.h"
 #include "private-make-operate_process_type.h"
 #include "private-print.h"
 
@@ -1464,6 +1465,96 @@ extern "C" {
   }
 #endif // _di_fake_make_operate_process_type_touch_
 
+#ifndef _di_fake_make_operate_process_type_write_
+  f_status_t fake_make_operate_process_type_write(fake_make_data_t * const data_make, const f_string_dynamics_t arguments) {
+
+    f_status_t status = F_none;
+    f_file_t file = f_file_t_initialize;
+
+    status = f_file_exists(arguments.array[0], F_true);
+
+    if (arguments.used == 1 || status == F_false) {
+      status = f_file_stream_open(arguments.array[0], f_file_open_mode_truncate_s, &file);
+
+      if (F_status_is_error(status)) {
+        if (F_status_is_error_not(fll_path_canonical(arguments.array[0], &data_make->path_cache))) {
+          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_open", F_true, data_make->path_cache, f_file_operation_open_s, fll_error_file_type_file_e);
+        }
+        else {
+          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.array[0], f_file_operation_open_s, fll_error_file_type_file_e);
+        }
+      }
+
+      if (F_status_is_error_not(status)) {
+
+        // Keep the stream open if there is a string to write to it.
+        if (arguments.used > 1) {
+          status = F_false;
+        }
+        else {
+          f_file_stream_close(F_true, &file);
+        }
+      }
+    }
+
+    if (F_status_is_error_not(status) && arguments.used > 1) {
+      if (status != F_false) {
+        status = f_file_stream_open(arguments.array[0], f_file_open_mode_append_s, &file);
+
+        if (F_status_is_error(status)) {
+          if (F_status_is_error_not(fll_path_canonical(arguments.array[0], &data_make->path_cache))) {
+            fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_open", F_true, data_make->path_cache, f_file_operation_open_s, fll_error_file_type_file_e);
+          }
+          else {
+            fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_open", F_true, arguments.array[0], f_file_operation_open_s, fll_error_file_type_file_e);
+          }
+        }
+      }
+
+      if (F_status_is_error_not(status)) {
+        for (f_array_length_t i = 1; i < arguments.used; ++i) {
+
+          status = fake_make_operate_process_buffer_escape(data_make, arguments.array[i], &data_make->cache_1);
+
+          if (F_status_is_error(status)) {
+            if (F_status_is_error_not(fll_path_canonical(arguments.array[0], &data_make->path_cache))) {
+              fll_error_file_print(data_make->error, F_status_set_fine(status), "fake_make_operate_process_buffer_escape", F_true, data_make->path_cache, f_file_operation_write_s, fll_error_file_type_file_e);
+            }
+            else {
+              fll_error_file_print(data_make->error, F_status_set_fine(status), "fake_make_operate_process_buffer_escape", F_true, arguments.array[0], f_file_operation_write_s, fll_error_file_type_file_e);
+            }
+
+            break;
+          }
+
+          status = f_file_stream_write(file, data_make->cache_1, 0);
+
+          if (F_status_is_error(status)) {
+            if (F_status_is_error_not(fll_path_canonical(arguments.array[0], &data_make->path_cache))) {
+              fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_write", F_true, data_make->path_cache, f_file_operation_write_s, fll_error_file_type_file_e);
+            }
+            else {
+              fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_stream_write", F_true, arguments.array[0], f_file_operation_write_s, fll_error_file_type_file_e);
+            }
+
+            break;
+          }
+
+          if (i + 1 < arguments.used) {
+            status = f_file_stream_write(file, f_string_ascii_space_s, 0);
+          }
+        } // for
+      }
+
+      f_file_stream_close(F_true, &file);
+    }
+
+    if (F_status_is_error(status)) return status;
+
+    return F_none;
+  }
+#endif // _di_fake_make_operate_process_type_write_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index b1161fc03df4eb6049e032ff68eebcfccf94a787..e83c655dec89c4a8037837cc5dc8d0e30ebb2e45 100644 (file)
@@ -501,6 +501,30 @@ extern "C" {
   extern f_status_t fake_make_operate_process_type_touch(fake_make_data_t * const data_make, const f_string_dynamics_t arguments) F_attribute_visibility_internal_d;
 #endif // _di_fake_make_operate_process_type_touch_
 
+/**
+ * Perform the write operation process.
+ *
+ * @param data_make
+ *   All make related setting data, including data from the fakefile and the build settings file.
+ *   This resets and uses data_make.cache_1.
+ * @param arguments
+ *   The arguments for the run or shell operation.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: f_file_exists().
+ *   Errors (with error bit) from: f_file_stream_open().
+ *   Errors (with error bit) from: f_file_stream_write().
+ *
+ * @see f_file_exists()
+ * @see f_file_stream_open()
+ * @see f_file_stream_write()
+ */
+#ifndef _di_fake_make_operate_process_type_write_
+  extern f_status_t fake_make_operate_process_type_write(fake_make_data_t * const data_make, const f_string_dynamics_t arguments) F_attribute_visibility_internal_d;
+#endif // _di_fake_make_operate_process_type_write_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 55230ca6848f028c12ab490e40a442b88d6b2f46..39955e50eac37cf8ea06d67054cc39f95351cc88 100644 (file)
@@ -1394,6 +1394,32 @@ extern "C" {
       }
     }
 
+    if (state_process->operation == fake_make_operation_type_write_e) {
+      if (arguments.used) {
+        if (!arguments.array[0].used) {
+          fake_print_error_argument_empty(data_make, 1);
+
+          *status = F_status_set_error(F_failure);
+        }
+        else {
+          *status = fake_make_assure_inside_project(data_make, arguments.array[0]);
+
+          if (F_status_is_error(*status)) {
+            fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(*status), "fake_make_assure_inside_project", data_make->path_cache.used ? data_make->path_cache : arguments.array[0]);
+
+            if (F_status_set_fine(*status) == F_false) {
+              *status = F_status_set_error(F_failure);
+            }
+          }
+        }
+      }
+      else {
+        fake_print_error_requires_more_arguments(data_make);
+
+        *status = F_status_set_error(F_failure);
+      }
+    }
+
     // Note: there is nothing to validate for fake_make_operation_type_print_e.
   }
 #endif // _di_fake_make_operate_validate_
index 2e4a0360141791bfd219f2fd3a2db5e513ee5071..f682a57ce875b60d75e55b2d157986553f953fa6 100644 (file)
@@ -348,6 +348,22 @@ extern "C" {
   }
 #endif // _di_fake_print_error_too_many_arguments_
 
+#ifndef _di_fake_print_error_argument_empty_
+  void fake_print_error_argument_empty(fake_make_data_t * const data_make, const f_array_length_t index) {
+
+    if (data_make->error.verbosity == f_console_verbosity_quiet_e) return;
+    if (!data_make->error.to.stream) return;
+
+    flockfile(data_make->error.to.stream);
+
+    fl_print_format("%r%[%QThe %]", data_make->error.to.stream, f_string_eol_s, data_make->error.context, data_make->error.prefix, data_make->error.context);
+    fl_print_format("%[%un%]", data_make->error.to.stream, data_make->error.notable, index, data_make->error.notable);
+    fl_print_format("%[ argument must not be an empty string.%]%r", data_make->error.to.stream, data_make->error.context, data_make->error.context, f_string_eol_s);
+
+    funlockfile(data_make->error.to.stream);
+  }
+#endif // _di_fake_print_error_argument_empty_
+
 #ifndef _di_fake_print_message_section_operation_failed_
   void fake_print_message_section_operation_failed(fake_data_t * const data, const fl_print_t print, const f_string_static_t buffer, const f_string_range_t section_name, const f_string_range_t operation_name) {
 
index 78398f78d6e66de525949f0059994588a13fcc8d..931d9be357abd3da5a33fa0ac860305fd524427f 100644 (file)
@@ -161,6 +161,20 @@ extern "C" {
 #endif // _di_fake_print_error_too_many_arguments_
 
 /**
+ * Print an error message for when an argument is an empty string.
+ *
+ * @param data_make
+ *   All make related setting data, including data from the fakefile and the build settings file.
+ * @param index
+ *   The index of the argument that is an empty string.
+ *
+ * @see fll_print_format()
+ */
+#ifndef _di_fake_print_error_argument_empty_
+  extern void fake_print_error_argument_empty(fake_make_data_t * const data_make, const f_array_length_t index) F_attribute_visibility_internal_d;
+#endif // _di_fake_print_error_argument_empty_
+
+/**
  * Print error messages when processing some fakefile section, for a specific line and operation, and that operation failed.
  *
  * @param data
index 7f926197b28eba6841c3f7acc4a058c078cacfcb..bef4bf44d22a07f7e157c33576d303a5a6d0ed5f 100644 (file)
@@ -441,6 +441,42 @@ Fakefile Documentation:
       The first Content must be either "file" or "directory".
       The remaining Content must be a path to the file.
 
+    - write\:
+      Write strings to a file within the project root.
+      The Content after the first Content is appended to the file.
+
+      The first Content represents the file to write to.
+      If there is no Content beyond the first, then the file is truncated (all content within the file is deleted).
+
+      In all cases, if the file does not exist, the file is created.
+
+      When only the first Content exists, this acts similar to the "touch" operation.
+      The major difference between the two is that the "touch" operation does not alter the content within the file.
+      This does alter the content within the file.
+
+      A single space is printed between each argument.
+      To preserve spaces, wrap the message in quotes (single or double).
+
+      The following escape sequences are supported for printing special characters:
+      - "\f": Form Feed.
+      - "\n": New Line.
+      - "\r": Carriage Return.
+      - "\t": Tab.
+      - "\v": Vertical Tab.
+      - "\\": Backslash Character (may require additional slashes in certain circumstances.)
+      - "\0": NULL Character.
+      - "\U+": Unicode Sequence (followed by a valid Unicode sequence with a minimum 4 hexidecimal digits and a maximum of 6 hexidecimal digits).
+      - "\U-": Terminate a Unicode Sequence, allowing for "\U+000A\U-5" to be equivalent to "\n5".
+
+      If the Unicode is invalid, then nothing is printed for that character (the invalid character is skipped when printing).
+      Example Unicodes\:
+      - "\U+000A": Prints a new line, equivalent to "\n".
+      - "\U+2E19": Prints the Unicode feather-like character "⸙".
+
+      Only ASCII alpha-numeric hexidecimal digits are allowed in the Unicode sequence (upper or lower case).
+
+      Invalid or unknown escape sequences are not printed.
+
   The IKI vocabulary context is supported and is further clarified as follows\:
   - context\:
     The context Object represents a name representing simple context or complex combination of context.
index 71e7c44c1b1df7ee5889b6ee9b8bc47d8c64f3c0..0669089731d4683c325727635f6ebe83f5abdb80 100644 (file)
@@ -75,7 +75,8 @@ Fakefile Specification:
   - skeleton: Zero Content.
   - to: One Content. First Content is the directory path.
   - top: Zero Content.
-  - touch: Two or more Content. First content is one of "file" or "directory", remaining Content are paths to files.
+  - touch: Two or more Content. First Content is one of "file" or "directory", remaining Content are paths to files.
+  - write: One or more Content. First Content the file to write to, remaining Content represent the string to write.
 
   The "if" Section Operation conditions are\:
   - ==: Two or more Content.