]> Kevux Git Server - fll/commitdiff
Progress: contoller program.
authorKevin Day <thekevinday@gmail.com>
Sun, 13 Dec 2020 04:09:40 +0000 (22:09 -0600)
committerKevin Day <thekevinday@gmail.com>
Sun, 13 Dec 2020 04:19:51 +0000 (22:19 -0600)
This focuses on getting the simulation code working as desired.

While --test is what triggers simulation mode, if --validate is specified, then additional information on rules are provided.

Break apart the default pid file name to operate on a per entry basis.
This will allow for multiple controllers to co-exist with different pid files (and likewise eventually socket files).

This fixes a lot of bugs/mistakes that cropped up during the previous codestorming as well as restructures some of the data types.

level_3/controller/c/controller.c
level_3/controller/c/controller.h
level_3/controller/c/private-common.h
level_3/controller/c/private-controller.c
level_3/controller/c/private-controller.h
level_3/controller/c/private-entry.c
level_3/controller/c/private-rule.c
level_3/controller/c/private-rule.h
level_3/controller/documents/entry.txt

index 56d575801d7fb0f0f0773f99aea0d8f9f9584ae3..a79552429446692017b731c35b7d970a6d1bffdf 100644 (file)
@@ -28,13 +28,20 @@ extern "C" {
 
     fll_program_print_help_option(output, context, controller_short_daemon, controller_long_daemon, f_console_symbol_short_enable, f_console_symbol_long_enable, "       Run in daemon only mode (do not process the entry).");
     fll_program_print_help_option(output, context, controller_short_interruptable, controller_long_interruptable, f_console_symbol_short_enable, f_console_symbol_long_enable, "Designate that this program can be interrupted.");
-    fll_program_print_help_option(output, context, controller_short_pid, controller_long_pid, f_console_symbol_short_enable, f_console_symbol_long_enable, "          Specify a custom pid file path, such as '" controller_path_pid "'.");
+    fll_program_print_help_option(output, context, controller_short_pid, controller_long_pid, f_console_symbol_short_enable, f_console_symbol_long_enable, "          Specify a custom pid file path, such as '" controller_path_pid controller_string_default controller_path_suffix "'.");
     fll_program_print_help_option(output, context, controller_short_settings, controller_long_settings, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Specify a custom settings path, such as '" controller_path_settings "'.");
-    fll_program_print_help_option(output, context, controller_short_test, controller_long_test, f_console_symbol_short_enable, f_console_symbol_long_enable, "         Run in test mode, where nothing is actually run (a simulation).");
-    fll_program_print_help_option(output, context, controller_short_validate, controller_long_validate, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Validate the settings (entry and rules) without running (or simulating).");
+    fll_program_print_help_option(output, context, controller_short_test, controller_long_test, f_console_symbol_short_enable, f_console_symbol_long_enable, "         Run in test mode, where nothing is actually run but is instead simulated.");
+    fll_program_print_help_option(output, context, controller_short_validate, controller_long_validate, f_console_symbol_short_enable, f_console_symbol_long_enable, "     Validate the settings (entry and rules) without running (does not simulate).");
 
     fll_program_print_help_usage(output, context, controller_name, "entry");
 
+    fprintf(output.stream, "  When both the ");
+    fl_color_print(output.stream, context.set.notable, "%s%s", f_console_symbol_long_enable, controller_long_test);
+    fprintf(output.stream, " operation and the ");
+    fl_color_print(output.stream, context.set.notable, "%s%s", f_console_symbol_long_enable, controller_long_validate);
+    fprintf(output.stream, " operation are specified, then additional information on each would be executed rule is printed.");
+    fprintf(output.stream, "%c", f_string_eol[0]);
+
     return F_none;
   }
 #endif // _di_controller_print_help_
@@ -230,6 +237,14 @@ extern "C" {
     if (!setting.path_pid.used) {
       status = fl_string_append(controller_path_pid, controller_path_pid_length, &setting.path_pid);
 
+      if (F_status_is_error_not(status)) {
+        status = fl_string_append(entry_name.string, entry_name.used, &setting.path_pid);
+      }
+
+      if (F_status_is_error_not(status)) {
+        status = fl_string_append(controller_path_suffix, controller_path_suffix_length, &setting.path_pid);
+      }
+
       if (F_status_is_error(status)) {
         if (data->error.verbosity != f_console_verbosity_quiet) {
           fll_error_print(data->error, F_status_set_fine(status), "fl_string_append", F_true);
@@ -282,7 +297,7 @@ extern "C" {
       else {
         status = controller_preprocess_entry(*data, &setting, &cache);
 
-        if (data->parameters[controller_parameter_validate].result == f_console_result_none) {
+        if (data->parameters[controller_parameter_validate].result == f_console_result_none || data->parameters[controller_parameter_test].result == f_console_result_found) {
 
           if (f_file_exists(setting.path_pid.string) == F_true) {
             if (data->error.verbosity != f_console_verbosity_quiet) {
index 1ded520b7d0b6a8357aac3dd4d7f24760348fb1a..da4d165929bc6274b4cd21c41949ddd1adc68db0 100644 (file)
@@ -27,6 +27,7 @@
 #include <level_0/memory.h>
 #include <level_0/string.h>
 #include <level_0/utf.h>
+#include <level_0/color.h>
 #include <level_0/console.h>
 #include <level_0/directory.h>
 #include <level_0/file.h>
@@ -72,18 +73,20 @@ extern "C" {
   // This specifically must be at least 2 for this project.
   #define controller_default_allocation_step 4
 
-  #define controller_path_pid      "/var/run/controller/controller.pid"
+  #define controller_path_pid      "/var/run/controller/controller-"
   #define controller_path_settings "/etc/controller"
+  #define controller_path_suffix   ".pid"
 
-  #define controller_path_pid_length      34
+  #define controller_path_pid_length      31
   #define controller_path_settings_length 15
+  #define controller_path_suffix_length   4
 
   #define controller_short_daemon        "d"
   #define controller_short_interruptable "i"
   #define controller_short_pid           "p"
   #define controller_short_settings      "s"
   #define controller_short_test          "t"
-  #define controller_short_validate      "V"
+  #define controller_short_validate      "v"
 
   #define controller_long_daemon        "daemon"
   #define controller_long_interruptable "interruptable"
index 5208035fcf39b05a7eff91af58d28205497d0155..0508ca4827b08a0cbbec1f6d6338ba1b45ca9e37 100644 (file)
@@ -14,6 +14,7 @@ extern "C" {
 
 #ifndef _di_controller_string_
   #define controller_string_action        "action"
+  #define controller_string_actions       "actions"
   #define controller_string_asynchronous  "asynchronous"
   #define controller_string_create        "create"
   #define controller_string_command       "command"
@@ -55,6 +56,7 @@ extern "C" {
   #define controller_string_wish          "wish"
 
   #define controller_string_action_length        6
+  #define controller_string_actions_length       7
   #define controller_string_asynchronous_length  12
   #define controller_string_create_length        6
   #define controller_string_command_length       7
@@ -121,6 +123,9 @@ extern "C" {
   };
 
   typedef struct {
+    uint8_t method;
+    uint8_t type;
+
     f_string_length_t line;
 
     f_status_t status;
@@ -131,6 +136,8 @@ extern "C" {
   #define controller_rule_action_t_initialize \
     { \
       0, \
+      0, \
+      0, \
       F_known_not, \
       f_string_dynamics_t_initialize, \
     }
@@ -141,9 +148,6 @@ extern "C" {
 
 #ifndef _di_controller_rule_actions_t_
   typedef struct {
-    uint8_t method;
-    uint8_t type;
-
     controller_rule_action_t *array;
 
     f_array_length_t size;
@@ -155,8 +159,6 @@ extern "C" {
       0, \
       0, \
       0, \
-      0, \
-      0, \
     }
 
   #define controller_macro_rule_actions_t_delete_simple(actions) \
index 532b6be9f67d991a1c3070a1e832ce22298a0fc4..ee07570166c849e3b2b8dd436440f4cef347534f 100644 (file)
@@ -9,16 +9,23 @@
 extern "C" {
 #endif
 
+#ifndef _di_controller_string_dynamic_rip_nulless_terminated_
+  f_return_status controller_string_dynamic_rip_nulless_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) {
+
+    f_status_t status = fl_string_dynamic_rip_nulless(source, range, destination);
+    if (F_status_is_error(status)) return status;
+
+    return fl_string_dynamic_terminate_after(destination);
+  }
+#endif // _di_controller_string_dynamic_rip_nulless_terminated_
+
 #ifndef _di_controller_string_dynamic_append_terminated_
   f_return_status controller_string_dynamic_append_terminated(const f_string_static_t source, f_string_dynamic_t *destination) {
 
     f_status_t status = fl_string_dynamic_append(source, destination);
-
     if (F_status_is_error(status)) return status;
 
-    status = fl_string_dynamic_terminate_after(destination);
-
-    return status;
+    return fl_string_dynamic_terminate_after(destination);
   }
 #endif // _di_controller_string_dynamic_append_terminated_
 
@@ -26,12 +33,9 @@ extern "C" {
   f_return_status controller_string_dynamic_partial_append_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) {
 
     f_status_t status = fl_string_dynamic_partial_append(source, range, destination);
-
     if (F_status_is_error(status)) return status;
 
-    status = fl_string_dynamic_terminate_after(destination);
-
-    return status;
+    return fl_string_dynamic_terminate_after(destination);
   }
 #endif // _di_controller_string_dynamic_partial_append_terminated_
 
@@ -48,7 +52,7 @@ extern "C" {
     status = fl_string_append(path_prefix, path_prefix_length, &cache->name_file);
 
     if (F_status_is_error_not(status)) {
-      status = fl_string_append(f_path_separator, f_path_separator_length, &cache->name_file);
+      status = fl_string_append(f_path_separator_s, f_path_separator_length, &cache->name_file);
     }
 
     if (F_status_is_error_not(status)) {
@@ -82,7 +86,7 @@ extern "C" {
       memcpy(path, setting.path_setting.string, setting.path_setting.used);
       memcpy(path + setting.path_setting.used + f_path_separator_length, cache->name_file.string, cache->name_file.used);
 
-      path[setting.path_setting.used] = f_path_separator[0];
+      path[setting.path_setting.used] = f_path_separator_s[0];
     }
 
     path[path_length] = 0;
@@ -271,6 +275,7 @@ extern "C" {
       return status;
     }
 
+    // utilize the ats cache as an item execution stack (at_i is for item index, and at_j (at_i + 1) is for action index).
     cache->ats.array[0] = 0;
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
@@ -492,6 +497,7 @@ extern "C" {
       return status;
     }
 
+    // utilize the ats cache as an item execution stack (at_i is for item index, and at_j (at_i + 1) is for action index).
     cache->ats.array[0] = 0;
     cache->ats.array[1] = 0;
     cache->ats.used = 2;
@@ -508,6 +514,13 @@ extern "C" {
       return status;
     }
 
+    if (simulate) {
+      fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+      fprintf(data.output.stream, "Processing entry item rule '");
+      fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_main, data.context.set.title.after->string);
+      fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+    }
+
     for (;;) {
 
       actions = &setting->entry.items.array[cache->ats.array[at_i]].actions;
@@ -529,10 +542,25 @@ extern "C" {
         if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_ready) {
 
           if (setting->ready == controller_setting_ready_wait) {
-            setting->ready = controller_setting_ready_yes;
 
-            controller_perform_ready(data, setting, cache);
-            if (F_status_is_error(status)) return status;
+            if (simulate) {
+              fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+              fprintf(data.output.stream, "Processing entry item action '");
+              fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_ready, data.context.set.title.after->string);
+              fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+            }
+            else {
+              controller_perform_ready(data, setting, cache);
+              if (F_status_is_error(status)) return status;
+            }
+
+            setting->ready = controller_setting_ready_yes;
+          }
+          else if (simulate) {
+            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "Ignoring entry item action '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_ready, data.context.set.title.after->string);
+            fprintf(data.output.stream, "', state is already ready.%c", f_string_eol_s[0]);
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_item) {
@@ -562,20 +590,21 @@ extern "C" {
           }
 
           // continue into the requested item.
+          cache->ats.array[cache->ats.used] = actions->array[cache->ats.array[at_j]].number;
+          cache->ats.array[cache->ats.used + 1] = 0;
+
           at_i = cache->ats.used;
           at_j = cache->ats.used + 1;
 
-          cache->ats.array[at_i] = actions->array[cache->ats.array[at_j]].number;
-          cache->ats.array[at_j] = 0;
           cache->ats.used += 2;
 
           cache->name_action.used = 0;
           cache->line_action = 0;
 
           cache->name_item.used = 0;
-          cache->line_item = setting->entry.items.array[actions->array[cache->ats.array[at_j]].number].line;
+          cache->line_item = setting->entry.items.array[cache->ats.array[at_i]].line;
 
-          status = controller_string_dynamic_append_terminated(setting->entry.items.array[actions->array[cache->ats.array[at_j]].number].name, &cache->name_item);
+          status = controller_string_dynamic_append_terminated(setting->entry.items.array[cache->ats.array[at_i]].name, &cache->name_item);
 
           if (F_status_is_error(status)) {
             fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_append_terminated", F_true);
@@ -584,6 +613,14 @@ extern "C" {
             return status;
           }
 
+          if (simulate) {
+            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "Processing entry item '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, cache->name_item.string, data.context.set.title.after->string);
+            fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+          }
+
+          // exit inner loop to force restarting and start processing the requested item.
           break;
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_consider || actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
@@ -596,85 +633,111 @@ extern "C" {
 
             return status;
           }
-          else {
-            const f_string_length_t rule_id_length = actions->array[cache->ats.array[at_j]].parameters.array[0].used + actions->array[cache->ats.array[at_j]].parameters.array[1].used + 1;
-            char rule_id_name[rule_id_length + 1];
-            const f_string_static_t rule_id = f_macro_string_static_t_initialize(rule_id_name, rule_id_length);
 
-            memcpy(rule_id_name, actions->array[cache->ats.array[at_j]].parameters.array[0].string, actions->array[cache->ats.array[at_j]].parameters.array[0].used);
-            memcpy(rule_id_name + actions->array[cache->ats.array[at_j]].parameters.array[0].used + 1, actions->array[cache->ats.array[at_j]].parameters.array[1].string, actions->array[cache->ats.array[at_j]].parameters.array[1].used);
+          const f_string_length_t rule_id_length = actions->array[cache->ats.array[at_j]].parameters.array[0].used + actions->array[cache->ats.array[at_j]].parameters.array[1].used + 1;
+          char rule_id_name[rule_id_length + 1];
+          const f_string_static_t rule_id = f_macro_string_static_t_initialize(rule_id_name, rule_id_length);
 
-            rule_id_name[actions->array[cache->ats.array[at_j]].parameters.array[0].used] = f_path_separator[0];
-            rule_id_name[rule_id_length] = 0;
+          memcpy(rule_id_name, actions->array[cache->ats.array[at_j]].parameters.array[0].string, actions->array[cache->ats.array[at_j]].parameters.array[0].used);
+          memcpy(rule_id_name + actions->array[cache->ats.array[at_j]].parameters.array[0].used + 1, actions->array[cache->ats.array[at_j]].parameters.array[1].string, actions->array[cache->ats.array[at_j]].parameters.array[1].used);
 
-            at = controller_rule_find_loaded(data, *setting, rule_id);
+          rule_id_name[actions->array[cache->ats.array[at_j]].parameters.array[0].used] = f_path_separator_s[0];
+          rule_id_name[rule_id_length] = 0;
 
-            if (at == setting->rules.used) {
-              status = controller_rule_read(data, *setting, rule_id, cache, &setting->rules.array[setting->rules.used]);
+          at = controller_rule_find_loaded(data, *setting, rule_id);
 
-              if (F_status_is_error(status)) {
-                controller_entry_error_print(data.error, *cache);
+          if (simulate) {
+            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "%s entry item rule '", actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule ? "Processing" : "Considering");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, rule_id.string, data.context.set.title.after->string);
+            fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+          }
 
-                if (!simulate) break;
-              }
-              else {
-                setting->rules.used++;
-              }
+          if (at == setting->rules.used) {
+            status = controller_rule_read(data, *setting, rule_id, cache, &setting->rules.array[setting->rules.used]);
+
+            if (F_status_is_error(status)) {
+              controller_entry_error_print(data.error, *cache);
+
+              if (!simulate) break;
             }
+            else {
+              setting->rules.used++;
+            }
+          }
 
-            if (F_status_is_error_not(status)) {
+          if (F_status_is_error_not(status)) {
 
-              // rule execution will re-use the existing cache, so save the current cache.
-              const f_array_length_t cache_line_action = cache->line_action;
-              const f_array_length_t cache_line_item = cache->line_item;
+            // rule execution will re-use the existing cache, so save the current cache.
+            const f_array_length_t cache_line_action = cache->line_action;
+            const f_array_length_t cache_line_item = cache->line_item;
 
-              const f_string_length_t cache_name_action_used = cache->name_action.used;
-              const f_string_length_t cache_name_item_used = cache->name_item.used;
-              const f_string_length_t cache_name_file_used = cache->name_file.used;
+            const f_string_length_t cache_name_action_used = cache->name_action.used;
+            const f_string_length_t cache_name_item_used = cache->name_item.used;
+            const f_string_length_t cache_name_file_used = cache->name_file.used;
 
-              char cache_name_action[cache_name_action_used];
-              char cache_name_item[cache_name_item_used];
-              char cache_name_file[cache_name_file_used];
+            char cache_name_action[cache_name_action_used];
+            char cache_name_item[cache_name_item_used];
+            char cache_name_file[cache_name_file_used];
 
-              memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
-              memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
-              memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
+            memcpy(cache_name_action, cache->name_action.string, cache->name_action.used);
+            memcpy(cache_name_item, cache->name_item.string, cache->name_item.used);
+            memcpy(cache_name_file, cache->name_file.string, cache->name_file.used);
 
-              if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
-                // @todo: this will also need to support the asynchronous/wait behavior.
-                status = controller_rule_process(data, at, simulate, setting, cache);
-              }
+            if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_rule) {
+              // @todo: this will also need to support the asynchronous/wait behavior.
+              status = controller_rule_process(data, at, simulate, setting, cache);
+            }
 
-              // restore cache.
-              memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
-              memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
-              memcpy(cache->name_file.string, cache_name_file, cache_name_file_used);
+            // restore cache.
+            memcpy(cache->name_action.string, cache_name_action, cache_name_action_used);
+            memcpy(cache->name_item.string, cache_name_item, cache_name_item_used);
+            memcpy(cache->name_file.string, cache_name_file, cache_name_file_used);
 
-              cache->name_action.string[cache_name_action_used] = 0;
-              cache->name_item.string[cache_name_item_used] = 0;
-              cache->name_file.string[cache_name_file_used] = 0;
+            cache->name_action.string[cache_name_action_used] = 0;
+            cache->name_item.string[cache_name_item_used] = 0;
+            cache->name_file.string[cache_name_file_used] = 0;
 
-              cache->name_action.used = cache_name_action_used;
-              cache->name_item.used = cache_name_item_used;
-              cache->name_file.used = cache_name_file_used;
+            cache->name_action.used = cache_name_action_used;
+            cache->name_item.used = cache_name_item_used;
+            cache->name_file.used = cache_name_file_used;
 
-              cache->line_action = cache_line_action;
-              cache->line_item = cache_line_item;
-            }
+            cache->line_action = cache_line_action;
+            cache->line_item = cache_line_item;
+          }
 
-            if (F_status_is_error(status)) {
-              controller_entry_error_print(data.error, *cache);
+          if (F_status_is_error(status)) {
+            controller_entry_error_print(data.error, *cache);
 
-              if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
-                break;
-              }
+            if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
+              break;
             }
           }
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_timeout) {
+          if (simulate) {
+            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "Processing entry item action '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_timeout, data.context.set.title.after->string);
+            fprintf(data.output.stream, "' setting '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.important.before->string, "@todo", data.context.set.important.after->string);
+            fprintf(data.output.stream, "' to '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.important.before->string, "@todo", data.context.set.important.after->string);
+            fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+          }
+
           // @todo set the appropriate timeout value (set the entry actions timeouts which are later used as the initial defaults as the rule timeouts).
         }
         else if (actions->array[cache->ats.array[at_j]].type == controller_entry_action_type_failsafe) {
+          if (simulate) {
+            fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "Processing entry item action '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.title.before->string, controller_string_failsafe, data.context.set.title.after->string);
+            fprintf(data.output.stream, "' setting value to '");
+            fprintf(data.output.stream, "%s%s%s", data.context.set.important.before->string, "@todo", data.context.set.important.after->string);
+            fprintf(data.output.stream, "'.%c", f_string_eol_s[0]);
+          }
+
           // @todo set the failsafe rule to this rule id (find the rule and then assign by the rule id and then assign by the array index).
         }
 
@@ -715,6 +778,10 @@ extern "C" {
       }
     } // for
 
+    if (F_status_is_error_not(status) && simulate) {
+      fprintf(data.output.stream, "%c", f_string_eol_s[0]);
+    }
+
     return status;
   }
 #endif // _di_controller_process_entry_
index 03992e608794387849737be187c8b7eb98a3bdc9..72d8db9be3d5a61ef1d250106ce72f8ec46b8f43 100644 (file)
@@ -13,6 +13,27 @@ extern "C" {
 #endif
 
 /**
+ * Rip a string fromt he source and then add a NULL after the end of the string.
+ *
+ * @param source
+ *   The string to copy from.
+ * @param destination
+ *   The string to copy to.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: fl_string_dynamic_rip_nulless().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ *
+ * @see fl_string_dynamic_rip_nulless()
+ * @see fl_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_string_dynamic_rip_nulless_terminated_
+  extern f_return_status controller_string_dynamic_rip_nulless_terminated(const f_string_static_t source, const f_string_range_t range, f_string_dynamic_t *destination) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_string_dynamic_rip_nulless_terminated_
+
+/**
  * Append a string and then add a NULL after the end of the string.
  *
  * @param source
index 0f6c1033119641726e2f8be54287d7a62e2de989..5ef7a96682a50cffc2a216c66a3fa38bebc74a32 100644 (file)
@@ -154,17 +154,10 @@ extern "C" {
 
       action->line = ++cache->line_action;
 
-      status = fl_string_dynamic_rip_nulless(cache->buffer_file, cache->object_actions.array[i], &cache->name_action);
+      status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_actions.array[i], &cache->name_action);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true);
-        break;
-      }
-
-      status = fl_string_dynamic_terminate_after(&cache->name_action);
-
-      if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+        fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
         break;
       }
 
index 24ed727ef185efb506333537a52a05ec910336a1..4c19ed340b63fcbcaeadceec11710abb61eebbf1 100644 (file)
@@ -30,35 +30,37 @@ extern "C" {
   }
 #endif // _di_controller_rule_action_method_name_
 
-#ifndef _di_controller_rule_action_read_
-  f_return_status controller_rule_action_read(const controller_data_t data, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, controller_rule_action_t *action) {
+#ifndef _di_controller_rule_parameters_read_
+  f_return_status controller_rule_parameters_read(const controller_data_t data, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, f_string_dynamics_t *parameters) {
     f_status_t status = F_none;
 
+    parameters->used = 0;
+
     if (object && object->start <= object->start) {
-      status = fl_string_dynamics_increase(&action->parameters);
+      status = fl_string_dynamics_increase(parameters);
 
       if (F_status_is_error(status)) {
         fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamics_increase", F_true);
         return status;
       }
 
-      action->parameters.array[0].used = 0;
+      parameters->array[parameters->used].used = 0;
 
-      status = fl_string_dynamic_partial_append_nulless(buffer, *object, &action->parameters.array[0]);
+      status = fl_string_dynamic_partial_append_nulless(buffer, *object, &parameters->array[0]);
 
       if (F_status_is_error(status)) {
         fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
         return status;
       }
 
-      status = fl_string_dynamic_terminate_after(&action->parameters.array[0]);
+      status = fl_string_dynamic_terminate_after(&parameters->array[0]);
 
       if (F_status_is_error(status)) {
         fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
         return status;
       }
 
-      action->parameters.used++;
+      parameters->used++;
     }
 
     if (content && content->used) {
@@ -66,29 +68,36 @@ extern "C" {
 
         if (content->array[i].start > content->array[i].start) continue;
 
-        action->parameters.array[action->parameters.used].used = 0;
+        status = fl_string_dynamics_increase(parameters);
+
+        if (F_status_is_error(status)) {
+          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamics_increase", F_true);
+          return status;
+        }
+
+        parameters->array[parameters->used].used = 0;
 
-        status = fl_string_dynamic_partial_append_nulless(buffer, content->array[i], &action->parameters.array[action->parameters.used]);
+        status = fl_string_dynamic_partial_append_nulless(buffer, content->array[i], &parameters->array[parameters->used]);
 
         if (F_status_is_error(status)) {
           fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
           return status;
         }
 
-        status = fl_string_dynamic_terminate_after(&action->parameters.array[action->parameters.used]);
+        status = fl_string_dynamic_terminate_after(&parameters->array[parameters->used]);
 
         if (F_status_is_error(status)) {
           fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
           return status;
         }
 
-        action->parameters.used++;
+        parameters->used++;
       } // for
     }
 
     return F_none;
   }
-#endif // _di_controller_rule_action_read_
+#endif // _di_controller_rule_parameters_read_
 
 #ifndef _di_controller_rule_action_type_name_
   f_string_static_t controller_rule_action_type_name(const uint8_t type) {
@@ -169,59 +178,63 @@ extern "C" {
   }
 #endif // _di_controller_rule_actions_increase_by_
 
-#ifndef _di_controller_rule_actions_read_
-  f_return_status controller_rule_actions_read(const controller_data_t data, controller_cache_t *cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) {
+#ifndef _di_controller_rule_action_read_
+  f_return_status controller_rule_action_read(const controller_data_t data, const uint8_t type, const uint8_t method, controller_cache_t *cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) {
     f_status_t status = F_none;
 
-    actions->used = 0;
+    if (method == controller_rule_action_method_extended_list) {
+      cache->comments.used = 0;
+      cache->delimits.used = 0;
+      cache->content_action.used = 0;
+      cache->content_actions.used = 0;
+      cache->object_actions.used = 0;
 
-    // "script" types use the entire content and can be directly passed through.
-    if (item->type == controller_rule_item_type_script) {
-      status = controller_rule_actions_increase_by(controller_default_allocation_step, actions);
+      if (actions->size) {
+        actions->array[actions->used].parameters.used = 0;
+      }
+
+      status = fl_fss_extended_list_content_read(cache->buffer_item, range, &cache->content_action, &cache->delimits, &cache->comments);
 
       if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+        fll_error_print(data.error, F_status_set_fine(status), "fl_fss_extended_list_content_read", F_true);
       }
-      else {
-        actions->array[0].parameters.used = 0;
-
-        status = fl_string_dynamics_increase(&actions->array[0].parameters);
+      else if (status == FL_fss_found_content) {
 
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamics_increase", F_true);
-        }
-        else {
-          actions->array[0].line = cache->line_action;
+        // "script" types use the entire content and can be directly passed through.
+        if (item->type == controller_rule_item_type_script) {
+          actions->array[actions->used].parameters.used = 0;
 
-          status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->range_action, &actions->array[0].parameters.array[0]);
+          status = fl_string_dynamics_increase(&actions->array[actions->used].parameters);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append_nulless", F_true);
+            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamics_increase", F_true);
           }
+          else {
+            actions->array[actions->used].type = type;
+            actions->array[actions->used].method = method;
+            actions->array[actions->used].line = cache->line_action;
 
-          status = fl_string_dynamic_terminate_after(&actions->array[0].parameters.array[0]);
+            status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_action.array[0], &actions->array[actions->used].parameters.array[0]);
 
-          if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
-          }
-        }
-      }
+            if (F_status_is_error(status)) {
+              fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_append_nulless", F_true);
+            }
 
-      return status;
-    }
+            status = fl_string_dynamic_terminate_after(&actions->array[actions->used].parameters.array[0]);
 
-    if (actions->method == controller_rule_action_method_extended_list) {
-      cache->comments.used = 0;
-      cache->delimits.used = 0;
-      cache->content_actions.used = 0;
-      cache->object_actions.used = 0;
+            if (F_status_is_error(status)) {
+              fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+            }
+            else {
+              actions->array[actions->used].parameters.used = 1;
+              actions->used++;
+            }
+          }
 
-      status = fl_fss_extended_list_content_read(cache->buffer_item, range, &cache->content_action, &cache->delimits, &cache->comments);
+          return status;
+        }
 
-      if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "fl_fss_extended_list_content_read", F_true);
-      }
-      else if (status == FL_fss_found_content) {
+        // the object_actions and content_actions caches are being used for the purposes of getting the parameters a given the action.
         status = fll_fss_extended_read(cache->buffer_item, &cache->content_action.array[0], &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0);
 
         if (F_status_is_error(status)) {
@@ -239,6 +252,13 @@ extern "C" {
 
             for (; i < cache->object_actions.used; ++i) {
 
+              status = controller_rule_actions_increase_by(controller_default_allocation_step, actions);
+
+              if (F_status_is_error(status)) {
+                fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+                break;
+              }
+
               status = f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &actions->array[actions->used].line);
 
               if (F_status_is_error(status)) {
@@ -246,6 +266,8 @@ extern "C" {
                 break;
               }
 
+              actions->array[actions->used].type = type;
+              actions->array[actions->used].method = method;
               actions->array[actions->used].line += ++item->line;
               actions->array[actions->used].parameters.used = 0;
               actions->array[actions->used].status = F_known_not;
@@ -259,11 +281,13 @@ extern "C" {
                 break;
               }
 
-              status = controller_rule_action_read(data, cache->buffer_item, &cache->object_actions.array[i], &cache->content_actions.array[i], &actions->array[actions->used]);
+              status = controller_rule_parameters_read(data, cache->buffer_item, &cache->object_actions.array[i], &cache->content_actions.array[i], &actions->array[actions->used].parameters);
 
               actions->array[actions->used].status = controller_status_simplify(F_status_set_fine(status));
               actions->used++;
             } // for
+
+            range->start = cache->content_action.array[0].start;
           }
         }
       }
@@ -287,35 +311,30 @@ extern "C" {
           fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
         }
         else {
-          status = controller_rule_actions_increase_by(controller_default_allocation_step, actions);
+          status = f_fss_count_lines(cache->buffer_item, range->start, &actions->array[actions->used].line);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+            fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
           }
           else {
-            status = f_fss_count_lines(cache->buffer_item, range->start, &actions->array[0].line);
+            actions->array[actions->used].type = type;
+            actions->array[actions->used].method = method;
+            actions->array[actions->used].line += ++item->line;
+            actions->array[actions->used].parameters.used = 0;
+            actions->array[actions->used].status = F_known_not;
+
+            status = controller_rule_parameters_read(data, cache->buffer_item, 0, &cache->content_action, &actions->array[actions->used].parameters);
 
             if (F_status_is_error(status)) {
-              fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+              fll_error_print(data.error, F_status_set_fine(status), "controller_rule_parameters_read", F_true);
+
+              actions->array[actions->used].status = controller_status_simplify(F_status_set_fine(status));
             }
             else {
-              actions->array[0].line += ++item->line;
-              actions->array[0].parameters.used = 0;
-              actions->array[0].status = F_known_not;
-
-              status = controller_rule_action_read(data, cache->buffer_item, 0, &cache->content_action, &actions->array[0]);
-
-              if (F_status_is_error(status)) {
-                fll_error_print(data.error, F_status_set_fine(status), "controller_rule_action_read", F_true);
-
-                actions->array[0].status = controller_status_simplify(F_status_set_fine(status));
-              }
-              else {
-                actions->array[0].status = status;
-              }
-
-              actions->used = 1;
+              actions->array[actions->used].status = status;
             }
+
+            actions->used++;
           }
         }
       }
@@ -335,7 +354,7 @@ extern "C" {
 
     return status;
   }
-#endif // _di_controller_rule_actions_read_
+#endif // _di_controller_rule_action_read_
 
 #ifndef _di_controller_rule_error_print_
   void controller_rule_error_print(const fll_error_print_t output, const controller_cache_t cache, const bool item) {
@@ -369,19 +388,32 @@ extern "C" {
   }
 #endif // _di_controller_rule_error_print_
 
+#ifndef _di_controller_rule_error_need_want_wish_print_
+  void controller_rule_error_need_want_wish_print(const fll_error_print_t output, const f_string_t need_want_wish, const f_string_t value, const f_string_t why) {
+
+    if (output.verbosity != f_console_verbosity_quiet) {
+      fprintf(output.to.stream, "%c", f_string_eol_s[0]);
+      fprintf(output.to.stream, "%s%sThe %s rule '", output.context.before->string, output.prefix ? output.prefix : f_string_empty_s, need_want_wish);
+      fprintf(output.to.stream, "%s%s%s%s", output.context.after->string, output.notable.before->string, value, output.notable.after->string);
+      fprintf(output.to.stream, "%s' %s.%s%c", output.context.before->string, why, output.context.after->string, f_string_eol_s[0]);
+    }
+  }
+#endif // _di_controller_rule_error_need_want_wish_print_
+
 #ifndef _di_controller_rule_execute_
   f_return_status controller_rule_execute(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, controller_setting_t *setting) {
+    // @todo this needs the "action" in which to perform, such as "start", "stop", "restart", etc..
     // @todo
   }
 #endif // _di_controller_rule_execute_
 
 #ifndef _di_controller_rule_find_loaded_
   f_array_length_t controller_rule_find_loaded(const controller_data_t data, const controller_setting_t setting, const f_string_static_t rule_id) {
+
     f_array_length_t i = 0;
 
     for (; i < setting.rules.used; ++i) {
-
-      if (fl_string_dynamic_compare(setting.rules.array[i].id, rule_id) == F_equal_to) {
+      if (fl_string_dynamic_compare(rule_id, setting.rules.array[i].id) == F_equal_to) {
         return i;
       }
     } // for
@@ -390,17 +422,57 @@ extern "C" {
   }
 #endif // _di_controller_rule_find_loaded_
 
+#ifndef _di_controller_rule_id_construct_
+  f_return_status controller_rule_id_construct(const controller_data_t data, const f_string_static_t source, const f_string_range_t directory, const f_string_range_t basename, f_string_dynamic_t *id) {
+    f_status_t status = F_none;
+
+    id->used = 0;
+
+    status = fl_string_dynamic_partial_append_nulless(source, directory, id);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
+      return status;
+    }
+
+    status = fl_string_append(f_path_separator_s, f_path_separator_length, id);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_append", F_true);
+      return status;
+    }
+
+    status = fl_string_dynamic_partial_append_nulless(source, basename, id);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
+      return status;
+    }
+
+    status = fl_string_dynamic_terminate_after(id);
+
+    if (F_status_is_error(status)) {
+      fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_id_construct_
+
 #ifndef _di_controller_rule_item_read_
   f_return_status controller_rule_item_read(const controller_data_t data, controller_cache_t *cache, controller_rule_item_t *item) {
     f_status_t status = F_none;
 
     f_string_range_t range = f_macro_string_range_t_initialize(cache->buffer_item.used);
+    f_string_length_t last = 0;
 
-    controller_rule_actions_t *actions = 0;
-
+    uint8_t type = 0;
+    uint8_t method = 0;
     bool multiple = F_false;
 
-    for (range.start = 0; range.start < cache->buffer_item.used && range.start <= range.stop; cache->delimits.used = 0, cache->comments.used = 0) {
+    item->actions.used = 0;
+
+    for (; range.start < cache->buffer_item.used && range.start <= range.stop; last = range.start, cache->delimits.used = 0, cache->comments.used = 0) {
 
       status = fl_fss_extended_list_object_read(cache->buffer_item, &range, &cache->range_action, &cache->delimits);
 
@@ -409,23 +481,11 @@ extern "C" {
         break;
       }
 
-      if (range.start >= range.stop || range.start >= cache->buffer_item.used) {
-        if (status == FL_fss_found_object || status == FL_fss_found_object_content_not) {
-          if (data.error.verbosity != f_console_verbosity_quiet) {
-            fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-            fprintf(data.error.to.stream, "%s%sUnterminated FSS Extended List at end of rule file.%s%c", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, data.error.context.after->string, f_string_eol_s[0]);
-          }
-
-          status = F_status_set_error(FL_fss_found_object_content_not);
-        }
-
-        break;
-      }
-
       if (status == FL_fss_found_object) {
         multiple = F_true;
       }
       else {
+        range.start = last;
         multiple = F_false;
         cache->delimits.used = 0;
 
@@ -436,111 +496,107 @@ extern "C" {
           fll_error_print(data.error, F_status_set_fine(status), "fl_fss_extended_object_read", F_true);
           break;
         }
-      }
 
-      if (status == FL_fss_found_object_content_not || range.start >= range.stop || range.start >= cache->buffer_item.used) {
-        // object ended without any content.
-        break;
+        // Nothing of importance here, so continue onto the next line.
+        // @todo present an error if this line is anything but whitespace.
+        if (status != FL_fss_found_object) continue;
       }
 
-      if (status == FL_fss_found_object) {
-        status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
+      status = fl_fss_apply_delimit(cache->delimits, &cache->buffer_item);
 
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
-          break;
-        }
-
-        status = f_fss_count_lines(cache->buffer_item, cache->range_action.start, &cache->line_action);
+      if (F_status_is_error(status)) {
+        fll_error_print(data.error, F_status_set_fine(status), "fl_fss_apply_delimit", F_true);
+        break;
+      }
 
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
-          break;
-        }
+      status = f_fss_count_lines(cache->buffer_item, cache->range_action.start, &cache->line_action);
 
-        cache->line_action += ++item->line;
-        cache->name_action.used = 0;
+      if (F_status_is_error(status)) {
+        fll_error_print(data.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+        break;
+      }
 
-        status = fl_string_dynamic_rip_nulless(cache->buffer_item, cache->range_action, &cache->name_action);
+      cache->line_action += ++item->line;
+      cache->name_action.used = 0;
 
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true);
-          break;
-        }
+      status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_item, cache->range_action, &cache->name_action);
 
-        status = fl_string_dynamic_terminate_after(&cache->name_action);
+      if (F_status_is_error(status)) {
+        fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
+        break;
+      }
 
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
-          break;
-        }
+      if (fl_string_dynamic_compare_string(controller_string_create, cache->name_action, controller_string_create_length) == F_equal_to) {
+        type = controller_rule_action_type_create;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_group, cache->name_action, controller_string_group_length) == F_equal_to) {
+        type = controller_rule_action_type_group;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_kill, cache->name_action, controller_string_kill_length) == F_equal_to) {
+        type = controller_rule_action_type_kill;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_restart, cache->name_action, controller_string_restart_length) == F_equal_to) {
+        type = controller_rule_action_type_restart;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_reload, cache->name_action, controller_string_reload_length) == F_equal_to) {
+        type = controller_rule_action_type_reload;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_start, cache->name_action, controller_string_start_length) == F_equal_to) {
+        type = controller_rule_action_type_start;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_stop, cache->name_action, controller_string_stop_length) == F_equal_to) {
+        type = controller_rule_action_type_stop;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_use, cache->name_action, controller_string_use_length) == F_equal_to) {
+        type = controller_rule_action_type_use;
+      }
+      else if (fl_string_dynamic_compare_string(controller_string_user, cache->name_action, controller_string_user_length) == F_equal_to) {
+        type = controller_rule_action_type_user;
+      }
+      else {
+        if (data.warning.verbosity == f_console_verbosity_debug) {
+          fprintf(data.warning.to.stream, "%s%sUnknown rule item action '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
+          fprintf(data.warning.to.stream, "%s%s", data.warning.context.after->string, data.warning.notable.before->string);
+          f_print_dynamic(data.warning.to.stream, cache->name_action);
+          fprintf(data.warning.to.stream, "%s", data.warning.notable.after->string);
+          fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
 
-        if (fl_string_dynamic_compare_string(controller_string_create, cache->name_action, controller_string_create_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_create;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_group, cache->name_action, controller_string_group_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_group;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_kill, cache->name_action, controller_string_kill_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_kill;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_restart, cache->name_action, controller_string_restart_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_restart;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_reload, cache->name_action, controller_string_reload_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_reload;
+          controller_rule_error_print(data.warning, *cache, F_true);
         }
-        else if (fl_string_dynamic_compare_string(controller_string_start, cache->name_action, controller_string_start_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_start;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_stop, cache->name_action, controller_string_stop_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_stop;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_use, cache->name_action, controller_string_use_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_use;
-        }
-        else if (fl_string_dynamic_compare_string(controller_string_user, cache->name_action, controller_string_user_length) == F_equal_to) {
-          item->actions.type = controller_rule_action_type_user;
-        }
-        else {
-          if (data.warning.verbosity == f_console_verbosity_debug) {
-            fprintf(data.warning.to.stream, "%s%sUnknown rule item action '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-            fprintf(data.warning.to.stream, "%s%s", data.warning.context.after->string, data.warning.notable.before->string);
-            f_print_dynamic(data.warning.to.stream, cache->name_action);
-            fprintf(data.warning.to.stream, "%s", data.warning.notable.after->string);
-            fprintf(data.warning.to.stream, "%s'.%s%c", data.warning.context.before->string, data.warning.context.after->string, f_string_eol_s[0]);
 
+        continue;
+      }
 
-            controller_rule_error_print(data.warning, *cache, F_true);
+      if (multiple) {
+        if (type == controller_rule_action_type_create || type == controller_rule_action_type_group || type == controller_rule_action_type_use || type == controller_rule_action_type_user) {
+          if (data.error.verbosity != f_console_verbosity_quiet) {
+            fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
+            fprintf(data.error.to.stream, "%s%sFSS Extended List is not allowed for the rule item action '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
+            fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string);
+            f_print_dynamic(data.error.to.stream, cache->name_action);
+            fprintf(data.error.to.stream, "%s", data.error.notable.after->string);
+            fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
           }
 
-          continue;
+          status = F_status_set_error(F_supported_not);
+          break;
         }
 
-        if (multiple) {
-          if (item->actions.type == controller_rule_action_type_create || item->actions.type == controller_rule_action_type_group || item->actions.type == controller_rule_action_type_use || item->actions.type == controller_rule_action_type_user) {
-            if (data.error.verbosity != f_console_verbosity_quiet) {
-              fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-              fprintf(data.error.to.stream, "%s%sFSS Extended List is not allowed for the rule item action '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-              fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string);
-              f_print_dynamic(data.error.to.stream, cache->name_action);
-              fprintf(data.error.to.stream, "%s", data.error.notable.after->string);
-              fprintf(data.error.to.stream, "%s'.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-            }
-
-            status = F_status_set_error(F_supported_not);
-            break;
-          }
+        method = controller_rule_action_method_extended_list;
+      }
+      else {
+        method = controller_rule_action_method_extended;
+      }
 
-          item->actions.method = controller_rule_action_method_extended_list;
-        }
-        else {
-          item->actions.method = controller_rule_action_method_extended;
-        }
+      status = controller_rule_actions_increase_by(controller_default_allocation_step, &item->actions);
 
-        status = controller_rule_actions_read(data, cache, item, &item->actions, &range);
-        if (F_status_is_error(status)) break;
+      if (F_status_is_error(status)) {
+        fll_error_print(data.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+        break;
       }
+
+      status = controller_rule_action_read(data, type, method, cache, item, &item->actions, &range);
+      if (F_status_is_error(status)) break;
     } // for
 
     return status;
@@ -611,7 +667,7 @@ extern "C" {
       status = fl_string_append(setting.path_setting.string, setting.path_setting.used, path);
 
       if (F_status_is_error_not(status)) {
-        status = fl_string_append(f_path_separator, f_path_separator_length, path);
+        status = fl_string_append(f_path_separator_s, f_path_separator_length, path);
       }
     }
 
@@ -620,7 +676,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status)) {
-      status = fl_string_append(f_path_separator, f_path_separator_length, path);
+      status = fl_string_append(f_path_separator_s, f_path_separator_length, path);
     }
 
     if (F_status_is_error_not(status)) {
@@ -628,7 +684,7 @@ extern "C" {
     }
 
     if (F_status_is_error_not(status)) {
-      status = fl_string_append(f_path_separator, f_path_separator_length, path);
+      status = fl_string_append(f_path_separator_s, f_path_separator_length, path);
     }
 
     if (F_status_is_error_not(status)) {
@@ -716,7 +772,7 @@ extern "C" {
     status = fl_string_append(controller_string_rules, controller_string_rules_length, &cache->name_file);
 
     if (F_status_is_error_not(status)) {
-      status = fl_string_append(f_path_separator, f_path_separator_length, &cache->name_file);
+      status = fl_string_append(f_path_separator_s, f_path_separator_length, &cache->name_file);
     }
 
     if (F_status_is_error(status)) {
@@ -761,6 +817,10 @@ extern "C" {
 
     controller_rule_t *rule = &setting->rules.array[index];
 
+    if (simulate && data.parameters[controller_parameter_validate].result == f_console_result_found) {
+      controller_rule_simulate(data, *cache, index, setting);
+    }
+
     {
       f_array_length_t j = 0;
       f_array_length_t k = 0;
@@ -772,6 +832,12 @@ extern "C" {
         &rule->wish,
       };
 
+      const f_string_t strings[] = {
+        "needed",
+        "wanted",
+        "wished for",
+      };
+
       for (i = 0; i < 3; ++i) {
 
         for (j = 0; j < dynamics[i]->used; ++j) {
@@ -779,27 +845,16 @@ extern "C" {
 
           if (at == setting->rules.used) {
             if (i == 0) {
-
-              if (data.error.verbosity != f_console_verbosity_quiet) {
-                fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.error.to.stream, "%s%sThe needed rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-                fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
-                fprintf(data.error.to.stream, "%s' was not found.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
-                controller_rule_error_print(data.error, *cache, F_true);
-              }
+              controller_rule_error_need_want_wish_print(data.error, strings[i], dynamics[i]->array[j].string, "was not found");
 
               status = F_status_set_error(F_found_not);
+              controller_rule_error_print(data.error, *cache, F_true);
 
               if (!simulate) break;
             }
             else {
               if (data.warning.verbosity == f_console_verbosity_debug) {
-                fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
-                fprintf(data.warning.to.stream, "%s%sThe %s rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s, i == 1 ? "wanted" : "wished for");
-                fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
-                fprintf(data.warning.to.stream, "%s' was not found.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
+                controller_rule_error_need_want_wish_print(data.warning, strings[i], dynamics[i]->array[j].string, "was not found");
                 controller_rule_error_print(data.warning, *cache, F_true);
               }
             }
@@ -817,6 +872,7 @@ extern "C" {
 
               if (F_status_is_error(status)) {
                 fll_error_print(data.error, F_status_set_fine(status), "fl_type_array_lengths_increase_by", F_true);
+                controller_rule_error_print(data.error, *cache, F_true);
 
                 // always exit on memory errors, even in simulate mode.
                 break;
@@ -859,14 +915,8 @@ extern "C" {
               if (F_status_is_error(status)) {
                 if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
 
-                  if (data.error.verbosity != f_console_verbosity_quiet) {
-                    fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                    fprintf(data.error.to.stream, "%s%sThe %s rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, i ? "wanted" : "needed");
-                    fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
-                    fprintf(data.error.to.stream, "%s' failed during execution.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
-                    controller_rule_error_print(data.error, *cache, F_true);
-                  }
+                  controller_rule_error_need_want_wish_print(data.error, strings[i], dynamics[i]->array[j].string, "failed during execution");
+                  controller_rule_error_print(data.error, *cache, F_true);
 
                   if (!simulate || F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
                     break;
@@ -874,11 +924,7 @@ extern "C" {
                 }
                 else {
                   if (data.warning.verbosity == f_console_verbosity_debug) {
-                    fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
-                    fprintf(data.warning.to.stream, "%s%sThe wished for rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-                    fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
-                    fprintf(data.warning.to.stream, "%s' failed during execution.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
+                    controller_rule_error_need_want_wish_print(data.warning, strings[i], dynamics[i]->array[j].string, "failed during execution");
                     controller_rule_error_print(data.warning, *cache, F_true);
                   }
                 }
@@ -887,51 +933,32 @@ extern "C" {
             else if (F_status_is_error(setting->rules.array[at].status)) {
 
               if (i == 0 || i == 1) {
-
-                if (data.error.verbosity != f_console_verbosity_quiet) {
-                  fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
-                  fprintf(data.error.to.stream, "%s%sThe %s rule '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s, i ? "wanted" : "needed");
-                  fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.error.notable.after->string);
-                  fprintf(data.error.to.stream, "%s' is in a failed state.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
-                  controller_rule_error_print(data.error, *cache, F_true);
-                }
+                controller_rule_error_need_want_wish_print(data.error, strings[i], dynamics[i]->array[j].string, "is in a failed state");
 
                 status = F_status_set_error(F_found_not);
+                controller_rule_error_print(data.error, *cache, F_true);
 
                 if (!simulate) break;
               }
               else {
                 if (data.warning.verbosity == f_console_verbosity_debug) {
-                  fprintf(data.warning.to.stream, "%c", f_string_eol_s[0]);
-                  fprintf(data.warning.to.stream, "%s%sThe wished for rule '", data.warning.context.before->string, data.warning.prefix ? data.warning.prefix : f_string_empty_s);
-                  fprintf(data.warning.to.stream, "%s%s%s%s", data.warning.context.after->string, data.error.notable.before->string, dynamics[i]->array[j].string, data.warning.notable.after->string);
-                  fprintf(data.warning.to.stream, "%s' is in a failed state.%s%c", data.warning.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
-
+                  controller_rule_error_need_want_wish_print(data.warning, strings[i], dynamics[i]->array[j].string, "is in a failed state");
                   controller_rule_error_print(data.warning, *cache, F_true);
                 }
               }
             }
           }
-        }
+        } // for
 
-        if (F_status_is_error(status)) break;
+        if (F_status_is_error(status) && !simulate) break;
       } // for
     }
 
-    if (F_status_is_error_not(status)) {
-      if (simulate) {
-        controller_rule_simulate(data, *cache, index, setting);
-      }
-      else {
-        status = controller_rule_execute(data, *cache, index, setting);
-
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "controller_rule_execute", F_true);
-          controller_rule_error_print(data.error, *cache, F_true);
+    if (!simulate && F_status_is_error_not(status)) {
+      status = controller_rule_execute(data, *cache, index, setting);
 
-          return status;
-        }
+      if (F_status_is_error(status)) {
+        fll_error_print(data.error, F_status_set_fine(status), "controller_rule_execute", F_true);
       }
     }
 
@@ -1077,17 +1104,10 @@ extern "C" {
 
           rule->items.array[rule->items.used].line = ++cache->line_item;
 
-          status = fl_string_dynamic_rip_nulless(cache->buffer_file, cache->object_items.array[i], &cache->name_item);
-
-          if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true);
-            break;
-          }
-
-          status = fl_string_dynamic_terminate_after(&cache->name_item);
+          status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_file, cache->object_items.array[i], &cache->name_item);
 
           if (F_status_is_error(status)) {
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
+            fll_error_print(data.error, F_status_set_fine(status), "controller_string_dynamic_rip_nulless_terminated", F_true);
             break;
           }
 
@@ -1422,13 +1442,12 @@ extern "C" {
         setting_value->used = 0;
 
         if (type == controller_rule_setting_type_control_group || type == controller_rule_setting_type_name) {
-          status = fl_string_dynamic_rip_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
+
+          status = controller_string_dynamic_rip_nulless_terminated(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
 
           if (F_status_is_error(status)) {
             setting_value->used = 0;
 
-            fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_rip_nulless", F_true);
-
             if (F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
               return status;
             }
@@ -1447,7 +1466,9 @@ extern "C" {
             if (status == F_false) {
               fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
               fprintf(data.error.to.stream, "%s%sRule setting has an invalid name '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-              fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, setting_value->string, data.error.notable.after->string);
+              fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string);
+              f_print_dynamic(data.error.to.stream, *setting_value);
+              fprintf(data.error.to.stream, "%s", data.error.notable.after->string);
               fprintf(data.error.to.stream, "%s', there must be at least 1 graph character.%s%c", data.error.context.before->string, data.error.context.after->string, f_string_eol_s[0]);
 
               controller_rule_error_print(data.error, *cache, F_false);
@@ -1634,21 +1655,7 @@ extern "C" {
         continue;
       }
 
-      setting_values->array[setting_values->used].used = 0;
-      setting_values->array[setting_values->used + 1].used = 0;
-
-      status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &setting_values->array[setting_values->used]);
-
-      if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
-      }
-      else {
-        status = fl_string_dynamic_terminate_after(&setting_values->array[setting_values->used]);
-
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
-        }
-      }
+      status = controller_rule_id_construct(data, cache->buffer_item, cache->content_actions.array[i].array[0], cache->content_actions.array[i].array[1], &setting_values->array[setting_values->used]);
 
       if (F_status_is_error(status)) {
         setting_values->array[setting_values->used].used = 0;
@@ -1665,42 +1672,12 @@ extern "C" {
         continue;
       }
 
-      status = fl_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[1], &setting_values->array[setting_values->used + 1]);
-
-      if (F_status_is_error(status)) {
-        fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_partial_append_nulless", F_true);
-      }
-      else {
-        status = fl_string_dynamic_terminate_after(&setting_values->array[setting_values->used + 1]);
-
-        if (F_status_is_error(status)) {
-          fll_error_print(data.error, F_status_set_fine(status), "fl_string_dynamic_terminate_after", F_true);
-        }
-      }
-
-      if (F_status_is_error(status)) {
-        setting_values->array[setting_values->used].used = 0;
-        setting_values->array[setting_values->used + 1].used = 0;
-
-        if (F_status_set_fine(status) == F_memory_not || F_status_set_fine(status) == F_memory_allocation || F_status_set_fine(status) == F_memory_reallocation) {
-          return status;
-        }
-
-        if (F_status_is_error_not(status_return)) {
-          status_return = status;
-        }
-
-        controller_rule_error_print(data.error, *cache, F_false);
-        continue;
-      }
-
       cache->buffer_path.used = 0;
 
-      status = f_file_name_base(setting_values->array[setting_values->used + 1].string, setting_values->array[setting_values->used + 1].used, &cache->buffer_path);
+      status = f_file_name_base(setting_values->array[setting_values->used].string, setting_values->array[setting_values->used + 1].used, &cache->buffer_path);
 
       if (F_status_is_error(status)) {
         setting_values->array[setting_values->used].used = 0;
-        setting_values->array[setting_values->used + 1].used = 0;
 
         fll_error_print(data.error, F_status_set_fine(status), "f_file_name_base", F_true);
 
@@ -1716,12 +1693,14 @@ extern "C" {
         continue;
       }
 
-      if (fl_string_dynamic_compare(setting_values->array[setting_values->used + 1], cache->buffer_path) == F_equal_to_not) {
+      if (fl_string_dynamic_partial_compare_string(cache->buffer_item.string, cache->buffer_path, cache->buffer_item.used, cache->content_actions.array[i].array[1]) == F_equal_to_not) {
 
         if (data.error.verbosity != f_console_verbosity_quiet) {
           fprintf(data.error.to.stream, "%c", f_string_eol_s[0]);
           fprintf(data.error.to.stream, "%s%sThe rule item action second parameter '", data.error.context.before->string, data.error.prefix ? data.error.prefix : f_string_empty_s);
-          fprintf(data.error.to.stream, "%s%s%s%s", data.error.context.after->string, data.error.notable.before->string, setting_values->array[setting_values->used + 1].string, data.error.notable.after->string);
+          fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string);
+          f_print_dynamic_partial(data.error.to.stream, cache->buffer_item, cache->content_actions.array[i].array[1]);
+          fprintf(data.error.to.stream, "%s", data.error.notable.after->string);
           fprintf(data.error.to.stream, "%s' must be a base path name, such as %llu '", data.error.context.before->string, cache->buffer_path.used);
           fprintf(data.error.to.stream, "%s%s", data.error.context.after->string, data.error.notable.before->string);
           f_print_dynamic(data.error.to.stream, cache->buffer_path);
@@ -1730,12 +1709,11 @@ extern "C" {
         }
 
         setting_values->array[setting_values->used].used = 0;
-        setting_values->array[setting_values->used + 1].used = 0;
 
         continue;
       }
 
-      setting_values->used += 2;
+      setting_values->used++;
     } // for
 
     return status_return;
@@ -1745,6 +1723,8 @@ extern "C" {
 #ifndef _di_controller_rule_simulate_
   void controller_rule_simulate(const controller_data_t data, const controller_cache_t cache, const f_array_length_t index, controller_setting_t *setting) {
 
+    // @todo this needs the "action" in which to perform, such as "start", "stop", "restart", etc..
+
     controller_rule_t * const rule = &setting->rules.array[index];
 
     fprintf(data.output.stream, "%c", f_string_eol_s[0]);
@@ -1832,38 +1812,65 @@ extern "C" {
 
     fprintf(data.output.stream, "  }%c", f_string_eol_s[0]);
 
-    fprintf(data.output.stream, "  %s%s%s {%c", data.context.set.important.before->string, controller_string_item, data.context.set.important.after->string, f_string_eol_s[0]);
-
     if (rule->items.used) {
       controller_rule_action_t *action = 0;
       controller_rule_item_t *item = 0;
+      f_string_dynamic_t *parameter = 0;
 
       f_array_length_t j = 0;
+      f_array_length_t k = 0;
 
       for (i = 0; i < rule->items.used; ++i) {
 
         item = &rule->items.array[i];
 
-        fprintf(data.output.stream, "    %s%s%s %s%c", data.context.set.important.before->string, controller_string_type, data.context.set.important.after->string, controller_rule_item_type_name(item->type).string, f_string_eol_s[0]);
+        fprintf(data.output.stream, "  %s%s%s {%c", data.context.set.important.before->string, controller_string_item, data.context.set.important.after->string, f_string_eol_s[0]);
 
-        fprintf(data.output.stream, "    %s%s%s {%c", data.context.set.important.before->string, controller_string_action, data.context.set.important.after->string, f_string_eol_s[0]);
+        fprintf(data.output.stream, "    %s%s%s %s%c", data.context.set.important.before->string, controller_string_type, data.context.set.important.after->string, controller_rule_item_type_name(item->type).string, f_string_eol_s[0]);
 
         for (j = 0; j < item->actions.used; ++j) {
 
           action = &item->actions.array[j];
 
-          // action method
-          // action type
-          // action parameters
-        }
+          fprintf(data.output.stream, "    %s%s%s {%c", data.context.set.important.before->string, controller_string_action, data.context.set.important.after->string, f_string_eol_s[0]);
 
-        fprintf(data.output.stream, "    }%c", f_string_eol_s[0]);
-      } // for
+          fprintf(data.output.stream, "      %s%s%s %s%c", data.context.set.important.before->string, controller_string_type, data.context.set.important.after->string, controller_rule_action_type_name(action->type).string, f_string_eol_s[0]);
+          fprintf(data.output.stream, "      %s%s%s %s%c", data.context.set.important.before->string, controller_string_method, data.context.set.important.after->string, controller_rule_action_method_name(action->method).string, f_string_eol_s[0]);
+
+          if (action->method == controller_rule_action_method_extended_list && item->type == controller_rule_item_type_script) {
+            fprintf(data.output.stream, "      %s%s%s {%c", data.context.set.important.before->string, controller_string_parameter, data.context.set.important.after->string, f_string_eol_s[0]);
+
+            parameter = &action->parameters.array[0];
+
+            if (parameter->used) {
+              fprintf(data.output.stream, "        ");
+
+              for (k = 0; k < parameter->used; ++k) {
+
+                fprintf(data.output.stream, "%c", parameter->string[k]);
+
+                if (parameter->string[k] == f_fss_eol && k + 1 < parameter->used) {
+                  fprintf(data.output.stream, "        ");
+                }
+              } // for
+            }
 
-      fprintf(data.output.stream, "  }%c", f_string_eol_s[0]);
+            fprintf(data.output.stream, "      }%c", f_string_eol_s[0]);
+          }
+          else {
+            for (k = 0; k < action->parameters.used; ++k) {
+              fprintf(data.output.stream, "      %s%s%s %s%c", data.context.set.important.before->string, controller_string_parameter, data.context.set.important.after->string, action->parameters.array[k].string, f_string_eol_s[0]);
+            } // for
+          }
+
+          fprintf(data.output.stream, "    }%c", f_string_eol_s[0]);
+        } // for
+
+        fprintf(data.output.stream, "  }%c", f_string_eol_s[0]);
+      } // for
     }
 
-    fprintf(data.output.stream, "}%c%c", f_string_eol_s[0], f_string_eol_s[0]);
+    fprintf(data.output.stream, "}%c", f_string_eol_s[0]);
 
     setting->rules.array[index].status = F_complete;
   }
index 48d659fecd7b42afc03116e7e575a67a85998baf..42eba62af5760a60018c4287a457b06fd57e03d9 100644 (file)
@@ -27,7 +27,7 @@ extern "C" {
 #endif // _di_controller_rule_action_method_name_
 
 /**
- * Read the rule action.
+ * Read the parameters for some rule action.
  *
  * The object and content ranges are merged together (in that order) as the action parameters.
  *
@@ -41,8 +41,8 @@ extern "C" {
  * @param content
  *   (optional) The ranges representing where the content is found within the buffer.
  *   Set pointer address to 0 to disable.
- * @param action
- *   The processed action.
+ * @param parameters
+ *   The processed parameters.
  *
  * @return
  *   F_none on success.
@@ -55,9 +55,9 @@ extern "C" {
  * @see fl_string_dynamic_partial_append_nulless()
  * @see fl_string_dynamics_increase()
  */
-#ifndef _di_controller_rule_action_read_
-  extern f_return_status controller_rule_action_read(const controller_data_t data, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, controller_rule_action_t *action) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_rule_action_read_
+#ifndef _di_controller_rule_parameters_read_
+  extern f_return_status controller_rule_parameters_read(const controller_data_t data, const f_string_static_t buffer, f_fss_object_t *object, f_fss_content_t *content, f_string_dynamics_t *parameters) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_parameters_read_
 
 /**
  * Get a string representing the rule action type.
@@ -94,10 +94,16 @@ extern "C" {
 #endif // _di_controller_rule_actions_increase_by_
 
 /**
- * Read the content within the buffer, extracting all valid actions for the current processed item.
+ * Read the content within the buffer, processing the action (or a set of within a list) for the given item.
+ *
+ * This will automatically increase the size of the actions array as needed.
  *
  * @param data
  *   The program data.
+ * @param type
+ *   The action type for this action or set of actions.
+ * @param method
+ *   The action method for this action or set of actions.
  * @param cache
  *   A structure for containing and caching relevant data.
  * @param item
@@ -111,17 +117,17 @@ extern "C" {
  * @return
  *   F_none on success.
  *
- *   Errors (with error bit) from: controller_rule_action_read().
  *   Errors (with error bit) from: controller_rule_actions_increase_by().
+ *   Errors (with error bit) from: controller_rule_parameters_read().
  *   Errors (with error bit) from: f_fss_count_lines().
  *
- * @see controller_rule_action_read()
  * @see controller_rule_actions_increase_by()
+ * @see controller_rule_parameters_read()
  * @see f_fss_count_lines()
  */
-#ifndef _di_controller_rule_actions_read_
-  extern f_return_status controller_rule_actions_read(const controller_data_t data, controller_cache_t *cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) f_gcc_attribute_visibility_internal;
-#endif // _di_controller_rule_actions_read_
+#ifndef _di_controller_rule_action_read_
+  extern f_return_status controller_rule_action_read(const controller_data_t data, const uint8_t type, const uint8_t method, controller_cache_t *cache, controller_rule_item_t *item, controller_rule_actions_t *actions, f_string_range_t *range) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_action_read_
 
 /**
  * Print additional error/warning information in addition to existing error.
@@ -136,7 +142,7 @@ extern "C" {
  *   If TRUE, then this error is associated with an item.
  *   If FALSE, then this error is associated with a rule setting.
  *
- * @see controller_rule_actions_read()
+ * @see controller_rule_action_read()
  * @see controller_rule_item_read()
  * @see controller_rule_items_read()
  * @see controller_rule_read()
@@ -147,6 +153,22 @@ extern "C" {
 #endif // _di_controller_rule_error_print_
 
 /**
+ * Print an error or warning message related to need/want/wish settings of some rule.
+ *
+ * @param output
+ *   The error or warning output structure.
+ * @param need_want_wish
+ *   The appropriate string, such as "needs", "wants", or "wishes for" to print when describing this error/warning.
+ * @param value
+ *   The value that is the error or warning.
+ * @param why
+ *   A short explanation on why this is an error or warning.
+ */
+#ifndef _di_controller_rule_error_need_want_wish_print_
+  extern void controller_rule_error_need_want_wish_print(const fll_error_print_t output, const f_string_t need_want_wish, const f_string_t value, const f_string_t why) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_error_need_want_wish_print_
+
+/**
  * Perform an execution of the given rule.
  *
  * @param data
@@ -190,6 +212,34 @@ extern "C" {
 #endif // _di_controller_rule_find_loaded_
 
 /**
+ * Construct an id from two distinct strings found within a single given source.
+ *
+ * @param data
+ *   The program data.
+ * @param source
+ *   The source string that both the directory and basename are copied from.
+ * @param directory
+ *   A range within the source representing the directory part of a rule id.
+ * @param basename
+ *   A range within the source representing the basename part of a rule id.
+ * @param id
+ *   The constructed id.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   Errors (with error bit) from: fl_string_dynamic_partial_append_nulless().
+ *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
+ *
+ * @see f_string_append()
+ * @see fl_string_dynamic_partial_append_nulless()
+ * @see fl_string_dynamic_terminate_after()
+ */
+#ifndef _di_controller_rule_id_construct_
+  extern f_return_status controller_rule_id_construct(const controller_data_t data, const f_string_static_t source, const f_string_range_t directory, const f_string_range_t basename, f_string_dynamic_t *id) f_gcc_attribute_visibility_internal;
+#endif // _di_controller_rule_id_construct_
+
+/**
  * Read the content within the buffer, extracting all valid items after determining their type for some rule file.
  *
  * This will perform additional FSS read functions as appropriate.
@@ -208,7 +258,7 @@ extern "C" {
  *   Errors (with error bit) from: fl_string_dynamic_partial_append_nulless().
  *   Errors (with error bit) from: fl_string_dynamic_terminate_after().
  *
- * @see controller_rule_actions_read()
+ * @see controller_rule_action_read()
  * @see f_fss_count_lines()
  * @see fl_string_dynamic_partial_append_nulless()
  * @see fl_string_dynamic_terminate_after()
index 6da0273f754025d6126d5f80560f312cb8b5573e..f6d755ea518d4f7a0556baaede1abeb430e1f237 100644 (file)
@@ -33,17 +33,18 @@ Entry Documentation:
   Adding "ready" essentially specifies a point in time in the Entry in which things are expected to be safe for such basic operations.
 
   The "rule" Action immediately executes a named rule file.
-  The first Action Parameter represents the rule ID, which is a relative path the rule file is to be found, without the file extension.
-    - Do not include leading or trailing slashes in the name.
+  The first Action Parameter represents the rule directory, which is a relative directory path the rule file is to be found.
+    - Do not include leading or trailing slashes.
     - This is relative to the settings rules directory.
-    - For example the rule ID "example/my" would be found in "/etc/controller/settings/rules/example/my.rule" (assuming the directory structure).
-  The second Action Parameter represents the basename for the file representing the desired rule.
-  The directory is relative to the settings, such that if the controller rule settings are found in "/etc/controller/rules/", then for a directory called "[directory]" and a rule basename of "[filename]", the resulting path would be: "/etc/controller/rules/[directory]/[filename].rule"
+  The second Action Parameter represents the basename for the rule file, without the file extension.
+    - This must not have any directory paths.
   The remaining Action Parameters may be specified in any order\:
     - "asynchronous": Designates that execution will not block (wait).
     - "require": Designates that this rule must succeed or trigger execution of failsafe.
     - "wait": Designates that this rule will not execute until all other Actions before this (including "asynchronous" ones) finish executing (in a top-down manner).
 
+  The full path to the "rule" is relative to the settings, such that if the controller rule settings are found in "/etc/controller/rules/", then for a directory called "[directory]" and a rule basename of "[basename]", the resulting path would be: "/etc/controller/rules/[directory]/[basename].rule"
+
   It is important to note that for any given "rule", execution within that "rule" may be internally asynchronous (even if the "rule" is synchronous).
   For example, a service that is often called a daemon will execute in the background.
   Until that execution succeeds and the daemon goes into the background the representing rule will block.