]> Kevux Git Server - fll/commitdiff
Bugfix: Link operation is incorrect, support new link arguments, and clone and copy...
authorKevin Day <thekevinday@gmail.com>
Sat, 23 Jul 2022 22:41:30 +0000 (17:41 -0500)
committerKevin Day <thekevinday@gmail.com>
Sat, 23 Jul 2022 22:56:51 +0000 (17:56 -0500)
The link operation target path is relative to the point path.
The previous code is trying to treat the target path in isolation.
Change the behavior to make the target path relative to the point path.

Add two new options to make creating symbolic linking easier to use:
1) "force": Used to forcibly overwrite an existing file or directory.
2) "strict": Used to require the target path to exist when creating the symbolic links.

The clone and copy operations now have better error return code processing.

level_3/fake/c/private-common.c
level_3/fake/c/private-common.h
level_3/fake/c/private-make-operate_process_type.c
level_3/fake/c/private-make-operate_validate_type.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 46559a2a96a971b3d1166221f81609bce23494ed..74b9df12b7467d38b00c5b4a4bfa953279d2a827 100644 (file)
@@ -209,12 +209,13 @@ extern "C" {
   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);
-  const f_string_static_t fake_make_operation_argument_file_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_file_s, 0, FAKE_make_operation_argument_file_s_length);
   const f_string_static_t fake_make_operation_argument_directory_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_directory_s, 0, FAKE_make_operation_argument_directory_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_error_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_error_s, 0, FAKE_make_operation_argument_error_s_length);
   const f_string_static_t fake_make_operation_argument_exit_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_exit_s, 0, FAKE_make_operation_argument_exit_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);
+  const f_string_static_t fake_make_operation_argument_file_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_file_s, 0, FAKE_make_operation_argument_file_s_length);
+  const f_string_static_t fake_make_operation_argument_force_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_force_s, 0, FAKE_make_operation_argument_force_s_length);
   const f_string_static_t fake_make_operation_argument_has_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_has_s, 0, FAKE_make_operation_argument_has_s_length);
   const f_string_static_t fake_make_operation_argument_ignore_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_ignore_s, 0, FAKE_make_operation_argument_ignore_s_length);
   const f_string_static_t fake_make_operation_argument_is_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_is_s, 0, FAKE_make_operation_argument_is_s_length);
@@ -222,6 +223,7 @@ extern "C" {
   const f_string_static_t fake_make_operation_argument_parameter_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_parameter_s, 0, FAKE_make_operation_argument_parameter_s_length);
   const f_string_static_t fake_make_operation_argument_point_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_point_s, 0, FAKE_make_operation_argument_point_s_length);
   const f_string_static_t fake_make_operation_argument_recursive_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_recursive_s, 0, FAKE_make_operation_argument_recursive_s_length);
+  const f_string_static_t fake_make_operation_argument_strict_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_strict_s, 0, FAKE_make_operation_argument_strict_s_length);
   const f_string_static_t fake_make_operation_argument_success_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_success_s, 0, FAKE_make_operation_argument_success_s_length);
   const f_string_static_t fake_make_operation_argument_target_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_target_s, 0, FAKE_make_operation_argument_target_s_length);
   const f_string_static_t fake_make_operation_argument_warn_s = macro_f_string_static_t_initialize(FAKE_make_operation_argument_warn_s, 0, FAKE_make_operation_argument_warn_s_length);
