From: Kevin Day Date: Sun, 10 Jul 2022 22:10:52 +0000 (-0500) Subject: Feature: The featureless make program now supports the "write" operation. X-Git-Tag: 0.6.0~65 X-Git-Url: https://git.kevux.org/?a=commitdiff_plain;h=a6dd9c0685c1590bd6e47c5ca1c3eaff5c69f21e;p=fll Feature: The featureless make program now supports the "write" operation. 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". --- diff --git a/level_3/fake/c/private-common.c b/level_3/fake/c/private-common.c index ee30456..4f64731 100644 --- a/level_3/fake/c/private-common.c +++ b/level_3/fake/c/private-common.c @@ -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); diff --git a/level_3/fake/c/private-common.h b/level_3/fake/c/private-common.h index 0b5191b..f710baf 100644 --- a/level_3/fake/c/private-common.h +++ b/level_3/fake/c/private-common.h @@ -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" diff --git a/level_3/fake/c/private-make-operate.c b/level_3/fake/c/private-make-operate.c index 5c174d2..a1a96bd 100644 --- a/level_3/fake/c/private-make-operate.c +++ b/level_3/fake/c/private-make-operate.c @@ -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; diff --git a/level_3/fake/c/private-make-operate_process.c b/level_3/fake/c/private-make-operate_process.c index c00f8cc..13029ea 100644 --- a/level_3/fake/c/private-make-operate_process.c +++ b/level_3/fake/c/private-make-operate_process.c @@ -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) { diff --git a/level_3/fake/c/private-make-operate_process.h b/level_3/fake/c/private-make-operate_process.h index 04d9721..5d00e36 100644 --- a/level_3/fake/c/private-make-operate_process.h +++ b/level_3/fake/c/private-make-operate_process.h @@ -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 diff --git a/level_3/fake/c/private-make-operate_process_type.c b/level_3/fake/c/private-make-operate_process_type.c index 1a5c9af..4b9d5f4 100644 --- a/level_3/fake/c/private-make-operate_process_type.c +++ b/level_3/fake/c/private-make-operate_process_type.c @@ -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 diff --git a/level_3/fake/c/private-make-operate_process_type.h b/level_3/fake/c/private-make-operate_process_type.h index b1161fc..e83c655 100644 --- a/level_3/fake/c/private-make-operate_process_type.h +++ b/level_3/fake/c/private-make-operate_process_type.h @@ -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 diff --git a/level_3/fake/c/private-make-operate_validate.c b/level_3/fake/c/private-make-operate_validate.c index 55230ca..39955e5 100644 --- a/level_3/fake/c/private-make-operate_validate.c +++ b/level_3/fake/c/private-make-operate_validate.c @@ -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_ diff --git a/level_3/fake/c/private-print.c b/level_3/fake/c/private-print.c index 2e4a036..f682a57 100644 --- a/level_3/fake/c/private-print.c +++ b/level_3/fake/c/private-print.c @@ -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) { diff --git a/level_3/fake/c/private-print.h b/level_3/fake/c/private-print.h index 78398f7..931d9be 100644 --- a/level_3/fake/c/private-print.h +++ b/level_3/fake/c/private-print.h @@ -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 diff --git a/level_3/fake/documents/fakefile.txt b/level_3/fake/documents/fakefile.txt index 7f92619..bef4bf4 100644 --- a/level_3/fake/documents/fakefile.txt +++ b/level_3/fake/documents/fakefile.txt @@ -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. diff --git a/level_3/fake/specifications/fakefile.txt b/level_3/fake/specifications/fakefile.txt index 71e7c44..0669089 100644 --- a/level_3/fake/specifications/fakefile.txt +++ b/level_3/fake/specifications/fakefile.txt @@ -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.