index a5299ad7c531764d34d12f581a52162c04fc1143..4d372d19d0005d8ee3eb774371a1bfae05acf103 100644 (file)
@@ -1324,12 +1324,13 @@ extern "C" {
   // 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"
-  #define FAKE_make_operation_argument_file_s           "file"
   #define FAKE_make_operation_argument_directory_s      "directory"
+  #define FAKE_make_operation_argument_environment_s    "environment"
   #define FAKE_make_operation_argument_error_s          "error"
   #define FAKE_make_operation_argument_exit_s           "exit"
+  #define FAKE_make_operation_argument_failure_s        "failure"
+  #define FAKE_make_operation_argument_file_s           "file"
+  #define FAKE_make_operation_argument_force_s          "force"
   #define FAKE_make_operation_argument_has_s            "has"
   #define FAKE_make_operation_argument_ignore_s         "ignore"
   #define FAKE_make_operation_argument_is_s             "is"
@@ -1337,16 +1338,18 @@ extern "C" {
   #define FAKE_make_operation_argument_parameter_s      "parameter"
   #define FAKE_make_operation_argument_point_s          "point"
   #define FAKE_make_operation_argument_recursive_s      "recursive"
+  #define FAKE_make_operation_argument_strict_s         "strict"
   #define FAKE_make_operation_argument_success_s        "success"
   #define FAKE_make_operation_argument_target_s         "target"
   #define FAKE_make_operation_argument_warn_s           "warn"
 
-  #define FAKE_make_operation_argument_environment_s_length    11
-  #define FAKE_make_operation_argument_failure_s_length        7
-  #define FAKE_make_operation_argument_file_s_length           4
   #define FAKE_make_operation_argument_directory_s_length      9
+  #define FAKE_make_operation_argument_environment_s_length    11
   #define FAKE_make_operation_argument_error_s_length          5
   #define FAKE_make_operation_argument_exit_s_length           4
+  #define FAKE_make_operation_argument_failure_s_length        7
+  #define FAKE_make_operation_argument_file_s_length           4
+  #define FAKE_make_operation_argument_force_s_length          5
   #define FAKE_make_operation_argument_has_s_length            3
   #define FAKE_make_operation_argument_ignore_s_length         6
   #define FAKE_make_operation_argument_is_s_length             2
@@ -1354,16 +1357,18 @@ extern "C" {
   #define FAKE_make_operation_argument_parameter_s_length      9
   #define FAKE_make_operation_argument_point_s_length          5
   #define FAKE_make_operation_argument_recursive_s_length      9
+  #define FAKE_make_operation_argument_strict_s_length         6
   #define FAKE_make_operation_argument_success_s_length        7
   #define FAKE_make_operation_argument_target_s_length         6
   #define FAKE_make_operation_argument_warn_s_length           4
 
-  extern const f_string_static_t fake_make_operation_argument_environment_s;
-  extern const f_string_static_t fake_make_operation_argument_failure_s;
-  extern const f_string_static_t fake_make_operation_argument_file_s;
   extern const f_string_static_t fake_make_operation_argument_directory_s;
+  extern const f_string_static_t fake_make_operation_argument_environment_s;
   extern const f_string_static_t fake_make_operation_argument_error_s;
   extern const f_string_static_t fake_make_operation_argument_exit_s;
+  extern const f_string_static_t fake_make_operation_argument_failure_s;
+  extern const f_string_static_t fake_make_operation_argument_file_s;
+  extern const f_string_static_t fake_make_operation_argument_force_s;
   extern const f_string_static_t fake_make_operation_argument_has_s;
   extern const f_string_static_t fake_make_operation_argument_ignore_s;
   extern const f_string_static_t fake_make_operation_argument_is_s;
@@ -1371,6 +1376,7 @@ extern "C" {
   extern const f_string_static_t fake_make_operation_argument_parameter_s;
   extern const f_string_static_t fake_make_operation_argument_point_s;
   extern const f_string_static_t fake_make_operation_argument_recursive_s;
+  extern const f_string_static_t fake_make_operation_argument_strict_s;
   extern const f_string_static_t fake_make_operation_argument_success_s;
   extern const f_string_static_t fake_make_operation_argument_target_s;
   extern const f_string_static_t fake_make_operation_argument_warn_s;
index 375ff310a18c7eefe02c0e8030a68d56f6f92a4e..b36e3914cb51ad76e1dc02f265ca41b729959ada 100644 (file)
@@ -1312,10 +1312,52 @@ extern "C" {
 
     f_status_t status = F_none;
 
-    status = f_file_link(data_make->cache_arguments.array[0], data_make->cache_arguments.array[1]);
+    // 0x1 = force, 0x2 = strict.
+    uint8_t flag = 0;
+
+    if (data_make->cache_arguments.used > 2) {
+      if (fl_string_dynamic_compare(fake_make_operation_argument_force_s, data_make->cache_arguments.array[1]) != F_equal_to) {
+        flag |= 0x1;
+      }
+      else if (fl_string_dynamic_compare(fake_make_operation_argument_strict_s, data_make->cache_arguments.array[1]) == F_equal_to) {
+        flag |= 0x2;
+      }
+
+      if (data_make->cache_arguments.used > 3) {
+        if (fl_string_dynamic_compare(fake_make_operation_argument_force_s, data_make->cache_arguments.array[2]) != F_equal_to) {
+          flag |= 0x1;
+        }
+        else if (fl_string_dynamic_compare(fake_make_operation_argument_strict_s, data_make->cache_arguments.array[2]) == F_equal_to) {
+          flag |= 0x2;
+        }
+      }
+    }
+
+    if ((flag & 0x1) && f_file_exists(data_make->cache_arguments.array[data_make->cache_arguments.used - 1], F_false) == F_true) {
+      if (f_directory_is(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]) == F_true) {
+        status = f_directory_remove(data_make->cache_arguments.array[data_make->cache_arguments.used - 1], F_directory_descriptors_max_d, F_false);
+
+        if (F_status_is_error(status)) {
+          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_directory_remove", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_delete_s, fll_error_file_type_directory_e);
+
+          return F_status_set_error(F_failure);
+        }
+      }
+      else {
+        status = f_file_remove(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
+
+        if (F_status_is_error(status)) {
+          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_remove", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_delete_s, fll_error_file_type_file_e);
+
+          return F_status_set_error(F_failure);
+        }
+      }
+    }
+
+    status = f_file_link(data_make->cache_arguments.array[0], data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
     if (F_status_is_error(status)) {
-      fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_link", F_true, data_make->cache_arguments.array[1], f_file_operation_link_s, fll_error_file_type_file_e);
+      fll_error_file_print(data_make->error, F_status_set_fine(status), "f_file_link", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_link_s, fll_error_file_type_file_e);
 
       return F_status_set_error(F_failure);
     }
@@ -1323,7 +1365,7 @@ extern "C" {
     if (data_make->main->error.verbosity >= f_console_verbosity_verbose_e) {
       flockfile(data_make->main->output.to.stream);
 
-      fl_print_format("Created symbolic link from '%[%Q%]", data_make->main->output.to.stream, data_make->main->context.set.notable, data_make->cache_arguments.array[1], data_make->main->context.set.notable);
+      fl_print_format("Created symbolic link from '%[%Q%]", data_make->main->output.to.stream, data_make->main->context.set.notable, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], data_make->main->context.set.notable);
       fl_print_format("' to %[%Q%].%r", data_make->main->output.to.stream, data_make->main->context.set.notable, data_make->cache_arguments.array[0], data_make->main->context.set.notable, f_string_eol_s);
 
       funlockfile(data_make->main->output.to.stream);
index 7d96ed363282bebe2425c77266d27ad31dd42be3..c40b3771f0547c0425ae8bf8e347896c8c375be4 100644 (file)
@@ -123,15 +123,16 @@ extern "C" {
 
     if (data_make->cache_arguments.used > 1) {
       f_status_t status = F_none;
+      f_status_t status_file = F_none;
 
       for (f_array_length_t i = 0; i < data_make->cache_arguments.used; ++i) {
 
-        status = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[i]);
+        status_file = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[i]);
 
-        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->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[i]);
+        if (F_status_is_error(status_file)) {
+          fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", data_make->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[i]);
 
-          if (F_status_set_fine(status) == F_false) return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
       } // for
 
@@ -148,16 +149,16 @@ extern "C" {
             funlockfile(data_make->error.to.stream);
           }
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
       } // for
 
       if (data_make->cache_arguments.used > 2) {
 
         // The last file must be a directory.
-        status = f_directory_is(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
+        status_file = f_directory_is(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
-        if (status == F_false || status == F_file_found_not) {
+        if (status_file == F_false || status_file == F_file_found_not) {
           if (data_make->error.verbosity != f_console_verbosity_quiet_e && data_make->error.to.stream) {
             flockfile(data_make->error.to.stream);
 
@@ -168,24 +169,24 @@ extern "C" {
             funlockfile(data_make->error.to.stream);
           }
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
 
-        if (F_status_is_error(status)) {
-          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_directory_is", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_find_s, fll_error_file_type_directory_e);
+        if (F_status_is_error(status_file)) {
+          fll_error_file_print(data_make->error, F_status_set_fine(status_file), "f_directory_is", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_find_s, fll_error_file_type_directory_e);
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
       }
       else {
 
         // When the first file is a directory, then the second, if it exists, must also be a directory.
-        status = f_directory_is(data_make->cache_arguments.array[0]);
+        status_file = f_directory_is(data_make->cache_arguments.array[0]);
 
-        if (status == F_true) {
-          status = f_directory_is(data_make->cache_arguments.array[1]);
+        if (status_file == F_true) {
+          status_file = f_directory_is(data_make->cache_arguments.array[1]);
 
-          if (status == F_false) {
+          if (status_file == F_false) {
             if (data_make->error.verbosity != f_console_verbosity_quiet_e && data_make->error.to.stream) {
               flockfile(data_make->error.to.stream);
 
@@ -196,12 +197,12 @@ extern "C" {
               funlockfile(data_make->error.to.stream);
             }
 
-            return F_status_set_error(F_failure);
+            status = F_status_set_error(F_failure);
           }
         }
       }
 
-      return F_none;
+      return status;
     }
 
     fake_print_error_requires_more_arguments(data_make);
@@ -716,23 +717,18 @@ extern "C" {
 
     if (data_make->cache_arguments.used > 1) {
       f_status_t status = F_none;
+      f_status_t status_file = F_none;
 
-      {
-        f_status_t status_file = F_none;
-
-        for (f_array_length_t i = 0; i < data_make->cache_arguments.used; ++i) {
-
-          status_file = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[i]);
+      for (f_array_length_t i = 0; i < data_make->cache_arguments.used; ++i) {
 
-          if (F_status_is_error(status_file)) {
-            fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", data_make->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[i]);
+        status_file = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[i]);
 
-            status = F_status_set_error(F_failure);
-          }
-        } // for
-      }
+        if (F_status_is_error(status_file)) {
+          fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", data_make->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[i]);
 
-      if (F_status_is_error(status)) return status;
+          status = F_status_set_error(F_failure);
+        }
+      } // for
 
       for (f_array_length_t i = 0; i < data_make->cache_arguments.used - 1; ++i) {
 
@@ -747,16 +743,16 @@ extern "C" {
             funlockfile(data_make->error.to.stream);
           }
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
       } // for
 
       if (data_make->cache_arguments.used > 2) {
 
         // The last file must be a directory.
-        status = f_directory_is(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
+        status_file = f_directory_is(data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
-        if (status == F_false || status == F_file_found_not) {
+        if (status_file == F_false || status_file == F_file_found_not) {
           if (data_make->error.verbosity != f_console_verbosity_quiet_e && data_make->error.to.stream) {
             flockfile(data_make->error.to.stream);
 
@@ -767,24 +763,24 @@ extern "C" {
             funlockfile(data_make->error.to.stream);
           }
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
 
-        if (F_status_is_error(status)) {
-          fll_error_file_print(data_make->error, F_status_set_fine(status), "f_directory_is", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_identify_s, fll_error_file_type_directory_e);
+        if (F_status_is_error(status_file)) {
+          fll_error_file_print(data_make->error, F_status_set_fine(status_file), "f_directory_is", F_true, data_make->cache_arguments.array[data_make->cache_arguments.used - 1], f_file_operation_identify_s, fll_error_file_type_directory_e);
 
-          return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
         }
       }
       else {
 
         // When the first file is a directory, then the second, if it exists, must also be a directory.
-        status = f_directory_is(data_make->cache_arguments.array[0]);
+        status_file = f_directory_is(data_make->cache_arguments.array[0]);
 
-        if (status == F_true) {
-          status = f_directory_is(data_make->cache_arguments.array[1]);
+        if (status_file == F_true) {
+          status_file = f_directory_is(data_make->cache_arguments.array[1]);
 
-          if (status == F_false) {
+          if (status_file == F_false) {
             if (data_make->error.verbosity != f_console_verbosity_quiet_e && data_make->error.to.stream) {
               flockfile(data_make->error.to.stream);
 
@@ -795,12 +791,12 @@ extern "C" {
               funlockfile(data_make->error.to.stream);
             }
 
-            return F_status_set_error(F_failure);
+            status = F_status_set_error(F_failure);
           }
         }
       }
 
-      return F_none;
+      return status;
     }
 
     fake_print_error_requires_more_arguments(data_make);
@@ -1024,30 +1020,154 @@ extern "C" {
 #ifndef _di_fake_make_operate_validate_type_link_
   f_status_t fake_make_operate_validate_type_link(fake_make_data_t * const data_make) {
 
-    if (data_make->cache_arguments.used > 2) {
+    if (data_make->cache_arguments.used > 4) {
       fake_print_error_too_many_arguments(data_make);
 
       return F_status_set_error(F_failure);
     }
 
-    if (data_make->cache_arguments.used == 2) {
-      f_status_t status = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[0]);
+    if (data_make->cache_arguments.used > 1) {
+      f_status_t status = F_none;
+      f_status_t status_file = F_none;
 
-      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->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[0]);
+      // 0x1 = force, 0x2 = strict.
+      uint8_t flag = 0;
+
+      if (data_make->cache_arguments.used > 2) {
+        if (fl_string_dynamic_compare(fake_make_operation_argument_force_s, data_make->cache_arguments.array[0]) == F_equal_to) {
+          flag |= 0x1;
+        }
+        else if (fl_string_dynamic_compare(fake_make_operation_argument_strict_s, data_make->cache_arguments.array[0]) == F_equal_to) {
+          flag |= 0x2;
+        }
+        else {
+          fake_print_message_section_operation_link_argument_unknown(data_make->data, data_make->error, data_make->cache_arguments.array[0]);
 
-        if (F_status_set_fine(status) == F_false) return F_status_set_error(F_failure);
+          status = F_status_set_error(F_failure);
+        }
+
+        if (data_make->cache_arguments.used > 3) {
+          if (fl_string_dynamic_compare(fake_make_operation_argument_force_s, data_make->cache_arguments.array[1]) == F_equal_to) {
+            flag |= 0x1;
+          }
+          else if (fl_string_dynamic_compare(fake_make_operation_argument_strict_s, data_make->cache_arguments.array[1]) == F_equal_to) {
+            flag |= 0x2;
+          }
+          else {
+            fake_print_message_section_operation_link_argument_unknown(data_make->data, data_make->error, data_make->cache_arguments.array[1]);
+
+            status = F_status_set_error(F_failure);
+          }
+        }
       }
 
-      status = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[1]);
+      status_file = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
-      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->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[1]);
+      if (F_status_is_error(status_file)) {
+        fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", data_make->cache_path.used ? data_make->cache_path : data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
-        if (F_status_set_fine(status) == F_false) return F_status_set_error(F_failure);
+        status = F_status_set_error(F_failure);
       }
+      else {
+        if (!(flag & 0x1)) {
+          if (!data_make->cache_path.used || f_file_exists(data_make->cache_path, F_false) == F_true) {
+            fake_print_message_section_operation_link_point_exists(data_make->data, data_make->error, data_make->cache_arguments.array[data_make->cache_arguments.used - 1]);
 
-      return F_none;
+            status = F_status_set_error(F_failure);
+          }
+        }
+
+        if (f_path_is_absolute(data_make->cache_arguments.array[data_make->cache_arguments.used - 2]) == F_true) {
+          status_file = fake_make_assure_inside_project(data_make, data_make->cache_arguments.array[data_make->cache_arguments.used - 2]);
+
+          if (F_status_is_error(status_file)) {
+            fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", data_make->cache_arguments.array[data_make->cache_arguments.used - 2]);
+
+            status = F_status_set_error(F_failure);
+          }
+
+          if ((flag & 0x2) && F_status_is_error_not(status_file)) {
+            if (f_file_exists(data_make->cache_arguments.array[data_make->cache_arguments.used - 2], F_false) != F_true) {
+              fake_print_message_section_operation_link_target_exists_not(data_make->data, data_make->error, data_make->cache_arguments.array[data_make->cache_arguments.used - 2]);
+
+              status = F_status_set_error(F_failure);
+            }
+          }
+        }
+
+        // The target path is relative to the point path so construct the path for performing checks.
+        else if (data_make->cache_arguments.array[data_make->cache_arguments.used - 2].used) {
+
+          // The cache_path contains the full path to the point file, save this so that the cache_path can be re-used.
+          f_char_t full_string[data_make->cache_path.used + 1];
+          f_string_static_t full = macro_f_string_static_t_initialize(full_string, 0, data_make->cache_path.used);
+
+          memcpy(full_string, data_make->cache_path.string, sizeof(f_char_t) * data_make->cache_path.used);
+          full_string[data_make->cache_path.used] = 0;
+
+          data_make->cache_path.used = 0;
+
+          status_file = f_file_name_directory(full, &data_make->cache_path);
+
+          if (F_status_is_error(status_file)) {
+            fll_error_file_print(data_make->error, F_status_set_fine(status_file), "f_file_name_directory", F_true, full, f_file_operation_analyze_s, fll_error_file_type_path_e);
+
+            status = F_status_set_error(F_failure);
+          }
+
+          if (F_status_is_error_not(status_file)) {
+            status_file = f_string_dynamic_append_assure(f_path_separator_s, &data_make->cache_path);
+
+            if (F_status_is_error(status_file)) {
+              fll_error_print(data_make->error, F_status_set_fine(status_file), "f_string_dynamic_append_assure", F_true);
+
+              status = F_status_set_error(F_failure);
+            }
+            else {
+              status_file = f_string_dynamic_append(data_make->cache_arguments.array[data_make->cache_arguments.used - 2], &data_make->cache_path);
+
+              if (F_status_is_error(status_file)) {
+                fll_error_print(data_make->error, F_status_set_fine(status_file), "f_string_dynamic_append", F_true);
+
+                status = F_status_set_error(F_failure);
+              }
+            }
+
+            if (F_status_is_error_not(status_file)) {
+
+              // The cache_path is used by fake_make_assure_inside_project(), so copy the contents into another buffer.
+              f_char_t target_string[data_make->cache_path.used + 1];
+              f_string_static_t target = macro_f_string_static_t_initialize(target_string, 0, data_make->cache_path.used);
+
+              memcpy(target_string, data_make->cache_path.string, sizeof(f_char_t) * data_make->cache_path.used);
+              target_string[data_make->cache_path.used] = 0;
+
+              status_file = fake_make_assure_inside_project(data_make, target);
+
+              if (F_status_is_error(status_file)) {
+                fake_print_message_section_operation_path_outside(data_make->data, data_make->error, F_status_set_fine(status_file), "fake_make_assure_inside_project", target);
+
+                status = F_status_set_error(F_failure);
+              }
+
+              if ((flag & 0x2) && F_status_is_error_not(status_file)) {
+                if (f_file_exists(target, F_false) != F_true) {
+                  fake_print_message_section_operation_link_target_exists_not(data_make->data, data_make->error, target);
+
+                  status = F_status_set_error(F_failure);
+                }
+              }
+            }
+          }
+        }
+        else {
+          if (data_make->error.verbosity != f_console_verbosity_quiet_e && data_make->error.to.stream) {
+            fll_print_format("%r%[%QTarget filename argument must not be an empty string.%]%r", data_make->error.to.stream, f_string_eol_s, data_make->error.context, data_make->error.prefix, data_make->error.context, f_string_eol_s);
+          }
+        }
+      }
+
+      return status;
     }
 
     fake_print_error_requires_more_arguments(data_make);
index f682a57ce875b60d75e55b2d157986553f953fa6..57916b42a134572c5ae852b10efbbbf6a53ffcaf 100644 (file)
@@ -367,27 +367,79 @@ extern "C" {
 #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) {
 
-    if (data->main->error.verbosity == f_console_verbosity_quiet_e || !print.to.stream) return;
+    if (print.verbosity == f_console_verbosity_quiet_e || !print.to.stream) return;
 
     f_array_length_t line = 1;
     f_state_t state = f_state_t_initialize;
 
     f_fss_count_lines(state, buffer, operation_name.start, &line);
 
-    flockfile(data->main->error.to.stream);
+    flockfile(print.to.stream);
 
-    fl_print_format("%r%[%QThe section operation '%]", data->main->error.to.stream, f_string_eol_s, data->main->error.context, data->main->error.prefix, data->main->error.context);
-    fl_print_format("%[%/Q%]", data->main->error.to.stream, data->main->error.notable, buffer, operation_name, data->main->error.notable);
-    fl_print_format("%[' from section '%]", data->main->error.to.stream, data->main->error.context, data->main->error.context);
-    fl_print_format("%[%/Q%]", data->main->error.to.stream, data->main->error.notable, buffer, section_name, data->main->error.notable);
-    fl_print_format("%[' on line%] ", data->main->error.to.stream, data->main->error.context, data->main->error.context);
-    fl_print_format("%[%un%]", data->main->error.to.stream, data->main->error.notable, line, data->main->error.notable);
-    fl_print_format(" %[failed.%]%r", data->main->error.to.stream, data->main->error.context, data->main->error.context, f_string_eol_s);
+    fl_print_format("%r%[%QThe section operation '%]", print.to.stream, f_string_eol_s, print.context, print.prefix, print.context);
+    fl_print_format("%[%/Q%]", print.to.stream, print.notable, buffer, operation_name, print.notable);
+    fl_print_format("%[' from section '%]", print.to.stream, print.context, print.context);
+    fl_print_format("%[%/Q%]", print.to.stream, print.notable, buffer, section_name, print.notable);
+    fl_print_format("%[' on line%] ", print.to.stream, print.context, print.context);
+    fl_print_format("%[%un%]", print.to.stream, print.notable, line, print.notable);
+    fl_print_format(" %[failed.%]%r", print.to.stream, print.context, print.context, f_string_eol_s);
 
-    funlockfile(data->main->error.to.stream);
+    funlockfile(print.to.stream);
   }
 #endif // _di_fake_print_message_section_operation_failed_
 
+#ifndef _di_fake_print_message_section_operation_link_argument_unknown_
+  void fake_print_message_section_operation_link_argument_unknown(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) {
+
+    if (print.verbosity == f_console_verbosity_quiet_e || !print.to.stream) return;
+
+    flockfile(print.to.stream);
+
+    fl_print_format("%r%[%QThe argument '%]", print.to.stream, f_string_eol_s, print.context, print.prefix, print.context);
+    fl_print_format("%[%Q%]", print.to.stream, print.notable, argument, print.notable);
+    fl_print_format("%[' is not not valid and may only be one of either '%]", print.to.stream, print.context, print.context);
+    fl_print_format("%[%r%]", print.to.stream, print.notable, fake_make_operation_argument_force_s, print.notable);
+    fl_print_format("%[' or '%]", print.to.stream, print.context, print.context);
+    fl_print_format("%[%r%]", print.to.stream, print.notable, fake_make_operation_argument_strict_s, print.notable);
+    fl_print_format("%['.%]%r", print.to.stream, print.context, print.context, f_string_eol_s);
+
+    funlockfile(print.to.stream);
+
+  }
+#endif // _di_fake_print_message_section_operation_link_argument_unknown_
+
+#ifndef _di_fake_print_message_section_operation_link_point_exists_
+  void fake_print_message_section_operation_link_point_exists(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) {
+
+    if (print.verbosity == f_console_verbosity_quiet_e || !print.to.stream) return;
+
+    flockfile(print.to.stream);
+
+    fl_print_format("%r%[%QThe point file '%]", print.to.stream, f_string_eol_s, print.context, print.prefix, print.context);
+    fl_print_format("%[%Q%]", print.to.stream, print.notable, argument, print.notable);
+    fl_print_format("%[' already exists.%]%r", print.to.stream, print.context, print.context, f_string_eol_s);
+
+    funlockfile(print.to.stream);
+
+  }
+#endif // _di_fake_print_message_section_operation_link_point_exists_
+
+#ifndef _di_fake_print_message_section_operation_link_target_exists_not_
+  void fake_print_message_section_operation_link_target_exists_not(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) {
+
+    if (print.verbosity == f_console_verbosity_quiet_e || !print.to.stream) return;
+
+    flockfile(print.to.stream);
+
+    fl_print_format("%r%[%QThe target file '%]", print.to.stream, f_string_eol_s, print.context, print.prefix, print.context);
+    fl_print_format("%[%Q%]", print.to.stream, print.notable, argument, print.notable);
+    fl_print_format("%[' does not exist.%]%r", print.to.stream, print.context, print.context, f_string_eol_s);
+
+    funlockfile(print.to.stream);
+
+  }
+#endif // _di_fake_print_message_section_operation_link_target_exists_not_
+
 #ifndef _di_fake_print_message_section_operation_path_outside_
   void fake_print_message_section_operation_path_outside(fake_data_t * const data, const fl_print_t print, const f_status_t status, const char *function, const f_string_static_t path) {
 
index 931d9be357abd3da5a33fa0ac860305fd524427f..3f84b44ec1228539a9fb06363b75466aacabf0f8 100644 (file)
@@ -198,6 +198,63 @@ extern "C" {
 #endif // _di_fake_print_message_section_operation_failed_
 
 /**
+ * Print error messages when a given link argument is unknown.
+ *
+ * @param data
+ *   The program data.
+ * @param print
+ *   Designates how the section error/warning should be printed.
+ * @param argument
+ *   The argument that is unknown by the link operation.
+ *
+ * @see flockfile()
+ * @see funlockfile()
+ *
+ * @see fl_print_format()
+ */
+#ifndef _di_fake_print_message_section_operation_link_argument_unknown_
+  extern void fake_print_message_section_operation_link_argument_unknown(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) F_attribute_visibility_internal_d;
+#endif // _di_fake_print_message_section_operation_link_argument_unknown_
+
+/**
+ * Print error messages when a given link point file already exists.
+ *
+ * @param data
+ *   The program data.
+ * @param print
+ *   Designates how the section error/warning should be printed.
+ * @param argument
+ *   The argument representing the point file.
+ *
+ * @see flockfile()
+ * @see funlockfile()
+ *
+ * @see fl_print_format()
+ */
+#ifndef _di_fake_print_message_section_operation_link_point_exists_
+  extern void fake_print_message_section_operation_link_point_exists(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) F_attribute_visibility_internal_d;
+#endif // _di_fake_print_message_section_operation_link_point_exists_
+
+/**
+ * Print error messages when a given link target file does not already exist.
+ *
+ * @param data
+ *   The program data.
+ * @param print
+ *   Designates how the section error/warning should be printed.
+ * @param argument
+ *   The argument representing the point file.
+ *
+ * @see flockfile()
+ * @see funlockfile()
+ *
+ * @see fl_print_format()
+ */
+#ifndef _di_fake_print_message_section_operation_link_target_exists_not_
+  extern void fake_print_message_section_operation_link_target_exists_not(fake_data_t * const data, const fl_print_t print, const f_string_static_t argument) F_attribute_visibility_internal_d;
+#endif // _di_fake_print_message_section_operation_link_target_exists_not_
+
+/**
  * Print error messages when processing some fakefile section, for a specific line and operation, and that operation has a path outside of the project root.
  *
  * @param data
index 53e0b76f37212ebe975a7a4cd9d2c7316cb575f9..eb220fb13277c96e22ac7b9016796c1016d1f9ff 100644 (file)
@@ -337,8 +337,12 @@ Fakefile Documentation:
     - link\:
       Create a symbolic link from some point to some target.
 
-      The first Content represents the target file.
-      The second Content represents the point file.
+      The first Content, when there are more than 2 arguments, may be either "force" or "strict".
+      The second to last Content represents the target file.
+      The last Content represents the point file.
+
+      The "force" Content designates that the point file will be overwritten if the file already exists.
+      The "strict" Content requires that the target file already exists.
 
     - mode\:
       Change the mode permissions for a given file.
index 5f2bd8889127adbd9e6a4f89f6c47ba5fd6d87ce..eb2d3e51247a98281e77029f034de6c590a24e50 100644 (file)
@@ -59,7 +59,7 @@ Fakefile Specification:
   - groups: Two or more Content. First Content is group name, number, or "no_dereference" (when "no_dereference", then the second Content is the group name or number, etc..), remaining Content are paths to files.
   - if: One or more Content. First Content is the condition or is "no_dereference" (when "no_dereference", then the Second Content is the condition, etc..), remaining Content are specific to the condition.
   - index: One or more Content.
-  - link: Two Content. First Content is the link target file and second Content is the pointer file (the link).
+  - link: Two to Four Content. The first and second Content may be either "force" or "strict", the second to last Content is the link target file, and the last Content is the pointer file (the link).
   - mode: Two or more Content. First Content is the mode, remaining Content are paths to files.
   - modes: Two or more Content. First Content is the mode, remaining Content are paths to files.
   - move: Two or more Content representing paths to files.