]> Kevux Git Server - controller/commitdiff
Progress: Continue migrating the project.
authorKevin Day <kevin@kevux.org>
Tue, 7 May 2024 03:35:34 +0000 (22:35 -0500)
committerKevin Day <kevin@kevux.org>
Tue, 7 May 2024 03:35:34 +0000 (22:35 -0500)
33 files changed:
data/build/settings
sources/c/main/common/print.c
sources/c/main/common/print.h
sources/c/main/common/type/cache.c
sources/c/main/common/type/cache.h
sources/c/main/controller.h
sources/c/main/print/rule.c [new file with mode: 0644]
sources/c/main/print/rule.h [new file with mode: 0644]
sources/c/main/rule.c
sources/c/main/rule.h
sources/c/main/rule/action.c [new file with mode: 0644]
sources/c/main/rule/action.h [new file with mode: 0644]
sources/c/main/rule/execute.c [new file with mode: 0644]
sources/c/main/rule/execute.h [new file with mode: 0644]
sources/c/main/rule/expand.c [new file with mode: 0644]
sources/c/main/rule/expand.h [new file with mode: 0644]
sources/c/main/rule/instance.c
sources/c/main/rule/instance.h
sources/c/main/rule/is.c [new file with mode: 0644]
sources/c/main/rule/is.h [new file with mode: 0644]
sources/c/main/rule/item.c [new file with mode: 0644]
sources/c/main/rule/item.h [new file with mode: 0644]
sources/c/main/rule/parameter.c [new file with mode: 0644]
sources/c/main/rule/parameter.h [new file with mode: 0644]
sources/c/main/rule/read.c [new file with mode: 0644]
sources/c/main/rule/read.h [new file with mode: 0644]
sources/c/main/rule/setting.c [new file with mode: 0644]
sources/c/main/rule/setting.h [new file with mode: 0644]
sources/c/main/rule/validate.c [new file with mode: 0644]
sources/c/main/rule/validate.h [new file with mode: 0644]
sources/c/main/rule/wait.c [new file with mode: 0644]
sources/c/main/rule/wait.h [new file with mode: 0644]
sources/c/main/thread/is.h

index 6dd2c98b1b68af7ab456cd5b3aec34c2b1b18d14..3157770156845794ce0c1770f8526b53308ac52b 100644 (file)
@@ -43,8 +43,9 @@ build_libraries-monolithic -lfll
 build_sources_library main/common.c main/common/define.c main/common/enumeration.c main/common/print.c main/common/string.c main/common/type.c
 build_sources_library main/common/type/cache.c main/common/type/control.c main/common/type/entry.c main/common/type/global.c main/common/type/lock.c main/common/type/instance.c main/common/type/program.c main/common/type/rule.c main/common/type/thread.c
 build_sources_library main/common/string/general.c main/common/string/rule.c
-build_sources_library main/path.c main/rule.c main/rule/instance.c
-build_sources_library main/print/data.c main/print/debug.c main/print/error.c main/print/lock.c main/print/message.c main/print/verbose.c main/print/warning.c
+build_sources_library main/instance.c main/path.c
+build_sources_library main/rule.c main/rule/action.c main/rule/execute.c main/rule/expand.c main/rule/instance.c main/rule/is.c main/rule/item.c main/rule/parameter.c main/rule/read.c main/rule/setting.c main/rule/validate.c main/rule/wait.c
+build_sources_library main/print/data.c main/print/debug.c main/print/error.c main/print/lock.c main/print/message.c main/print/rule.c main/print/verbose.c main/print/warning.c
 build_sources_library main/signal.c main/time.c main/thread.c main/thread/instance.c main/thread/is.c
 
 build_sources_headers main/common.h main/controller.h main/common/define.h main/common/enumeration.h main/common/print.h main/common/string.h main/common/thread.h main/common/type.h
@@ -52,8 +53,9 @@ build_sources_headers main/common/define/control.h main/common/define/entry.h ma
 build_sources_headers main/common/enumeration/control.h main/common/enumeration/entry.h main/common/enumeration/instance.h main/common/enumeration/program.h main/common/enumeration/rule.h main/common/enumeration/thread.h
 build_sources_headers main/common/string/general.h main/common/string/rule.h
 build_sources_headers main/common/type/cache.h main/common/type/control.h main/common/type/entry.h main/common/type/global.h main/common/type/lock.h main/common/type/instance.h main/common/type/program.h main/common/type/rule.h main/common/type/thread.h
-build_sources_headers main/path.h main/rule.h main/rule/instance.h
-build_sources_headers main/print/data.h main/print/debug.h main/print/error.h main/print/lock.h main/print/message.h main/print/verbose.h main/print/warning.h
+build_sources_headers main/instance.h main/path.h
+build_sources_headers main/rule.h main/rule/action.h main/rule/execute.h main/rule/expand.h main/rule/instance.h main/rule/is.h main/rule/item.h main/rule/parameter.h main/rule/read.h main/rule/setting.h main/rule/validate.h main/rule/wait.h
+build_sources_headers main/print/data.h main/print/debug.h main/print/error.h main/print/lock.h main/print/message.h main/print/rule.h main/print/verbose.h main/print/warning.h
 build_sources_headers main/signal.h main/time.h main/thread.h main/thread/instance.h main/thread/is.h
 
 build_sources_documentation man
index c3cef5e24f709182963b23c2b6150cfe4083d081..94f9f4f8ea79f993cd9adca1927c69673ea262cb 100644 (file)
@@ -6,12 +6,25 @@ extern "C" {
 
 #ifndef _di_controller_f_a_
   const f_string_t controller_f_a[] = {
-    "f_controller_path_canonical_relative",
+    "controller_rule_actions_increase_by",
+    "controller_path_canonical_relative",
     "f_console_parameter_process",
+    "f_fss_apply_delimit",
+    "f_fss_count_lines",
+    "f_memory_array_increase",
     "f_path_current",
+    "f_rip_dynamic_partial",
     "f_string_append_assure",
     "f_string_dynamic_append",
+    "f_string_dynamic_partial_append_nulless",
+    "f_string_dynamic_partial_mash_nulless",
     "f_thread_create",
+    "fl_conversion_dynamic_partial_to_signed_detect",
+    "fl_fss_extended_list_content_read",
+    "fl_iki_read",
+    "fll_fss_extended_read",
+    "fll_fss_extended_content_read",
+    "fll_fss_extended_list_content_read",
     "fll_program_parameter_process_context_standard",
     "fll_program_parameter_process_verbosity_standard",
   };
index 09640ba8f2ebf33cf95723ca2fb57a115b3db7f2..dd5543dec82168863b37d52e62fc76b0a642ca1e 100644 (file)
@@ -39,12 +39,25 @@ extern "C" {
  */
 #ifndef _di_controller_f_e_
   enum {
+    controller_f_controller_rule_actions_increase_by_e,
     controller_f_controller_path_canonical_relative_e,
     controller_f_f_console_parameter_process_e,
+    controller_f_f_fss_apply_delimit_e,
+    controller_f_f_fss_count_lines_e,
+    controller_f_f_memory_array_increase_e,
     controller_f_f_path_current_e,
+    controller_f_f_rip_dynamic_partial_e,
     controller_f_f_string_append_assure_e,
     controller_f_f_string_dynamic_append_e,
+    controller_f_f_string_dynamic_partial_append_nulless_e,
+    controller_f_f_string_dynamic_partial_mash_nulless_e,
     controller_f_f_thread_create_e,
+    controller_f_fl_conversion_dynamic_partial_to_signed_detect_e,
+    controller_f_fl_fss_extended_list_content_read_e,
+    controller_f_fl_iki_read_e,
+    controller_f_fll_fss_extended_read_e,
+    controller_f_fll_fss_extended_content_read_e,
+    controller_f_fll_fss_extended_list_content_read_e,
     controller_f_fll_program_parameter_process_context_standard_e,
     controller_f_fll_program_parameter_process_verbosity_standard_e,
   }; // enum
index 8ef1eb2cf1ea684c7ae57d81d615aeb72d9a0e3b..d495e7d986646487b94fd7b03575ba2ab9051c14 100644 (file)
@@ -10,9 +10,40 @@ extern "C" {
     if (!cache) return;
 
     f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->buffer.string, &cache->buffer.used, &cache->buffer.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->buffer_file.string, &cache->buffer_file.used, &cache->buffer_file.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->buffer_item.string, &cache->buffer_item.used, &cache->buffer_item.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->buffer_path.string, &cache->buffer_path.used, &cache->buffer_path.size);
+
+    f_memory_arrays_resize(0, sizeof(f_string_dynamic_t), (void **) &cache->expanded.array, &cache->expanded.used, &cache->expanded.size, &f_string_dynamics_delete_callback);
+
+    f_memory_array_resize(0, sizeof(f_number_unsigned_t), (void **) &cache->ats.array, &cache->ats.used, &cache->ats.size);
+    f_memory_array_resize(0, sizeof(f_number_unsigned_t), (void **) &cache->stack.array, &cache->stack.used, &cache->stack.size);
+    f_memory_array_resize(0, sizeof(f_number_unsigned_t), (void **) &cache->delimits.array, &cache->delimits.used, &cache->delimits.size);
+
+    f_memory_array_resize(0, sizeof(f_range_t), (void **) &cache->comments.array, &cache->comments.used, &cache->comments.size);
+    f_memory_array_resize(0, sizeof(f_range_t), (void **) &cache->content_action.array, &cache->content_action.used, &cache->content_action.size);
+    f_memory_array_resize(0, sizeof(f_range_t), (void **) &cache->object_actions.array, &cache->object_actions.used, &cache->object_actions.size);
+    f_memory_array_resize(0, sizeof(f_range_t), (void **) &cache->object_items.array, &cache->object_items.used, &cache->object_items.size);
+
+    f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &cache->content_actions.array, &cache->content_actions.used, &cache->content_actions.size, &f_rangess_delete_callback);
+    f_memory_arrays_resize(0, sizeof(f_ranges_t), (void **) &cache->content_items.array, &cache->content_items.used, &cache->content_items.size, &f_rangess_delete_callback);
+
+    controller_cache_action_delete_simple(&cache->action);
   }
 #endif // _di_controller_cache_delete_
 
+#ifndef _di_controller_cache_action_delete_
+  void controller_cache_action_delete(controller_cache_action_t * const cache) {
+
+    if (!cache) return;
+
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->name_action.string, &cache->name_action.used, &cache->name_action.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->name_file.string, &cache->name_file.used, &cache->name_file.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->name_item.string, &cache->name_item.used, &cache->name_item.size);
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &cache->generic.string, &cache->generic.used, &cache->generic.size);
+  }
+#endif // _di_controller_cache_action_delete_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 342155f6a862f6db810ee07968a124384f5cb76e..e5419982627b96f11742bf8bbfc78d09ae89655e 100644 (file)
@@ -17,18 +17,122 @@ extern "C" {
 #endif
 
 /**
- * The Controller main program cache.
+ * Action related cache.
  *
- * buffer: A generic buffer.
+ * line_action: The line in some file representing an Action.
+ * line_item:   The line in some file representing an Item.
+ *
+ * name_action: A NULL terminated name of some Action.
+ * name_file:   A NULL terminated name of some File.
+ * name_item:   A NULL terminated name of some Item.
+ *
+ * generic: A NULL terminated string for general use.
+ */
+#ifndef _di_controller_cache_action_t_
+  typedef struct {
+    f_number_unsigned_t line_action;
+    f_number_unsigned_t line_item;
+
+    f_string_dynamic_t name_action;
+    f_string_dynamic_t name_file;
+    f_string_dynamic_t name_item;
+
+    f_string_dynamic_t generic;
+  } controller_cache_action_t;
+
+  #define controller_cache_action_t_initialize { \
+    0, \
+    0, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+    f_string_dynamic_t_initialize, \
+  }
+
+  #define macro_controller_cache_action_t_clear(cache) \
+    cache.line_action = 0; \
+    cache.line_item = 0; \
+    macro_f_string_dynamic_t_clear(cache.name_action) \
+    macro_f_string_dynamic_t_clear(cache.name_file) \
+    macro_f_string_dynamic_t_clear(cache.name_item) \
+    macro_f_string_dynamic_t_clear(cache.generic)
+#endif // _di_controller_cache_action_t_
+
+/**
+ * A cache intended for re-using memory while loading and processing rules whenever possible.
+ *
+ * timestamp: The timestamp.
+ *
+ * range_action: The Range for some Action.
+ *
+ * ats:   Locations.
+ * stack: Locations within a items history used as a history stack for circular recursion prevention.
+ *
+ * comments: Comments associated with a buffer string.
+ * delimits: Delimits associated with a buffer string.
+ *
+ * content_action:  The specific Content for some Action.
+ * content_actions: Content for some Action.
+ * content_items:   Content for some Item.
+ * object_actions:  Objects for some Action.
+ * object_items:    Objects for some Item.
+ *
+ * buffer:      A generic buffer.
+ * buffer_file: A generic file related buffer.
+ * buffer_item: A generic item related buffer.
+ * buffer_path: A generic path related buffer.
+ *
+ * expanded: An array of expanded strings, generally used by the execute functions.
+ *
+ * action: A cache for some Action, often used by error printing for reporting where an error happened.
  */
 #ifndef _di_controller_cache_t_
   typedef struct {
+    f_time_simple_t timestamp;
+
+    f_range_t range_action;
+
+    f_number_unsigneds_t ats;
+    f_number_unsigneds_t stack;
+
+    f_ranges_t comments;
+    f_number_unsigneds_t delimits;
+
+    f_ranges_t content_action;
+    f_rangess_t content_actions;
+    f_rangess_t content_items;
+    f_ranges_t object_actions;
+    f_ranges_t object_items;
+
     f_string_dynamic_t buffer;
+    f_string_dynamic_t buffer_file;
+    f_string_dynamic_t buffer_item;
+    f_string_dynamic_t buffer_path;
+
+    f_string_dynamics_t expanded;
+
+    controller_cache_action_t action;
   } controller_cache_t;
 
   #define controller_cache_t_initialize \
     { \
+      f_time_simple_t_initialize, \
+      f_range_t_initialize, \
+      f_number_unsigneds_t_initialize, \
+      f_number_unsigneds_t_initialize, \
+      f_ranges_t_initialize, \
+      f_number_unsigneds_t_initialize, \
+      f_ranges_t_initialize, \
+      f_rangess_t_initialize, \
+      f_rangess_t_initialize, \
+      f_ranges_t_initialize, \
+      f_ranges_t_initialize, \
+      f_string_dynamic_t_initialize, \
+      f_string_dynamic_t_initialize, \
+      f_string_dynamic_t_initialize, \
       f_string_dynamic_t_initialize, \
+      f_string_dynamics_t_initialize, \
+      controller_cache_action_t_initialize, \
     }
 #endif // _di_controller_cache_t_
 
@@ -37,7 +141,6 @@ extern "C" {
  *
  * @param cache
  *   The cache.
- *
  *   Must not be NULL.
  *
  * @see f_memory_array_resize()
@@ -46,6 +149,19 @@ extern "C" {
   extern void controller_cache_delete(controller_cache_t * const cache);
 #endif // _di_controller_cache_delete_
 
+/**
+ * Deallocate the Controller action cache.
+ *
+ * @param cache
+ *   The action cache.
+ *   Must not be NULL.
+ *
+ * @see f_memory_array_resize()
+ */
+#ifndef _di_controller_cache_action_delete_
+  extern void controller_cache_action_delete(controller_cache_action_t * const cache);
+#endif // _di_controller_cache_action_delete_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index 5d269a8c534a99f30f445137f41f564f17cc5776..ab28a4cfd8272ac2da04dd6268ed6d01fa6f5d26 100644 (file)
@@ -94,6 +94,7 @@
 #include <program/controller/main/print/error.h>
 #include <program/controller/main/print/lock.h>
 #include <program/controller/main/print/message.h>
+#include <program/controller/main/print/rule.h>
 #include <program/controller/main/print/verbose.h>
 #include <program/controller/main/print/warning.h>
 #include <program/controller/main/signal.h>
 #include <program/controller/main/thread.h>
 #include <program/controller/main/instance.h>
 #include <program/controller/main/rule.h>
+#include <program/controller/main/rule/action.h>
+#include <program/controller/main/rule/execute.h>
+#include <program/controller/main/rule/expand.h>
 #include <program/controller/main/rule/instance.h>
+#include <program/controller/main/rule/is.h>
+#include <program/controller/main/rule/item.h>
+#include <program/controller/main/rule/parameter.h>
+#include <program/controller/main/rule/read.h>
+#include <program/controller/main/rule/setting.h>
+#include <program/controller/main/rule/validate.h>
+#include <program/controller/main/rule/wait.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/sources/c/main/print/rule.c b/sources/c/main/print/rule.c
new file mode 100644 (file)
index 0000000..f2d125c
--- /dev/null
@@ -0,0 +1,141 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_main_print_rule_debug_item_action_empty_
+  f_status_t controller_main_print_rule_debug_item_action_empty(fl_print_t * const print, controller_cache_t * const cache) {
+
+    if (!print || !cache) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_debug_e) return F_output_not;
+
+    controller_lock_print(print->to, global->thread);
+
+    fl_print_format("%r%[%QAction is empty, nothing to do.%]%r", primt->to, f_string_eol_s, print->context, print->prefix, print->context, f_string_eol_s);
+
+    controller_rule_print_rule_message_cache(print, cache->action, F_true);
+
+    controller_unlock_print_flush(print->to, global->thread);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_rule_debug_item_action_empty_
+
+#ifndef _di_controller_main_print_rule_error_item_action_first_
+  f_status_t controller_main_print_rule_error_item_action_first(fl_print_t * const print, controller_cache_t * const cache) {
+
+    if (!print || !cache) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_error_e) return F_output_not;
+
+    controller_lock_print(print->to, global->thread);
+
+    fl_print_format("%r%[%QRule item action '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
+    fl_print_format(f_string_format_r_single_s.string, print->to, print->notable, controller_rerun_s, print->notable);
+    fl_print_format("%[' has '%]", print->to, print->context, print->context);
+    fl_print_format(f_string_format_Q_range_single_s.string, print->to, print->notable, cache->buffer_item, cache->content_action.array[0], print->notable);
+    fl_print_format("%[' as the first value, only the following are allowed: '%]", print->to, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_freeze_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_kill_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_pause_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_reload_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_restart_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_resume_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', '%]", print->to, print->notable, controller_start_s, print->notable, print->context, print->context);
+    fl_print_format("%[%r%]%[', or '%]", print->to, print->notable, controller_stop_s, print->notable, print->context, print->context);
+    fl_print_format(f_string_format_r_single_s.string, print->to, print->notable, controller_thaw_s, print->notable, print->context);
+    fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->context, print->context, f_string_eol_s);
+
+    controller_rule_print_rule_message_cache(print, cache->action, F_true);
+
+    controller_unlock_print_flush(print->to, global->thread);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_rule_error_item_action_first_
+
+#ifndef _di_controller_main_print_rule_error_item_action_second_
+  f_status_t controller_main_print_rule_error_item_action_second(fl_print_t * const print, controller_cache_t * const cache) {
+
+    if (!print || !cache) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_error_e) return F_output_not;
+
+    controller_lock_print(print->to, global->thread);
+
+    fl_print_format("%r%[%QRule item action '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
+    fl_print_format(f_string_format_r_single_s.string, print->to, print->notable, controller_rerun_s, print->notable);
+    fl_print_format("%[' has '%]", print->to, print->context, print->context);
+    fl_print_format(f_string_format_Q_range_single_s.string, print->to, print->notable, cache->buffer_item, cache->content_action.array[1], print->notable);
+    fl_print_format("%[' as the second value, only the following are allowed: '%]", print->to, print->context, print->context);
+    fl_print_format("%[%r%]%[' or '%]", print->to, print->notable, controller_stop_s, print->notable, print->context, print->context);
+    fl_print_format(f_string_format_r_single_s.string, print->to, print->notable, controller_thaw_s, print->notable, print->context);
+    fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->context, print->context, f_string_eol_s);
+
+    controller_rule_print_rule_message_cache(print, cache->action, F_true);
+
+    controller_unlock_print_flush(print->to, global->thread);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_rule_error_item_action_second_
+
+#ifndef _di_controller_main_print_rule_error_item_action_unknown_
+  f_status_t controller_main_print_rule_error_item_action_unknown(fl_print_t * const print, controller_cache_t * const cache, const f_string_static_t name, const f_number_unsigned_t index) {
+
+    if (!print || !cache) return F_status_set_error(F_output_not);
+    if (print->verbosity < f_console_verbosity_error_e) return F_output_not;
+
+    controller_lock_print(print->to, global->thread);
+
+    fl_print_format("%r%[%QRule item action '%]", print->to, f_string_eol_s, print->context, print->prefix, print->context);
+    fl_print_format(f_string_format_r_single_s.string, print->to, print->notable, name, print->notable);
+    fl_print_format("%[' has an unknown value '%]", print->to, print->context, print->context);
+    fl_print_format(f_string_format_Q_range_single_s.string, print->to, print->notable, cache->buffer_item, cache->content_action.array[index], print->notable);
+    fl_print_format(f_string_format_sentence_end_quote_s.string, print->to, print->context, print->context, f_string_eol_s);
+
+    controller_rule_print_rule_message_cache(print, cache->action, F_true);
+
+    controller_unlock_print_flush(print->to, global->thread);
+
+    return F_okay;
+  }
+#endif // _di_controller_main_print_rule_error_item_action_unknown_
+
+#ifndef _di_controller_rule_print_rule_message_cache_
+  f_status_t controller_rule_print_rule_message_cache(fl_print_t * const print, controller_cache_action_t * const cache, const bool item) {
+
+    if (!print || !cache) return F_status_set_error(F_output_not);
+
+    if (print->verbosity == f_console_verbosity_quiet_e) return;
+
+    fl_print_format("%r%[%QWhile processing ", print->to, f_string_eol_s, print->context, print->prefix);
+
+    if (cache->name_action.used) {
+      fl_print_format("%r '%]", print->to, item ? controller_action_s : controller_value_s, print->context);
+      fl_print_format(f_string_format_Q_single_s.string, print->to, print->notable, cache->name_action, print->notable);
+      fl_print_format("%[' on line%] ", print->to, print->context, print->context);
+      fl_print_format("%[%un%]", print->to, print->notable, cache->line_action, print->notable);
+      fl_print_format("%[ for ", print->to, print->context);
+    }
+
+    if (cache->name_item.used) {
+      fl_print_format("rule %r '%]", print->to, item ? controller_item_s : controller_settings_s, print->context);
+      fl_print_format(f_string_format_Q_single_s.string, print->to, print->notable, cache->name_item, print->notable);
+      fl_print_format("%[' on line%] ", print->to, print->context, print->context);
+      fl_print_format("%[%un%]", print->to, print->notable, cache->line_item, print->notable);
+      fl_print_format("%[ for ", print->to, print->context);
+    }
+
+    if (cache->name_file.used) {
+      fl_print_format("rule file '%]%[%Q%]%['", print->to, print->context, print->notable, cache->name_file, print->notable, print->context);
+    }
+
+    fl_print_format(".%]%r", print->to, print->context, f_string_eol_s);
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_print_rule_message_cache_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/print/rule.h b/sources/c/main/print/rule.h
new file mode 100644 (file)
index 0000000..71db437
--- /dev/null
@@ -0,0 +1,163 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the print rule functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_print_rule_h
+#define _controller_main_print_rule_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Print rule debug message about the rule item action being empty.
+ *
+ * @param print
+ *   The output structure to print to.
+ *   Must not be NULL.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fl_print_format()
+ * @see fll_error_print()
+ *
+ * @see controller_lock_print()
+ * @see controller_rule_print_rule_message_cache()
+ * @see controller_unlock_print_flush()
+ */
+#ifndef _di_controller_main_print_rule_debug_item_action_empty_
+  extern f_status_t controller_main_print_rule_debug_item_action_empty(fl_print_t * const print, controller_cache_t * const cache);
+#endif // _di_controller_main_print_rule_debug_item_action_empty_
+
+/**
+ * Print rule error message about the first rule item action parameter being invalid.
+ *
+ * @param print
+ *   The output structure to print to.
+ *   Must not be NULL.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fl_print_format()
+ * @see fll_error_print()
+ *
+ * @see controller_lock_print()
+ * @see controller_rule_print_rule_message_cache()
+ * @see controller_unlock_print_flush()
+ */
+#ifndef _di_controller_main_print_rule_error_item_action_first_
+  extern f_status_t controller_main_print_rule_error_item_action_first(fl_print_t * const print, controller_cache_t * const cache);
+#endif // _di_controller_main_print_rule_error_item_action_first_
+
+/**
+ * Print rule error message about the second rule item action parameter being invalid.
+ *
+ * @param print
+ *   The output structure to print to.
+ *   Must not be NULL.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fl_print_format()
+ * @see fll_error_print()
+ *
+ * @see controller_lock_print()
+ * @see controller_rule_print_rule_message_cache()
+ * @see controller_unlock_print_flush()
+ */
+#ifndef _di_controller_main_print_rule_error_item_action_second_
+  extern f_status_t controller_main_print_rule_error_item_action_second(fl_print_t * const print, controller_cache_t * const cache);
+#endif // _di_controller_main_print_rule_error_item_action_second_
+
+/**
+ * Print rule error message about the rule item action being unknown.
+ *
+ * @param print
+ *   The output structure to print to.
+ *   Must not be NULL.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   Must not be NULL.
+ * @param name
+ *   The parameter name whose value is unknown.
+ * @param index
+ *   The index in the content action cache representing the unknown value.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fl_print_format()
+ * @see fll_error_print()
+ *
+ * @see controller_lock_print()
+ * @see controller_rule_print_rule_message_cache()
+ * @see controller_unlock_print_flush()
+ */
+#ifndef _di_controller_main_print_rule_error_item_action_unknown_
+  extern f_status_t controller_main_print_rule_error_item_action_unknown(fl_print_t * const print, controller_cache_t * const cache, const f_string_static_t name, const f_number_unsigned_t index);
+#endif // _di_controller_main_print_rule_error_item_action_unknown_
+
+/**
+ * Print rule error message about the rule item action being unknown.
+ *
+ * @param print
+ *   The output structure to print to.
+ *   Must not be NULL.
+ *
+ *   This does not lock the stream.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ *   Must not be NULL.
+ * @param name
+ *   The parameter name related to the message.
+ * @param index
+ *   The index in the content action cache representing the parameter value related to the message.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_output_not on success, but no printing is performed.
+ *
+ *   F_output_not (with error bit) if setting is NULL.
+ *
+ * @see fl_print_format()
+ */
+#ifndef _di_controller_rule_print_rule_message_cache_
+  extern f_status_t controller_rule_print_rule_message_cache(fl_print_t * const print, controller_cache_action_t * const cache, const bool item);
+#endif // _di_controller_rule_print_rule_message_cache_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_print_rule_h
index b4400f972b6e865216a05a532e96ff84ca197eb9..a0a7abef68b7fddc7f63ad6b2797a0a415836f83 100644 (file)
@@ -4,6 +4,271 @@
 extern "C" {
 #endif
 
+#ifndef _di_controller_rule_copy_
+  f_status_t controller_rule_copy(const controller_rule_t source, controller_rule_t *destination) {
+
+    // Delete the third party structures.
+    f_memory_array_resize(0, sizeof(f_char_t), (void **) &destination->cgroup.path.string, &destination->cgroup.path.used, &destination->cgroup.path.size);
+    f_memory_arrays_resize(0, sizeof(f_string_dynamic_t), (void **) &destination->cgroup.groups.array, &destination->cgroup.groups.used, &destination->cgroup.groups.size, &f_string_dynamics_delete_callback);
+
+    f_capability_delete(&destination->capability);
+
+    f_number_unsigned_t i = 0;
+
+    for (; i < controller_rule_action_type__enum_size_e; ++i) {
+      destination->status[i] = source.status[i];
+    } // for
+
+    destination->timeout_kill = source.timeout_kill;
+    destination->timeout_start = source.timeout_start;
+    destination->timeout_stop = source.timeout_stop;
+
+    destination->has = source.has;
+    destination->nice = source.nice;
+    destination->user = source.user;
+    destination->group = source.group;
+
+    destination->timestamp.seconds = source.timestamp.seconds;
+    destination->timestamp.seconds_nano = source.timestamp.seconds_nano;
+
+    destination->alias.used = 0;
+    destination->engine.used = 0;
+    destination->engine_arguments.used = 0;
+    destination->name.used = 0;
+    destination->path.used = 0;
+
+    destination->define.used = 0;
+    destination->parameter.used = 0;
+    destination->environment.used = 0;
+
+    destination->affinity.used = 0;
+    destination->groups.used = 0;
+    destination->limits.used = 0;
+    destination->scheduler.policy = source.scheduler.policy;
+    destination->scheduler.priority = source.scheduler.priority;
+
+    for (i = 0; i < destination->ons.size; ++i) {
+
+      destination->ons.array[i].action = 0;
+      destination->ons.array[i].need.used = 0;
+      destination->ons.array[i].want.used = 0;
+      destination->ons.array[i].wish.used = 0;
+    } // for
+
+    for (i = 0; i < destination->engine_arguments.size; ++i) {
+      destination->engine_arguments.array[i].used = 0;
+    } // for
+
+    destination->ons.used = 0;
+    destination->items.used = 0;
+
+    f_status_t status = F_okay;
+
+    status = f_string_dynamic_append(source.alias, &destination->alias);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_dynamic_append(source.engine, &destination->engine);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_dynamic_append(source.name, &destination->name);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_dynamic_append(source.path, &destination->path);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_maps_append_all(source.define, &destination->define);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_maps_append_all(source.parameter, &destination->parameter);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_dynamics_append_all(source.engine_arguments, &destination->engine_arguments);
+    if (F_status_is_error(status)) return status;
+
+    status = f_string_dynamics_append_all(source.environment, &destination->environment);
+    if (F_status_is_error(status)) return status;
+
+    if (source.ons.used) {
+      if (destination->ons.used < source.ons.used) {
+        status = controller_rule_ons_resize(source.ons.used, &destination->ons);
+        if (F_status_is_error(status)) return status;
+      }
+
+      for (i = 0; i < source.ons.used; ++i) {
+
+        destination->ons.array[i].action = source.ons.array[i].action;
+
+        if (source.ons.array[i].need.used) {
+          destination->ons.array[i].need.used = 0;
+
+          status = f_string_dynamics_append_all(source.ons.array[i].need, &destination->ons.array[i].need);
+          if (F_status_is_error(status)) return status;
+        }
+
+        if (source.ons.array[i].want.used) {
+          destination->ons.array[i].want.used = 0;
+
+          status = f_string_dynamics_append_all(source.ons.array[i].want, &destination->ons.array[i].want);
+          if (F_status_is_error(status)) return status;
+        }
+
+        if (source.ons.array[i].wish.used) {
+          destination->ons.array[i].wish.used = 0;
+
+          status = f_string_dynamics_append_all(source.ons.array[i].wish, &destination->ons.array[i].wish);
+          if (F_status_is_error(status)) return status;
+        }
+      } // for
+
+      destination->ons.used = source.ons.used;
+    }
+
+    status = f_memory_array_append_all(source.affinity.array, source.affinity.used, sizeof(int32_t), (void **) &destination->affinity.array, &destination->affinity.used, &destination->affinity.size);
+    if (F_status_is_error(status)) return status;
+
+    if (source.capability) {
+      status = f_capability_copy(source.capability, &destination->capability);
+      if (F_status_is_error(status)) return status;
+    }
+
+    status = f_control_group_copy(source.cgroup, &destination->cgroup);
+    if (F_status_is_error(status)) return status;
+
+    status = f_memory_array_append_all(source.groups.array, source.groups.used, sizeof(int32_t), (void **) &destination->groups.array, &destination->groups.used, &destination->groups.size);
+    if (F_status_is_error(status)) return status;
+
+    destination->limits.used = 0;
+    status = f_memory_array_append_all(source.limits.array, source.limits.used, sizeof(f_limit_set_t), (void **) &destination->limits.array, &destination->limits.used, &destination->limits.size);
+    if (F_status_is_error(status)) return status;
+
+    if (source.items.used) {
+      controller_rule_item_t *item_source = 0;
+      controller_rule_item_t *item_destination = 0;
+
+      controller_rule_action_t *action_source = 0;
+      controller_rule_action_t *action_destination = 0;
+
+      if (source.items.used > destination->items.size) {
+        status = controller_rule_items_increase_by(source.items.used - destination->items.size, &destination->items);
+        if (F_status_is_error(status)) return status;
+      }
+
+      f_number_unsigned_t j = 0;
+
+      for (i = 0; i < source.items.used; ++i) {
+
+        item_source = &source.items.array[i];
+        item_destination = &destination->items.array[i];
+
+        if (item_source->actions.used > item_destination->actions.size) {
+          status = controller_rule_actions_increase_by(item_source->actions.used - item_destination->actions.size, &item_destination->actions);
+          if (F_status_is_error(status)) return status;
+        }
+
+        item_destination->type = item_source->type;
+        item_destination->with = item_source->with;
+        item_destination->line = item_source->line;
+        item_destination->pid_file.used = 0;
+
+        status = f_string_dynamic_append(item_source->pid_file, &item_destination->pid_file);
+        if (F_status_is_error(status)) return status;
+
+        for (j = 0; j < controller_rule_action_execute_type__enum_size_e; ++j) {
+
+          item_destination->reruns[j].is = item_source->reruns[j].is;
+          item_destination->reruns[j].failure.count = item_source->reruns[j].failure.count;
+          item_destination->reruns[j].failure.delay = item_source->reruns[j].failure.delay;
+          item_destination->reruns[j].failure.max = item_source->reruns[j].failure.max;
+          item_destination->reruns[j].success.count = item_source->reruns[j].success.count;
+          item_destination->reruns[j].success.delay = item_source->reruns[j].success.delay;
+          item_destination->reruns[j].success.max = item_source->reruns[j].success.max;
+        } // for
+
+        for (j = 0; j < item_source->actions.used; ++j) {
+
+          action_source = &item_source->actions.array[j];
+          action_destination = &item_destination->actions.array[j];
+
+          action_destination->type = action_source->type;
+          action_destination->line = action_source->line;
+          action_destination->status = action_source->status;
+
+          action_destination->parameters.used = 0;
+          action_destination->ikis.used = 0;
+
+          status = f_string_dynamics_append_all(action_source->parameters, &action_destination->parameters);
+          if (F_status_is_error(status)) return status;
+
+          status = f_iki_datas_append_all(action_source->ikis, &action_destination->ikis);
+          if (F_status_is_error(status)) return status;
+        } // for
+
+        item_destination->actions.used = item_source->actions.used;
+      } // for
+
+      destination->items.used = source.items.used;
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_copy_
+
+#ifndef _di_controller_rule_find_
+  f_status_t controller_rule_find(const f_string_static_t alias, const controller_rules_t rules, f_number_unsigned_t *at) {
+
+    if (!alias.used) return F_okay;
+    if (!rules.used) return F_false;
+
+    for (f_number_unsigned_t i = 0; i < rules.used; ++i) {
+
+      if (f_compare_dynamic(alias, rules.array[i].alias) == F_equal_to) {
+        if (at) *at = i;
+
+        return F_true;
+      }
+    } // for
+
+    return F_false;
+  }
+#endif // _di_controller_rule_find_
+
+#ifndef _di_controller_rule_id_construct_
+  f_status_t controller_rule_id_construct(controller_global_t * const global, const f_string_static_t source, const f_range_t directory, const f_range_t basename, f_string_dynamic_t * const alias) {
+
+    if (!global || !global->main || !global->thread) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    alias->used = 0;
+
+    status = f_string_dynamic_partial_append_nulless(source, directory, alias);
+
+    if (F_status_is_error(status)) {
+      controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_partial_append_nulless));
+
+      return status;
+    }
+
+    status = f_string_dynamic_append(f_path_separator_s, alias);
+
+    if (F_status_is_error(status)) {
+      controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_append));
+
+      return status;
+    }
+
+    status = f_string_dynamic_partial_append_nulless(source, basename, alias);
+
+    if (F_status_is_error(status)) {
+      controller_main_print_error(&main->program.error, macro_controller_f(f_string_dynamic_partial_append_nulless));
+
+      return status;
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_id_construct_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
index db45e4249920fed5e0051e93b972aced9dbe890c..8f95ac3448d7c6cb94f5289bf6f2bd8edd4cda10 100644 (file)
 extern "C" {
 #endif
 
+/**
+ * Copy a rule, allocating new space as necessary.
+ *
+ * This does not do any locking or unlocking for the rule data, be sure to lock appropriately before and after calling this.
+ *
+ * @param source
+ *   The source rule to copy from.
+ * @param destination
+ *   The destination rule to copy to.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Errors (with error bit) from: f_capability_copy().
+ *   Errors (with error bit) from: f_control_group_copy().
+ *   Errors (with error bit) from: f_iki_datas_append_all().
+ *   Errors (with error bit) from: f_int32s_append_all().
+ *   Errors (with error bit) from: f_limit_sets_copy().
+ *   Errors (with error bit) from: f_string_dynamic_append().
+ *   Errors (with error bit) from: f_string_dynamics_append_all().
+ *   Errors (with error bit) from: f_string_maps_append_all().
+ *
+ *  @see f_capability_copy()
+ *  @see f_control_group_copy()
+ *  @see f_iki_datas_append_all()
+ *  @see f_int32s_append_all()
+ *  @see f_limit_sets_append()
+ *  @see f_string_dynamic_append()
+ *  @see f_string_dynamics_append_all()
+ *  @see f_string_maps_append_all()
+ */
+#ifndef _di_controller_rule_copy_
+  extern f_status_t controller_rule_copy(const controller_rule_t source, controller_rule_t *destination);
+#endif // _di_controller_rule_copy_
+
+/**
+ * Find the location of the Rule by the Rule alias.
+ *
+ * @param alias
+ *   The Rule alias to find.
+ * @param rules
+ *   The rules to search through.
+ * @param at
+ *   The index the rule was found at.
+ *   (optional) Set to NULL to disable.
+ *
+ * @return
+ *   F_okay on success, but the id.used is 0.
+ *   F_true on success and rule was found, index is updated.
+ *   F_false on success and rule was not found.
+ */
+#ifndef _di_controller_rule_find_
+  extern f_status_t controller_rule_find(const f_string_static_t alias, const controller_rules_t rules, f_number_unsigned_t *at);
+#endif // _di_controller_rule_find_
+
+/**
+ * Construct an id from two distinct strings found within a single given source.
+ *
+ * @param global
+ *   The global 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 alias
+ *   The constructed alias.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ *
+ * @see f_string_append()
+ * @see f_string_dynamic_partial_append_nulless()
+ */
+#ifndef _di_controller_rule_id_construct_
+  extern f_status_t controller_rule_id_construct(controller_global_t * const global, const f_string_static_t source, const f_range_t directory, const f_range_t basename, f_string_dynamic_t * const alias);
+#endif // _di_controller_rule_id_construct_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
diff --git a/sources/c/main/rule/action.c b/sources/c/main/rule/action.c
new file mode 100644 (file)
index 0000000..52b0d19
--- /dev/null
@@ -0,0 +1,490 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_action_method_name_
+  f_string_static_t controller_rule_action_method_name(const uint8_t type) {
+
+    if (type == controller_rule_action_method_extended_e) return controller_rule_action_method_string_extended_s;
+    if (type == controller_rule_action_method_extended_list_e) return controller_rule_action_method_string_extended_list_s;
+
+    return f_string_empty_s;
+  }
+#endif // _di_controller_rule_action_method_name_
+
+#ifndef _di_controller_rule_action_type_to_action_execute_type_
+  uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type) {
+
+    if (type == controller_rule_action_type_freeze_e) return controller_rule_action_execute_type_freeze_e;
+    if (type == controller_rule_action_type_kill_e) return controller_rule_action_execute_type_kill_e;
+    if (type == controller_rule_action_type_pause_e) return controller_rule_action_execute_type_pause_e;
+    if (type == controller_rule_action_type_reload_e) return controller_rule_action_execute_type_reload_e;
+    if (type == controller_rule_action_type_restart_e) return controller_rule_action_execute_type_restart_e;
+    if (type == controller_rule_action_type_resume_e) return controller_rule_action_execute_type_resume_e;
+    if (type == controller_rule_action_type_start_e) return controller_rule_action_execute_type_start_e;
+    if (type == controller_rule_action_type_stop_e) return controller_rule_action_execute_type_stop_e;
+    if (type == controller_rule_action_type_thaw_e) return controller_rule_action_execute_type_thaw_e;
+
+    return controller_rule_action_execute_type__enum_size_e;
+  }
+#endif // _di_controller_rule_action_type_to_action_execute_type_
+
+#ifndef _di_controller_rule_action_read_
+  f_status_t controller_rule_action_read(controller_global_t * const global, const bool is_normal, const uint8_t type, const uint8_t method, controller_cache_t * const cache, controller_rule_item_t * const item, controller_rule_actions_t * const actions, f_range_t * const range) {
+
+    if (!global || !item || !actions || !range) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize_1(is_normal, global->thread);
+    f_state_t state = macro_f_state_t_initialize_1(controller_common_allocation_large_d, controller_common_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+    f_number_unsigned_t i = 0;
+
+    for (; i < cache->comments.size; ++i) {
+
+      cache->comments.array[i].start = 1;
+      cache->comments.array[i].stop = 0;
+    } // for
+
+    for (i = 0; i < cache->delimits.size; ++i) {
+      cache->delimits.array[i] = 0;
+    } // for
+
+    for (i = 0; i < cache->content_action.size; ++i) {
+
+      cache->content_action.array[i].start = 1;
+      cache->content_action.array[i].stop = 0;
+    } // for
+
+    cache->comments.used = 0;
+    cache->delimits.used = 0;
+    cache->content_action.used = 0;
+
+    if (method == controller_rule_action_method_extended_list_e) {
+      fl_fss_extended_list_content_read(cache->buffer_item, range, &cache->content_action, &cache->delimits, &cache->comments, &state);
+
+      if (F_status_is_error(status)) {
+        controller_main_print_error(&global->main->program.error, macro_controller_f(fl_fss_extended_list_content_read));
+
+        return status;
+      }
+
+      if (status == F_fss_found_content) {
+        f_fss_apply_delimit(cache->delimits, &cache->buffer_item, &state);
+
+        if (F_status_is_error(status)) {
+          controller_main_print_error(&global->main->program.error, macro_controller_f(f_fss_apply_delimit));
+
+          return status;
+        }
+
+        // The "script" and "utility" types use the entire content and can be directly passed through at index 0.
+        if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+          actions->array[actions->used].parameters.used = 0;
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &actions->array[actions->used].parameters.array, &actions->array[actions->used].parameters.used, &actions->array[actions->used].parameters.size);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_memory_array_increase));
+
+            return status;
+          }
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_iki_data_t), (void **) &actions->array[actions->used].ikis.array, &actions->array[actions->used].ikis.used, &actions->array[actions->used].ikis.size);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_memory_array_increase));
+
+            return status;
+          }
+
+          actions->array[actions->used].type = type;
+          actions->array[actions->used].line = cache->action.line_action;
+          actions->array[actions->used].status = F_known_not;
+
+          status = f_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)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_string_dynamic_append_nulless));
+
+            actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+            return status;
+          }
+
+          if (actions->array[actions->used].parameters.array[0].used) {
+            state.step_large = controller_common_allocation_iki_large_d;
+            state.step_small = controller_common_allocation_iki_small_d;
+            state.interrupt = &controller_thread_signal_state_iki;
+
+            f_range_t range_iki = macro_f_range_t_initialize_2(actions->array[actions->used].parameters.array[0].used);
+
+            fl_iki_read(&actions->array[actions->used].parameters.array[0], &range_iki, &actions->array[actions->used].ikis.array[0], &state);
+
+            if (F_status_is_error(status)) {
+              controller_main_print_error(&global->main->program.error, macro_controller_f(fl_iki_read));
+
+              actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+              return status;
+            }
+          }
+
+          actions->array[actions->used].ikis.used = 1;
+          actions->array[actions->used++].parameters.used = 1;
+
+          return status;
+        }
+
+        cache->delimits.used = 0;
+
+        // The object_actions and content_actions caches are being used for the purposes of getting the parameters a given the action.
+        fll_fss_extended_read(cache->buffer_item, &cache->content_action.array[0], &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0, &state);
+
+        if (F_status_is_error(status)) {
+          controller_main_print_error(&global->main->program.error, macro_controller_f(fll_fss_extended_read));
+
+          return status;
+        }
+
+        f_fss_apply_delimit(cache->delimits, &cache->buffer_item, &state);
+
+        if (F_status_is_error(status)) {
+          controller_main_print_error(&global->main->program.error, macro_controller_f(f_fss_apply_delimit));
+
+          return status;
+        }
+
+        for (i = 0; i < cache->object_actions.used; ++i) {
+
+          status = controller_rule_actions_increase_by(controller_common_allocation_small_d, actions);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(controller_rule_actions_increase_by));
+
+            return status;
+          }
+
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &actions->array[actions->used].line, &state);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_fss_count_lines));
+
+            return status;
+          }
+
+          actions->array[actions->used].type = type;
+          actions->array[actions->used].line += ++item->line;
+          actions->array[actions->used].status = F_known_not;
+
+          status = controller_rule_parameters_read(global, cache->buffer_item, &cache->object_actions.array[i], &cache->content_actions.array[i], &actions->array[actions->used], &state);
+
+          if (F_status_is_error(status)) {
+            actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+            return status;
+          }
+
+          actions->array[actions->used++].status = status;
+        } // for
+
+        range->start = cache->content_action.array[0].start;
+      }
+      else {
+        status = F_data_not;
+      }
+    }
+    else {
+      fl_fss_extended_content_read(cache->buffer_item, range, &cache->content_action, 0, &cache->delimits, &state);
+
+      if (F_status_is_error(status)) {
+        controller_main_print_error(&global->main->program.error, macro_controller_f(fll_fss_extended_content_read));
+      }
+      else if (status == F_fss_found_content) {
+        f_fss_apply_delimit(cache->delimits, &cache->buffer_item, &state);
+
+        if (F_status_is_error(status)) {
+          controller_main_print_error(&global->main->program.error, macro_controller_f(f_fss_apply_delimit));
+        }
+        else if (type == controller_rule_action_type_pid_file_e) {
+          item->pid_file.used = 0;
+
+          status = f_rip_dynamic_partial(cache->buffer_item, cache->content_action.array[0], &item->pid_file);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_rip_dynamic_partial));
+          }
+        }
+        else if (type == controller_rule_action_type_rerun_e) {
+          uint8_t type_rerun = 0;
+
+          if (cache->content_action.used) {
+            if (f_compare_dynamic_partial_string(controller_freeze_s.string, cache->buffer_item, controller_freeze_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_freeze_e;
+            }
+            if (f_compare_dynamic_partial_string(controller_kill_s.string, cache->buffer_item, controller_kill_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_kill_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_pause_s.string, cache->buffer_item, controller_pause_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_pause_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_reload_s.string, cache->buffer_item, controller_reload_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_reload_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_restart_s.string, cache->buffer_item, controller_restart_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_restart_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_resume_s.string, cache->buffer_item, controller_resume_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_resume_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_start_s.string, cache->buffer_item, controller_start_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_start_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_stop_s.string, cache->buffer_item, controller_stop_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_stop_e;
+            }
+            else if (f_compare_dynamic_partial_string(controller_thaw_s.string, cache->buffer_item, controller_thaw_s.used, cache->content_action.array[0]) == F_equal_to) {
+              type_rerun = controller_rule_action_execute_type_thaw_e;
+            }
+          }
+
+          if (!type_rerun) {
+            controller_main_print_rule_error_item_action_first(&global->main->program.error, cache);
+
+            return F_status_set_error(F_valid_not);
+          }
+
+          controller_rule_rerun_item_t *rerun_item = 0;
+
+          if (cache->content_action.used > 1) {
+            if (f_compare_dynamic_partial_string(controller_failure_s.string, cache->buffer_item, controller_failure_s.used, cache->content_action.array[1]) == F_equal_to) {
+              rerun_item = &item->reruns[type_rerun].failure;
+              item->reruns[type_rerun].is |= controller_rule_rerun_is_failure_d;
+            }
+            else if (f_compare_dynamic_partial_string(controller_success_s.string, cache->buffer_item, controller_success_s.used, cache->content_action.array[1]) == F_equal_to) {
+              rerun_item = &item->reruns[type_rerun].success;
+              item->reruns[type_rerun].is |= controller_rule_rerun_is_success_d;
+            }
+          }
+          else {
+            controller_main_print_rule_error_item_action_second(&global->main->program.error, cache);
+
+            return F_status_set_error(F_valid_not);
+          }
+
+          for (i = 2; i < cache->content_action.used; ++i) {
+
+            if (f_compare_dynamic_partial_string(controller_delay_s.string, cache->buffer_item, controller_delay_s.used, cache->content_action.array[i]) == F_equal_to) {
+              status = controller_rule_action_read_rerun_number(global, controller_delay_s.string, cache, &i, &rerun_item->delay);
+            }
+            else if (f_compare_dynamic_partial_string(controller_max_s.string, cache->buffer_item, controller_max_s.used, cache->content_action.array[i]) == F_equal_to) {
+              status = controller_rule_action_read_rerun_number(global, controller_max_s.string, cache, &i, &rerun_item->max);
+            }
+            else if (f_compare_dynamic_partial_string(controller_reset_s.string, cache->buffer_item, controller_reset_s.used, cache->content_action.array[i]) == F_equal_to) {
+              item->reruns[type_rerun].is |= rerun_item == &item->reruns[type_rerun].failure ? controller_rule_rerun_is_failure_reset_d : controller_rule_rerun_is_success_reset_d;
+            }
+            else {
+              controller_main_print_rule_error_item_action_unknown(&global->main->program.error, cache, controller_rerun_s, i);
+
+              return F_status_set_error(F_valid_not);
+            }
+          } // for
+        }
+        else if (type == controller_rule_action_type_with_e) {
+          for (i = 0; i < cache->content_action.used; ++i) {
+
+            if (f_compare_dynamic_partial_string(controller_full_path_s.string, cache->buffer_item, controller_full_path_s.used, cache->content_action.array[i]) == F_equal_to) {
+              item->with |= controller_with_full_path_d;
+            }
+            else if (f_compare_dynamic_partial_string(controller_session_new_s.string, cache->buffer_item, controller_session_new_s.used, cache->content_action.array[i]) == F_equal_to) {
+              item->with |= controller_with_session_new_d;
+
+              // The "session_new" and "session_same" are mutually exclusive.
+              item->with &= ~controller_with_session_same_d;
+            }
+            else if (f_compare_dynamic_partial_string(controller_session_same_s.string, cache->buffer_item, controller_session_same_s.used, cache->content_action.array[i]) == F_equal_to) {
+              item->with |= controller_with_session_same_d;
+
+              // The "session_new" and "session_same" are mutually exclusive.
+              item->with &= ~controller_with_session_new_d;
+            }
+            else {
+              controller_main_print_rule_error_item_action_unknown(&global->main->program.error, cache, controller_with_s, i);
+
+              status = F_status_set_error(F_valid_not);
+
+              break;
+            }
+          } // for
+        }
+        else if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &actions->array[actions->used].parameters.array, &actions->array[actions->used].parameters.used, &actions->array[actions->used].parameters.size);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_memory_array_increase));
+
+            return status;
+          }
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_iki_data_t), (void **) &actions->array[actions->used].ikis.array, &actions->array[actions->used].ikis.used, &actions->array[actions->used].ikis.size);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_memory_array_increase));
+
+            return status;
+          }
+
+          // The "script" types use the entire content as a single string piped to the script, so merge all arguments together.
+          actions->array[actions->used].type = type;
+          actions->array[actions->used].line = cache->action.line_action;
+          actions->array[actions->used].status = F_known_not;
+
+          for (i = 0; i < cache->content_action.used; ++i) {
+
+            status = f_string_dynamic_partial_mash_nulless(f_string_space_s, cache->buffer_item, cache->content_action.array[i], &actions->array[actions->used].parameters.array[0]);
+            if (F_status_is_error(status)) break;
+          } // for
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_string_dynamic_partial_mash_nulless));
+
+            actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+            return status;
+          }
+
+          if (actions->array[actions->used].parameters.array[0].used) {
+            state.step_large = controller_common_allocation_iki_large_d;
+            state.step_small = controller_common_allocation_iki_small_d;
+            state.interrupt = &controller_thread_signal_state_iki;
+
+            f_range_t range_iki = macro_f_range_t_initialize_2(actions->array[actions->used].parameters.array[0].used);
+
+            fl_iki_read(&actions->array[actions->used].parameters.array[0], &range_iki, &actions->array[actions->used].ikis.array[0], &state);
+
+            if (F_status_is_error(status)) {
+              controller_main_print_error(&global->main->program.error, macro_controller_f(fl_iki_read));
+
+              actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+              return status;
+            }
+          }
+
+          actions->array[actions->used].ikis.used = 1;
+          actions->array[actions->used++].parameters.used = 1;
+        }
+        else {
+          f_fss_count_lines(cache->buffer_item, range->start, &actions->array[actions->used].line, &state);
+
+          if (F_status_is_error(status)) {
+            controller_main_print_error(&global->main->program.error, macro_controller_f(f_fss_count_lines));
+
+            return status;
+          }
+
+          actions->array[actions->used].type = type;
+          actions->array[actions->used].line += ++item->line;
+          actions->array[actions->used].status = F_known_not;
+
+          status = controller_rule_parameters_read(global, cache->buffer_item, 0, &cache->content_action, &actions->array[actions->used], &state);
+
+          if (F_status_is_error(status)) {
+            actions->array[actions->used++].status = controller_status_simplify_error(F_status_set_fine(status));
+
+            return status;
+          }
+
+          actions->array[actions->used++].status = status;
+        }
+      }
+      else {
+        status = F_data_not;
+      }
+    }
+
+    if (F_status_is_error_not(status) && status == F_data_not) {
+      if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+        controller_main_print_rule_debug_item_action_empty(&global->main->program.debug);
+      }
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_action_read_
+
+#ifndef _di_controller_rule_action_read_rerun_number_
+  f_status_t controller_rule_action_read_rerun_number(controller_global_t * const global, const f_string_t name, controller_cache_t * const cache, f_number_unsigned_t * const index, f_number_unsigned_t * const number) {
+
+    f_status_t status = F_okay;
+    f_number_signed_t parsed = 0;
+
+    if (*index + 1 == cache->content_action.used) {
+      status = F_status_set_error(F_valid_not);
+    }
+    else {
+      status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_action.array[++(*index)], &parsed);
+
+      if (F_status_set_fine(status) == F_number_positive) {
+        status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_action.array[*index]), &parsed);
+      }
+
+      if (status == F_data_not) {
+        status = F_status_set_error(F_valid_not);
+      }
+    }
+
+    if (F_status_is_error(status)) {
+      if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+        status = F_status_set_fine(status);
+
+        if (status != F_valid_not && status != F_number && status != F_number_decimal && status != F_number_overflow && status != F_number_underflow && status != F_number_negative) {
+          controller_main_print_error(&global->main->program.error, macro_controller_f(fl_conversion_dynamic_partial_to_signed_detect));
+        }
+        else {
+          controller_lock_print(global->main->program.error.to, global->thread);
+
+          fl_print_format("%r%[%QRule item action '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+          fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_rerun_s, global->main->program.error.notable);
+          fl_print_format("%[' requires a positive whole number or 0 for the '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+          fl_print_format("%[%S%]", global->main->program.error.to, global->main->program.error.notable, name, global->main->program.error.notable);
+          fl_print_format("%[' value", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+
+          if (*index + 1 == cache->content_action.used) {
+            fl_print_format(", but none were given.%]%r", global->main->program.error.to, global->main->program.error.context, f_string_eol_s);
+          }
+          else {
+            fl_print_format(", but '%]%[%/Q%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.notable, cache->buffer_item, cache->content_action.array[*index], global->main->program.error.notable);
+
+            if (status == F_number || status == F_number_decimal) {
+              fl_print_format("%[' was given.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+            }
+            else if (status == F_number_overflow) {
+              fl_print_format("%[' is too large.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+            }
+            else {
+              fl_print_format("%[' is negative.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+            }
+          }
+
+          controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_true);
+
+          controller_unlock_print_flush(global->main->program.error.to, global->thread);
+        }
+      }
+
+      return status;
+    }
+
+    *number = (f_number_unsigned_t) parsed;
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_action_read_rerun_number_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/action.h b/sources/c/main/rule/action.h
new file mode 100644 (file)
index 0000000..f33ed6e
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "action" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_action_h
+#define _controller_main_rule_action_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Get a string representing the rule action method.
+ *
+ * @param type
+ *   The rule action type code.
+ *
+ * @return
+ *   The string with used > 0 on success.
+ *   The string with used == 0 if no match was found.
+ */
+#ifndef _di_controller_rule_action_method_name_
+  extern f_string_static_t controller_rule_action_method_name(const uint8_t type);
+#endif // _di_controller_rule_action_method_name_
+
+/**
+ * Convert the action type to an action execute type.
+ *
+ * @param type
+ *   The action type to convert from.
+ *
+ * @return
+ *   The converted action type, converted into an action execute type.
+ *
+ *   The code controller_rule_action_execute_type__enum_size_e is returned for unknown types.
+ */
+#ifndef _di_controller_rule_action_type_to_action_execute_type_
+  extern uint8_t controller_rule_action_type_to_action_execute_type(const uint8_t type);
+#endif // _di_controller_rule_action_type_to_action_execute_type_
+
+/**
+ * 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 global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param is_normal
+ *   If TRUE, then process as if this operates during a normal operation (entry and control).
+ *   If FALSE, then process as if this operates during a an exit operation.
+ * @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.
+ *   Must not be NULL.
+ * @param item
+ *   The processed item.
+ *   Must not be NULL.
+ * @param actions
+ *   The processed actions.
+ *   Must not be NULL.
+ * @param range
+ *   The current positions within the buffer being operated on.
+ *   This is expected to be set to a position immediately after a valid object read.
+ *   Must not be NULL.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   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_actions_increase_by()
+ * @see controller_rule_parameters_read()
+ * @see f_fss_count_lines()
+ */
+#ifndef _di_controller_rule_action_read_
+  extern f_status_t controller_rule_action_read(controller_global_t * const global, const bool is_normal, const uint8_t type, const uint8_t method, controller_cache_t * const cache, controller_rule_item_t * const item, controller_rule_actions_t * const actions, f_range_t * const range);
+#endif // _di_controller_rule_action_read_
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_action_h
diff --git a/sources/c/main/rule/execute.c b/sources/c/main/rule/execute.c
new file mode 100644 (file)
index 0000000..0de8ba2
--- /dev/null
@@ -0,0 +1,1002 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_execute_
+  f_status_t controller_rule_execute(controller_global_t * const global, const uint8_t action, const uint8_t options, controller_instance_t * const instance) {
+
+    if (!global || !instance) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+    f_status_t success = F_false;
+
+    f_number_unsigned_t i = 0;
+    f_number_unsigned_t j = 0;
+    f_number_unsigned_t k = 0;
+
+    // Child processes should receive all signals and handle the signals as they see fit.
+    f_signal_how_t signals = f_signal_how_t_initialize;
+    f_signal_set_empty(&signals.block);
+    f_signal_set_fill(&signals.block_not);
+
+    f_string_maps_t environment = f_string_maps_t_initialize;
+
+    controller_execute_set_t execute_set = macro_controller_execute_set_t_initialize_1(0, 0, &environment, &signals, 0, fl_execute_as_t_initialize);
+
+    if (process->rule.affinity.used) {
+      execute_set.as.affinity = &process->rule.affinity;
+    }
+
+    if (process->rule.capability) {
+      execute_set.as.capability = process->rule.capability;
+    }
+
+    if (process->rule.has & controller_rule_has_cgroup_d) {
+      execute_set.as.control_group = &process->rule.cgroup;
+
+      // Make sure all required cgroup directories exist.
+      if (controller_rule_status_is_available(action, process->rule)) {
+        status = fll_control_group_prepare(process->rule.cgroup);
+
+        if (F_status_is_error(status)) {
+          controller_print_error_file(global->thread, &global->main->program.error, F_status_set_fine(status), "fll_control_group_prepare", F_true, process->rule.cgroup.path, controller_rule_print_control_groups_prepare_s, fll_error_file_type_directory_e);
+
+          return status;
+        }
+      }
+    }
+
+    if (process->rule.has & controller_rule_has_group_d) {
+      execute_set.as.id_group = &process->rule.group;
+
+      if (process->rule.groups.used) {
+        execute_set.as.id_groups = &process->rule.groups;
+      }
+    }
+
+    if (process->rule.limits.used) {
+      execute_set.as.limits = &process->rule.limits;
+    }
+
+    if (process->rule.has & controller_rule_has_scheduler_d) {
+      execute_set.as.scheduler = &process->rule.scheduler;
+    }
+
+    if (process->rule.has & controller_rule_has_nice_d) {
+      execute_set.as.nice = &process->rule.nice;
+    }
+
+    if (process->rule.has & controller_rule_has_user_d) {
+      execute_set.as.id_user = &process->rule.user;
+    }
+
+    if (process->rule.has & controller_rule_has_environment_d) {
+      status = fl_environment_load_names(process->rule.environment, &environment);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "fl_environment_load_names", F_true);
+
+        return status;
+      }
+
+      // When a "define" from the entry/exit is in the "environment", add it to the exported environments (and overwrite any existing environment variable of the same name).
+      controller_entry_t *entry = 0;
+
+      if (process->type == controller_data_type_entry_e) {
+        entry = &global->setting->entry;
+      }
+      else if (process->type == controller_data_type_exit_e) {
+        entry = &global->setting->exit;
+      }
+
+      if (entry) {
+        for (i = 0; i < entry->define.used; ++i) {
+
+          for (j = 0; j < process->rule.environment.used; ++j) {
+
+            if (f_compare_dynamic(entry->define.array[i].key, process->rule.environment.array[j]) == F_equal_to) {
+
+              for (k = 0; k < environment.used; ++k) {
+
+                if (f_compare_dynamic(entry->define.array[i].key, environment.array[k].key) == F_equal_to) {
+
+                  environment.array[k].value.used = 0;
+
+                  status = f_string_dynamic_append(entry->define.array[i].value, &environment.array[k].value);
+
+                  if (F_status_is_error(status)) {
+                    controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
+
+                    return status;
+                  }
+
+                  break;
+                }
+              } // for
+
+              if (k == environment.used) {
+                status = f_string_maps_append(entry->define.array[i], &environment);
+
+                if (F_status_is_error(status)) {
+                  controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_maps_append", F_true);
+
+                  return status;
+                }
+              }
+
+              break;
+            }
+          } // for
+        } // for
+      }
+
+      // When a "define" is in the "environment", add it to the exported environments (and overwrite any existing environment variable of the same name).
+      for (i = 0; i < process->rule.define.used; ++i) {
+
+        for (j = 0; j < process->rule.environment.used; ++j) {
+
+          if (f_compare_dynamic(process->rule.define.array[i].key, process->rule.environment.array[j]) == F_equal_to) {
+
+            for (k = 0; k < environment.used; ++k) {
+
+              if (f_compare_dynamic(process->rule.define.array[i].key, environment.array[k].key) == F_equal_to) {
+
+                environment.array[k].value.used = 0;
+
+                status = f_string_dynamic_append(process->rule.define.array[i].value, &environment.array[k].value);
+
+                if (F_status_is_error(status)) {
+                  controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
+
+                  return status;
+                }
+
+                break;
+              }
+            } // for
+
+            if (k == environment.used) {
+              status = f_string_maps_append(process->rule.define.array[i], &environment);
+
+              if (F_status_is_error(status)) {
+                controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_maps_append", F_true);
+
+                return status;
+              }
+            }
+
+            break;
+          }
+        } // for
+      } // for
+    }
+    else {
+      controller_entry_t *entry = 0;
+
+      if (process->type == controller_data_type_entry_e) {
+        entry = &global->setting->entry;
+      }
+      else if (process->type == controller_data_type_exit_e) {
+        entry = &global->setting->exit;
+      }
+
+      // When a custom define is specified, it needs to be exported into the environment.
+      if (entry->define.used || process->rule.define.used) {
+
+        // Copy all environment variables over when a custom define is used.
+        status = f_environment_get_all(&environment);
+
+        if (F_status_is_error(status)) {
+          controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_environment_get_all", F_true);
+
+          return status;
+        }
+
+        for (i = 0; i < entry->define.used; ++i) {
+
+          status = f_string_maps_append(entry->define.array[i], &environment);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_maps_append", F_true);
+
+            return status;
+          }
+        } // for
+
+        for (i = 0; i < process->rule.define.used; ++i) {
+
+          status = f_string_maps_append(process->rule.define.array[i], &environment);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_maps_append", F_true);
+
+            return status;
+          }
+        } // for
+      }
+      else {
+
+        // When no custom environment variables are defined, just let the original environment pass through.
+        execute_set.parameter.environment = 0;
+      }
+    }
+
+    for (i = 0; i < process->rule.items.used && controller_thread_is_enabled_process(process, global->thread); ++i) {
+
+      if (process->rule.items.array[i].type == controller_rule_item_type_settings_e) continue;
+
+      for (j = 0; j < process->rule.items.array[i].actions.used; ++j) {
+
+        if (!controller_thread_is_enabled_process(process, global->thread)) {
+          status = F_status_set_error(F_interrupt);
+
+          break;
+        }
+
+        if (process->rule.items.array[i].actions.array[j].type != action) continue;
+
+        execute_set.parameter.data = 0;
+        execute_set.parameter.option = FL_execute_parameter_option_threadsafe_d | FL_execute_parameter_option_return_d;
+
+        if (process->rule.items.array[i].with & controller_with_full_path_d) {
+          execute_set.parameter.option |= FL_execute_parameter_option_path_d;
+        }
+
+        if (process->rule.items.array[i].with & controller_with_session_new_d) {
+          execute_set.parameter.option |= FL_execute_parameter_option_session_d;
+        }
+
+        if (process->rule.items.array[i].type == controller_rule_item_type_command_e) {
+          status = controller_rule_expand(global, process->rule.items.array[i].actions.array[j], process);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, process->cache.action, F_status_set_fine(status), "controller_rule_expand", F_true, F_false);
+
+            break;
+          }
+
+          do {
+            status = controller_rule_execute_foreground(process->rule.items.array[i].type, f_string_empty_s, process->cache.expanded, options, &execute_set, process);
+
+            if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+            if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+          } while (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0);
+
+          if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+          if (F_status_is_error(status)) {
+            process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+            if (!(options & controller_process_option_simulate_d)) break;
+
+            success = F_status_set_error(F_failure);
+          }
+          else if (success == F_false || success == F_ignore) {
+            success = F_true;
+          }
+        }
+        else if (process->rule.items.array[i].type == controller_rule_item_type_script_e) {
+          status = controller_rule_expand(global, process->rule.items.array[i].actions.array[j], process);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, process->cache.action, F_status_set_fine(status), "controller_rule_expand", F_true, F_false);
+
+            break;
+          }
+
+          if (process->cache.expanded.used) {
+            execute_set.parameter.data = &process->cache.expanded.array[0];
+          }
+          else {
+            execute_set.parameter.data = 0;
+          }
+
+          do {
+            if (process->rule.engine.used) {
+              status = controller_rule_execute_foreground(process->rule.items.array[i].type, process->rule.engine, process->rule.engine_arguments, options, &execute_set, process);
+            }
+            else {
+              status = controller_rule_execute_foreground(process->rule.items.array[i].type, *global->main->setting.default_engine, process->rule.engine_arguments, options, &execute_set, process);
+            }
+
+            if (status == F_child || F_status_set_fine(status) == F_lock) break;
+            if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+          } while (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0);
+
+          if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+          if (F_status_is_error(status)) {
+            process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+            if (!(options & controller_process_option_simulate_d)) break;
+
+            success = F_status_set_error(F_failure);
+          }
+          else if (success == F_false || success == F_ignore) {
+            success = F_true;
+          }
+        }
+        else if (process->rule.items.array[i].type == controller_rule_item_type_service_e) {
+          status = controller_rule_expand(global, process->rule.items.array[i].actions.array[j], process);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, process->cache.action, F_status_set_fine(status), "controller_rule_expand", F_true, F_false);
+
+            break;
+          }
+
+          if (process->rule.items.array[i].pid_file.used) {
+            do {
+              status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, f_string_empty_s, process->cache.expanded, options, process->rule.items.array[i].with, &execute_set, process);
+
+              if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+              if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+            } while (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0);
+
+            if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+            if (F_status_is_error(status)) {
+              process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+              if (!(options & controller_process_option_simulate_d)) break;
+
+              success = F_status_set_error(F_failure);
+            }
+            else if (success == F_false || success == F_ignore) {
+              success = F_true;
+            }
+          }
+          else {
+            success = F_status_set_error(F_failure);
+
+            controller_rule_action_print_error_missing_pid(&global->main->program.error, process->rule.alias);
+          }
+        }
+        else if (process->rule.items.array[i].type == controller_rule_item_type_utility_e) {
+          if (process->rule.items.array[i].pid_file.used) {
+            status = controller_rule_expand(global, process->rule.items.array[i].actions.array[j], process);
+
+            if (F_status_is_error(status)) {
+              controller_rule_print_error(global->thread, &global->main->program.error, process->cache.action, F_status_set_fine(status), "controller_rule_expand", F_true, F_false);
+
+              break;
+            }
+
+            if (process->cache.expanded.used) {
+              execute_set.parameter.data = &process->cache.expanded.array[0];
+            }
+            else {
+              execute_set.parameter.data = 0;
+            }
+
+            do {
+              status = controller_rule_execute_pid_with(process->rule.items.array[i].pid_file, process->rule.items.array[i].type, process->rule.engine.used ? process->rule.engine : *global->main->setting.default_engine, process->rule.engine_arguments, options, process->rule.items.array[i].with, &execute_set, process);
+
+              if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+              if (F_status_is_error(status) && F_status_set_fine(status) != F_failure) break;
+
+            } while (controller_rule_execute_rerun(controller_rule_action_type_to_action_execute_type(action), process, &process->rule.items.array[i]) > 0);
+
+            if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock) break;
+
+            if (F_status_is_error(status)) {
+              process->rule.items.array[i].actions.array[j].status = F_status_set_error(F_failure);
+
+              if (!(options & controller_process_option_simulate_d)) break;
+
+              success = F_status_set_error(F_failure);
+            }
+            else if (success == F_false || success == F_ignore) {
+              success = F_true;
+            }
+          }
+          else {
+            success = F_status_set_error(F_failure);
+
+            controller_rule_action_print_error_missing_pid(&global->main->program.error, process->rule.alias);
+          }
+        }
+        else {
+          if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+            controller_lock_print(global->main->program.warning.to, global->thread);
+
+            fl_print_format("%r%[%QAction type is unknown, ignoring.%]%r", global->main->program.warning.to, f_string_eol_s, global->main->program.warning.context, global->main->program.warning.prefix, global->main->program.warning.context, f_string_eol_s);
+
+            controller_rule_print_rule_message_cache(&global->main->program.warning, process->cache.action, F_true);
+
+            controller_unlock_print_flush(global->main->program.warning.to, global->thread);
+          }
+
+          if (success == F_false) {
+            success = F_ignore;
+          }
+
+          continue;
+        }
+      } // for
+
+      if (status == F_child || F_status_set_fine(status) == F_interrupt || F_status_is_error(status) && !(options & controller_process_option_simulate_d)) {
+         break;
+       }
+    } // for
+
+    f_memory_arrays_resize(0, sizeof(f_string_map_t), (void **) &environment.array, &environment.used, &environment.size, &f_string_maps_delete_callback);
+
+    // Lock failed, attempt to re-establish lock before returning.
+    if (F_status_set_fine(status) == F_lock) {
+      status = controller_lock_read(process, global->thread, &process->lock);
+      if (F_status_is_error(status)) return F_status_set_error(F_lock);
+
+      success = F_false;
+    }
+
+    if (!controller_thread_is_enabled_process(process, global->thread)) {
+      return F_status_set_error(F_interrupt);
+    }
+
+    if (status == F_child || F_status_is_error(status)) {
+      return status;
+    }
+
+    if (success == F_false || success == F_failure) {
+      return F_status_set_error(F_failure);
+    }
+
+    if (success == F_ignore) {
+      return F_ignore;
+    }
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_execute_
+
+#ifndef _di_controller_rule_execute_foreground_
+  f_status_t controller_rule_execute_foreground(const uint8_t type, const f_string_static_t program, const f_string_statics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_instance_t * const instance) {
+
+    if (!instance) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+    f_status_t status_lock = F_okay;
+
+    controller_main_t * const main = (controller_main_t *) process->main_data;
+    controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+
+    f_execute_result_t result = f_execute_result_t_initialize;
+
+    status = controller_pids_increase(&process->childs);
+
+    if (F_status_is_error(status)) {
+      controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+      return status;
+    }
+
+    pid_t *child = 0;
+
+    {
+      f_number_unsigned_t i = 0;
+
+      while (i < process->childs.used && process->childs.array[i]) {
+        ++i;
+      } // while
+
+      child = &process->childs.array[i];
+
+      if (i == process->childs.used) {
+        ++process->childs.used;
+      }
+    }
+
+    if (options & controller_process_option_simulate_d) {
+      if (main->program.output.verbosity != f_console_verbosity_quiet_e) {
+        controller_lock_print(main->program.output.to, thread);
+
+        fl_print_format("%rSimulating execution of '%[", main->program.output.to, f_string_eol_s, main->program.context.set.title);
+
+        if (program.used) {
+          f_print_dynamic_safely(program, main->program.output.to);
+        }
+        else {
+          f_print_dynamic_safely(arguments.array[0], main->program.output.to);
+        }
+
+        fl_print_format("%]' with the arguments: '%[", main->program.output.to, main->program.context.set.title, main->program.context.set.important);
+
+        for (f_number_unsigned_t i = program.used ? 0 : 1; i < arguments.used; ++i) {
+
+          if (program.used && i || !program.used && i > 1) {
+            f_print_dynamic_raw(f_string_space_s, main->program.output.to);
+          }
+
+          f_print_dynamic_safely(arguments.array[i], main->program.output.to);
+        } // for
+
+        fl_print_format("%]' from '", main->program.output.to, main->program.context.set.important);
+        fl_print_format("%[%Q%]'.%r", main->program.output.to, main->program.context.set.notable, process->rule.name, main->program.context.set.notable, f_string_eol_s);
+
+        controller_unlock_print_flush(main->program.output.to, thread);
+      }
+
+      // Sleep for less than a second to better show simulation of synchronous vs asynchronous.
+      {
+        const f_time_spec_t delay = controller_time_milliseconds(controller_thread_simulation_timeout_d);
+
+        if (controller_time_sleep_nanoseconds(main, (controller_process_t *) process->main_setting, delay) == -1) {
+          status = F_status_set_error(F_interrupt);
+        }
+      }
+
+      if (F_status_set_fine(status) != F_interrupt) {
+        fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize_1(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment_d ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &f_string_empty_s);
+
+        status = fll_execute_program(*main->setting.default_engine, process->rule.engine_arguments, &simulated_parameter, &execute_set->as, (void *) &result);
+      }
+    }
+    else {
+      status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result);
+    }
+
+    if (status == F_parent) {
+      const pid_t id_child = result.pid;
+      result.status = 0;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, thread);
+
+        if (F_status_set_fine(status_lock) != F_interrupt) {
+          status = controller_lock_read_process(process, thread, &process->lock);
+
+          if (status == F_okay) {
+            return status_lock;
+          }
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      // Assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
+      *child = id_child;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, thread);
+      }
+
+      if (F_status_set_fine(status_lock) != F_interrupt) {
+
+        // Have the parent wait for the child process to finish.
+        waitpid(id_child, &result.status, 0);
+      }
+
+      if (F_status_set_fine(status_lock) == F_interrupt || !controller_thread_is_enabled_process(process, thread)) {
+        if (status_lock == F_okay) {
+          return F_status_set_error(F_interrupt);
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      if (status_lock == F_okay) {
+        f_thread_unlock(&process->lock);
+      }
+
+      status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, thread);
+
+        if (F_status_set_fine(status_lock) != F_interrupt) {
+          status = controller_lock_read_process(process, thread, &process->lock);
+
+          if (status == F_okay) {
+            return status_lock;
+          }
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      process->result = result.status;
+
+      // Remove the pid now that waidpid() has returned.
+      *child = 0;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, thread);
+
+        return F_status_set_error(F_lock);
+      }
+
+      if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) {
+        status = F_status_set_error(F_failure);
+      }
+      else {
+        status = F_okay;
+      }
+    }
+    else {
+      main->program.child = result.status;
+
+      if (!controller_thread_is_enabled_process(process, thread)) {
+        return F_status_set_error(F_interrupt);
+      }
+    }
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if (status == F_child || status == F_capability || status == F_group || status == F_nice || status == F_user) {
+        status = F_child;
+      }
+      else {
+        status = F_status_set_error(status);
+      }
+    }
+
+    if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+      return status;
+    }
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
+        controller_rule_item_print_error_execute(type == controller_rule_item_type_script_e, program.used ? program : arguments.array[0], status, process);
+      }
+      else {
+        controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "fll_execute_program", F_true);
+      }
+
+      status = F_status_set_error(status);
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_execute_foreground_
+
+#ifndef _di_controller_rule_execute_pid_with_
+  f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const f_string_static_t program, const f_string_statics_t arguments, const uint8_t options, const uint8_t with, controller_execute_set_t * const execute_set, controller_instance_t * const instance) {
+
+    if (!execute_set || !instance) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+    f_status_t status_lock = F_okay;
+
+    controller_main_t * const main = (controller_main_t *) process->main_data;
+    controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+
+    f_execute_result_t result = f_execute_result_t_initialize;
+
+    status = controller_pids_increase(&process->childs);
+
+    if (F_status_is_error(status)) {
+      controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "controller_pids_increase", F_true);
+
+      return status;
+    }
+
+    status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &process->path_pids.array, &process->path_pids.used, &process->path_pids.size);
+
+    if (F_status_is_error(status)) {
+      controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "f_memory_array_increase", F_true);
+
+      return status;
+    }
+
+    pid_t *child = 0;
+    f_string_dynamic_t *child_pid_file = 0;
+
+    {
+      f_number_unsigned_t i = 0;
+
+      while (i < process->childs.used && process->childs.array[i]) {
+        ++i;
+      } // while
+
+      child = &process->childs.array[i];
+
+      if (i == process->childs.used) {
+        ++process->childs.used;
+      }
+
+      i = 0;
+
+      while (i < process->path_pids.used && process->path_pids.array[i].used) {
+        ++i;
+      } // while
+
+      child_pid_file = &process->path_pids.array[i];
+
+      if (i == process->path_pids.used) {
+        ++process->path_pids.used;
+      }
+    }
+
+    status = f_file_exists(pid_file, F_true);
+
+    if (F_status_is_error(status)) {
+      controller_print_error_file(thread, &global->main->program.error, F_status_set_fine(status), "f_file_exists", F_true, pid_file, f_file_operation_find_s, fll_error_file_type_file_e);
+
+      return status;
+    }
+
+    if (status == F_true) {
+      controller_print_error_file(thread, &global->main->program.error, F_file_found, "f_file_exists", F_true, pid_file, f_file_operation_find_s, fll_error_file_type_file_e);
+
+      return F_status_set_error(F_file_found);
+    }
+
+    status = f_string_dynamic_append_nulless(pid_file, child_pid_file);
+
+    if (F_status_is_error(status)) {
+      controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
+
+      return status;
+    }
+
+    if (options & controller_process_option_simulate_d) {
+      if (main->program.error.verbosity > f_console_verbosity_error_e) {
+        controller_lock_print(main->program.error.to, thread);
+
+        fl_print_format("%rSimulating execution of '%[", main->program.error.to, f_string_eol_s, main->program.context.set.title);
+
+        if (program.used) {
+          f_print_dynamic_safely(program, main->program.error.to);
+        }
+        else {
+          f_print_dynamic_safely(arguments.array[0], main->program.error.to);
+        }
+
+        fl_print_format("%]' with the arguments: '%[", main->program.error.to, main->program.context.set.title, main->program.context.set.important);
+
+        for (f_number_unsigned_t i = program.used ? 0 : 1; i < arguments.used; ++i) {
+
+          if (program.used && i || !program.used && i > 1) {
+            f_print_dynamic_raw(f_string_space_s, main->program.error.to);
+          }
+
+          f_print_dynamic_safely(arguments.array[i], main->program.error.to);
+        } // for
+
+        fl_print_format("%]' from '", main->program.error.to, main->program.context.set.important);
+        fl_print_format("%[%Q%]'.%r", main->program.error.to, main->program.context.set.notable, process->rule.name, main->program.context.set.notable, f_string_eol_s);
+
+        controller_unlock_print_flush(main->program.error.to, thread);
+      }
+
+      // Sleep for less than a second to better show simulation of synchronous vs asynchronous.
+      {
+        const f_time_spec_t delay = controller_time_milliseconds(controller_thread_simulation_timeout_d);
+
+        if (controller_time_sleep_nanoseconds(main, (controller_process_t *) process->main_setting, delay) == -1) {
+          status = F_status_set_error(F_interrupt);
+        }
+      }
+
+      if (F_status_set_fine(status) != F_interrupt) {
+        const f_string_statics_t simulated_arguments = f_string_statics_t_initialize;
+        fl_execute_parameter_t simulated_parameter = macro_fl_execute_parameter_t_initialize_1(execute_set->parameter.option, execute_set->parameter.wait, process->rule.has & controller_rule_has_environment_d ? execute_set->parameter.environment : 0, execute_set->parameter.signals, &f_string_empty_s);
+
+        status = fll_execute_program(*main->setting.default_engine, simulated_arguments, &simulated_parameter, &execute_set->as, (void *) &result);
+      }
+    }
+    else {
+      status = fll_execute_program(program, arguments, &execute_set->parameter, &execute_set->as, (void *) &result);
+    }
+
+    if (status == F_parent) {
+      const pid_t id_child = result.pid;
+      result.status = 0;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, thread);
+
+        if (F_status_set_fine(status_lock) != F_interrupt) {
+          status = controller_lock_read_process(process, thread, &process->lock);
+
+          if (status == F_okay) {
+            return status_lock;
+          }
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      // Assign the child process id to allow for the cancel process to send appropriate termination signals to the child process.
+      *child = id_child;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, thread);
+      }
+
+      if (F_status_set_fine(status_lock) != F_interrupt) {
+
+        // The child process should perform the change into background, therefore it is safe to wait for the child to exit (another process is spawned).
+        waitpid(id_child, &result.status, 0);
+      }
+
+      if (!controller_thread_is_enabled_process(process, thread)) {
+        if (status_lock == F_okay) {
+          return F_status_set_error(F_interrupt);
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      if (status_lock == F_okay) {
+        f_thread_unlock(&process->lock);
+      }
+
+      status_lock = controller_lock_write_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, thread);
+
+        if (F_status_set_fine(status_lock) != F_interrupt) {
+          status = controller_lock_read_process(process, thread, &process->lock);
+
+          if (status == F_okay) {
+            return status_lock;
+          }
+        }
+
+        return F_status_set_error(F_lock);
+      }
+
+      process->result = result.status;
+
+      // Remove the pid now that waidpid() has returned.
+      *child = 0;
+
+      f_thread_unlock(&process->lock);
+
+      status_lock = controller_lock_read_process(process, thread, &process->lock);
+
+      if (F_status_is_error(status_lock)) {
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, thread);
+
+        return F_status_set_error(F_lock);
+      }
+
+      if (WIFEXITED(result.status) ? WEXITSTATUS(result.status) : 0) {
+        status = F_status_set_error(F_failure);
+      }
+      else {
+        status = F_okay;
+      }
+    }
+    else {
+      main->program.child = result.status;
+
+      if (!controller_thread_is_enabled_process(process, thread)) {
+        return F_status_set_error(F_interrupt);
+      }
+    }
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if (status == F_child || status == F_capability || status == F_group || status == F_nice || status == F_user) {
+        status = F_child;
+      }
+      else {
+        status = F_status_set_error(status);
+      }
+    }
+
+    if (status == F_child || F_status_set_fine(status) == F_interrupt) {
+      return status;
+    }
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if ((WIFEXITED(process->result) && WEXITSTATUS(process->result)) || status == F_control_group || status == F_failure || status == F_limit || status == F_processor || status == F_schedule) {
+        controller_rule_item_print_error_execute(type == controller_rule_item_type_utility_e, program.used ? program : arguments.array[0], status, process);
+      }
+      else {
+        controller_print_error(thread, &global->main->program.error, F_status_set_fine(status), "fll_execute_program", F_true);
+      }
+
+      return F_status_set_error(status);
+    }
+
+    return status;
+  }
+#endif // _di_controller_rule_execute_pid_with_
+
+#ifndef _di_controller_rule_execute_rerun_
+  int8_t controller_rule_execute_rerun(const uint8_t action, controller_instance_t * const instance, controller_rule_item_t * const item) {
+
+    if (!instance || !item) return F_status_set_error(F_parameter);
+
+    const int result = WIFEXITED(process->result) ? WEXITSTATUS(process->result) : 0;
+
+    if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure_d : controller_rule_rerun_is_success_d)) {
+      controller_main_t * const main = (controller_main_t *) process->main_data;
+      controller_thread_t * const thread = (controller_thread_t *) process->main_thread;
+      controller_rule_rerun_item_t *rerun_item = result ? &item->reruns[action].failure : &item->reruns[action].success;
+
+      if (!controller_thread_is_enabled_process(process, thread)) return -2;
+
+      if (!rerun_item->max || rerun_item->count < rerun_item->max) {
+        if (main->program.error.verbosity == f_console_verbosity_debug_e) {
+          controller_lock_print(main->program.output.to, thread);
+
+          fl_print_format("%rRe-running '", main->program.output.to, f_string_eol_s);
+          fl_print_format("%[%r%]' '", main->program.output.to, main->program.context.set.title, process->rule.alias, main->program.context.set.title);
+          fl_print_format("%[%r%]' with a ", main->program.output.to, main->program.context.set.notable, controller_rule_action_execute_type_name(action), main->program.context.set.notable);
+          fl_print_format("%[%r%] of ", main->program.output.to, main->program.context.set.notable, controller_delay_s, main->program.context.set.notable);
+          fl_print_format("%[%ul%] MegaTime", main->program.output.to, main->program.context.set.notable, rerun_item->delay, main->program.context.set.notable);
+
+          if (rerun_item->max) {
+            fl_print_format(" for %[%ul%]", main->program.output.to, main->program.context.set.notable, rerun_item->count, main->program.context.set.notable);
+            fl_print_format(" of %[%r%] ", main->program.output.to, main->program.context.set.notable, controller_max_s, main->program.context.set.notable);
+            fl_print_format(f_string_format_un_single_s.string, main->program.output.to, main->program.context.set.notable, rerun_item->max, main->program.context.set.notable);
+            fl_print_format(".%r", main->program.output.to, f_string_eol_s);
+          }
+          else {
+            fl_print_format(" with no %[%r%].%r", main->program.output.to, main->program.context.set.notable, controller_max_s, main->program.context.set.notable, f_string_eol_s);
+          }
+
+          controller_unlock_print_flush(main->program.output.to, thread);
+        }
+
+        if (rerun_item->delay) {
+          const f_time_spec_t delay = controller_time_milliseconds(rerun_item->delay);
+
+          if (controller_time_sleep_nanoseconds(main, (controller_process_t *) process->main_setting, delay) == -1) {
+            return -1;
+          }
+
+          if (!controller_thread_is_enabled_process(process, thread)) return -2;
+        }
+
+        if (item->reruns[action].is & (result ? controller_rule_rerun_is_failure_reset_d : controller_rule_rerun_is_success_reset_d)) {
+          if (result) {
+            item->reruns[action].success.count = 0;
+          }
+          else {
+            item->reruns[action].failure.count = 0;
+          }
+        }
+
+        if (rerun_item->max) {
+          ++rerun_item->count;
+        }
+
+        return F_true;
+      }
+    }
+
+    return F_false;
+  }
+#endif // _di_controller_rule_execute_rerun_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/execute.h b/sources/c/main/rule/execute.h
new file mode 100644 (file)
index 0000000..76d9880
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "execute" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_execute_h
+#define _controller_main_rule_execute_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Perform an execution of the given rule.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param action
+ *   The action to perform based on the action type codes.
+ *
+ *   Only subset of the action type codes are supported:
+ *   - controller_rule_action_type_kill_e
+ *   - controller_rule_action_type_pause_e
+ *   - controller_rule_action_type_reload_e
+ *   - controller_rule_action_type_restart_e
+ *   - controller_rule_action_type_resume_e
+ *   - controller_rule_action_type_start_e
+ *   - controller_rule_action_type_stop_e
+ * @param options
+ *   Process options to consider when executing.
+ *   If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param process
+ *   The process data for processing this rule.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_child on child process exiting.
+ *   F_ignore if the rule is unknown and nothing can be done.
+ *
+ *   F_failure (with error bit) if failed to execute.
+ *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *   F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ *   On success and the rule is run synchronously, then the individual status for the rule is set to F_complete.
+ *   On success and the rule is run asynchronously, then the individual status for the rule is set to F_busy.
+ *   On failure, the individual status for the rule is set to an appropriate error status.
+ */
+#ifndef _di_controller_rule_execute_
+  extern f_status_t controller_rule_execute(controller_global_t * const global, const uint8_t action, const uint8_t options, controller_instance_t * const instance);
+#endif // _di_controller_rule_execute_
+
+/**
+ * Perform an execution of the given rule in the foreground.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * @param type
+ *   The item type code.
+ * @param program
+ *   The program to use (such as "bash").
+ * @param arguments
+ *   The arguments to pass to the program.
+ * @param options
+ *   Process options to consider when executing.
+ *   If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param execute_set
+ *   The execute parameter and as settings.
+ * @param process
+ *   The process data for processing this rule.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_child on child process exiting.
+ *
+ *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *   F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ *   Errors (with error bit) from: fll_execute_program().
+ *
+ * @see fll_execute_program()
+ */
+#ifndef _di_controller_rule_execute_foreground_
+  extern f_status_t controller_rule_execute_foreground(const uint8_t type, const f_string_static_t program, const f_string_statics_t arguments, const uint8_t options, controller_execute_set_t * const execute_set, controller_instance_t * const instance);
+#endif // _di_controller_rule_execute_foreground_
+
+/**
+ * Perform an execution of the given rule in the foreground or background and creating a PID file.
+ *
+ * This requires that a read lock be set on process->lock before being called.
+ *
+ * When this is synchronous, this will wait for the PID file to be generated before continuing.
+ * When this is asynchronous, this will continue on adding the rule id and action to the asynchronous list.
+ *
+ * @param pid_file
+ *   The path to the PID file.
+ * @param type
+ *   The item type code.
+ * @param program
+ *   The program to use (such as "bash").
+ * @param arguments
+ *   The arguments to pass to the program.
+ * @param options
+ *   Process options to consider when executing.
+ *   If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ * @param with
+ *   The "with" option flags.
+ * @param execute_set
+ *   The execute parameter and as settings.
+ * @param process
+ *   The process data for processing this rule.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_child on child process exiting.
+ *
+ *   F_file_found (with error bit) if the PID file already exists.
+ *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *   F_lock (with error bit) if failed to re-establish read lock on process->lock while returning.
+ *
+ *   Errors (with error bit) from: fll_execute_program().
+ *
+ * @see fll_execute_program()
+ */
+#ifndef _di_controller_rule_execute_pid_with_
+  extern f_status_t controller_rule_execute_pid_with(const f_string_dynamic_t pid_file, const uint8_t type, const f_string_static_t program, const f_string_statics_t arguments, const uint8_t options, const uint8_t with, controller_execute_set_t * const execute_set, controller_instance_t * const instance);
+#endif // _di_controller_rule_execute_pid_with_
+
+/**
+ * Determine whether or not an execute rule should be re-run, applying a delay as requested.
+ *
+ * @param action
+ *   The action type.
+ * @param instance
+ *   The instance data for processing this rule.
+ * @param item
+ *   The rule item being executed.
+ *
+ * @return
+ *   A positive number to designate re-run.
+ *   0 to designate do not re-run.
+ *   -1 to designate an error from nanosleep(), with errno set to values like:
+ *     - EFAULT: Designates that there was a problem copying information from user space.
+ *     - EINTR:  Consider this having returned F_interrupt.
+ *     - EINVAL: Consider this having returned F_status_set_error(F_parameter);
+ *   -2 to designate exit due to signal/disabled thread.
+ */
+#ifndef _di_controller_rule_execute_rerun_
+  extern int8_t controller_rule_execute_rerun(const uint8_t action, controller_instance_t * const instance, controller_rule_item_t * const item);
+#endif // _di_controller_rule_execute_rerun_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_execute_h
diff --git a/sources/c/main/rule/expand.c b/sources/c/main/rule/expand.c
new file mode 100644 (file)
index 0000000..dbc6ef2
--- /dev/null
@@ -0,0 +1,386 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_expand_
+  f_status_t controller_rule_expand(controller_global_t * const global, const controller_rule_action_t action, controller_instance_t * const instance) {
+
+    if (!global || !instance) return F_status_set_error(F_parameter);
+
+    process->cache.expanded.used = 0;
+
+    if (!action.parameters.used) return F_okay;
+
+    f_status_t status = f_memory_array_increase_by(action.parameters.used, sizeof(f_string_dynamic_t), (void **) &process->cache.expanded.array, &process->cache.expanded.used, &process->cache.expanded.size);
+    if (F_status_is_error(status)) return status;
+
+    f_number_unsigned_t i = 0;
+    f_number_unsigned_t first = 0;
+    f_range_t range = f_range_t_initialize;
+
+    f_iki_data_t *iki_data = 0;
+    f_string_dynamic_t *buffer = 0;
+
+    for (; process->cache.expanded.used < action.parameters.used; ++process->cache.expanded.used) {
+
+      buffer = &process->cache.expanded.array[process->cache.expanded.used];
+      buffer->used = 0;
+
+      if (action.ikis.array[process->cache.expanded.used].variable.used) {
+        iki_data = &action.ikis.array[process->cache.expanded.used];
+
+        // Allocate enough room plus an extra buffer to help reduce reallocations.
+        status = f_memory_array_increase_by(action.parameters.array[process->cache.expanded.used].used + controller_common_allocation_large_d, sizeof(f_char_t), (void **) &buffer->string, &buffer->used, &buffer->size);
+
+        // Apply the IKI delimits.
+        for (i = 0; i < iki_data->delimits.used; ++i) {
+          action.parameters.array[process->cache.expanded.used].string[iki_data->delimits.array[i]] = f_iki_syntax_placeholder_s.string[0];
+        } // for
+
+        if (iki_data->variable.used) {
+          for (i = 0, first = 0; i < iki_data->variable.used; ++i) {
+
+            // Copy everything up to the start of the IKI variable.
+            if (first < iki_data->variable.array[i].start) {
+              range.start = first;
+              range.stop = iki_data->variable.array[i].start - 1;
+
+              status = f_string_dynamic_partial_append_nulless(action.parameters.array[process->cache.expanded.used], range, buffer);
+              if (F_status_is_error(status)) break;
+            }
+
+            status = controller_rule_expand_iki(process, action.parameters.array[process->cache.expanded.used], iki_data->vocabulary.array[i], iki_data->content.array[i], buffer);
+            if (F_status_is_error(status)) break;
+
+            first = iki_data->variable.array[i].stop + 1;
+          } // for
+
+          if (F_status_is_error(status)) break;
+
+          // Copy everything after the last IKI variable to the end of the content.
+          if (first < action.parameters.array[process->cache.expanded.used].used) {
+            range.start = first;
+            range.stop = action.parameters.array[process->cache.expanded.used].used - 1;
+
+            status = f_string_dynamic_partial_append(action.parameters.array[process->cache.expanded.used], range, buffer);
+          }
+        }
+        else {
+          status = f_string_dynamic_append_nulless(action.parameters.array[process->cache.expanded.used], buffer);
+        }
+
+        // Unapply the IKI delimits.
+        for (i = 0; i < iki_data->delimits.used; ++i) {
+          action.parameters.array[process->cache.expanded.used].string[iki_data->delimits.array[i]] = f_iki_syntax_slash_s.string[0];
+        } // for
+      }
+      else {
+        status = f_string_dynamic_append(action.parameters.array[process->cache.expanded.used], buffer);
+      }
+
+      if (F_status_is_error(status)) return status;
+    } // for
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_expand_
+
+#ifndef _di_controller_rule_expand_iki_
+  f_status_t controller_rule_expand_iki(controller_instance_t * const instance, const f_string_static_t source, const f_range_t vocabulary, const f_range_t content, f_string_dynamic_t * const destination) {
+
+    if (!instance || !destination) return F_status_set_error(F_parameter);
+    if (vocabulary.start > vocabulary.stop) return F_okay;
+    if (content.start > content.stop) return F_okay;
+
+    f_status_t status = F_okay;
+
+    if (f_compare_dynamic_partial_string(controller_define_s.string, source, controller_define_s.used, vocabulary) == F_equal_to) {
+      f_number_unsigned_t i = 0;
+
+      // First check to see if the environment variable is overwritten by a "define".
+      for (; i < process->rule.define.used; ++i) {
+
+        if (f_compare_dynamic_partial_string(process->rule.define.array[i].key.string, source, process->rule.define.array[i].key.used, content) == F_equal_to) {
+          status = f_string_dynamic_append(process->rule.define.array[i].value, destination);
+          if (F_status_is_error(status)) return status;
+
+          break;
+        }
+      } // for
+
+      if (i == process->rule.define.used) {
+        controller_entry_t * const entry = process->type == controller_data_type_entry_e ? &((controller_process_t *) process->main_setting)->entry : &((controller_process_t *) process->main_setting)->exit;
+
+        for (i = 0; i < entry->define.used; ++i) {
+
+            if (f_compare_dynamic_partial_string(entry->define.array[i].key.string, source, entry->define.array[i].key.used, content) == F_equal_to) {
+            status = f_string_dynamic_append(entry->define.array[i].value, destination);
+            if (F_status_is_error(status)) return status;
+
+            break;
+          }
+        } // for
+
+        if (i == entry->define.used) {
+          i = process->rule.define.used;
+        }
+      }
+
+      if (i == process->rule.define.used) {
+        f_string_static_t buffer = f_string_static_t_initialize;
+        buffer.used = (content.stop - content.start) + 1;
+
+        f_char_t buffer_string[buffer.used + 1];
+
+        memcpy(buffer_string, source.string + content.start, sizeof(f_char_t) * buffer.used);
+        buffer_string[buffer.used] = 0;
+        buffer.string = buffer_string;
+        process->cache.action.generic.used = 0;
+
+        status = f_environment_get(buffer, &process->cache.action.generic);
+        if (F_status_is_error(status)) return status;
+
+        status = f_string_dynamic_append(process->cache.action.generic, destination);
+        if (F_status_is_error(status)) return status;
+      }
+    }
+    else if (f_compare_dynamic_partial_string(controller_parameter_s.string, source, controller_parameter_s.used, vocabulary) == F_equal_to) {
+      f_number_unsigned_t i = 0;
+
+      for (; i < process->rule.parameter.used; ++i) {
+
+        if (f_compare_dynamic_partial_string(process->rule.parameter.array[i].key.string, source, process->rule.parameter.array[i].key.used, content) == F_equal_to) {
+          status = f_string_dynamic_append(process->rule.parameter.array[i].value, destination);
+          if (F_status_is_error(status)) return status;
+
+          break;
+        }
+      } // for
+
+      if (i == process->rule.parameter.used) {
+        controller_entry_t * const entry = process->type == controller_data_type_entry_e ? &((controller_process_t *) process->main_setting)->entry : &((controller_process_t *) process->main_setting)->exit;
+
+        for (i = 0; i < entry->parameter.used; ++i) {
+
+            if (f_compare_dynamic_partial_string(entry->parameter.array[i].key.string, source, entry->parameter.array[i].key.used, content) == F_equal_to) {
+            status = f_string_dynamic_append(entry->parameter.array[i].value, destination);
+            if (F_status_is_error(status)) return status;
+
+            break;
+          }
+        } // for
+      }
+    }
+    else if (f_compare_dynamic_partial_string(controller_program_s.string, source, controller_program_s.used, vocabulary) == F_equal_to) {
+      f_string_static_t * const argv = ((controller_main_t *) process->main_data)->program.parameters.arguments.array;
+      f_console_parameters_t * const parameters = &((controller_main_t *) process->main_data)->program.parameters;
+
+      const f_string_static_t options[] = {
+        f_console_standard_long_light_s,
+        f_console_standard_long_dark_s,
+        f_console_standard_long_no_color_s,
+        f_console_standard_long_quiet_s,
+        f_console_standard_long_normal_s,
+        f_console_standard_long_verbose_s,
+        f_console_standard_long_debug_s,
+        controller_long_init_s,
+        controller_long_interruptible_s,
+        controller_long_daemon_s,
+        controller_long_simulate_s,
+        controller_long_uninterruptible_s,
+        controller_long_validate_s,
+
+        // Option and Value.
+        controller_long_cgroup_s,
+        controller_long_pid_s,
+        controller_long_settings_s,
+        controller_long_socket_s,
+      };
+
+      const f_string_static_t symbols[] = {
+        f_console_symbol_short_inverse_s, // light.
+        f_console_symbol_short_inverse_s, // dark.
+        f_console_symbol_short_inverse_s, // no_color.
+        f_console_symbol_short_inverse_s, // quiet.
+        f_console_symbol_short_inverse_s, // normal.
+        f_console_symbol_short_inverse_s, // verbose.
+        f_console_symbol_short_inverse_s, // debug.
+        f_console_symbol_short_normal_s,  // daemon.
+        f_console_symbol_short_normal_s,  // init.
+        f_console_symbol_short_normal_s,  // interruptible.
+        f_console_symbol_short_normal_s,  // simulate.
+        f_console_symbol_short_normal_s,  // uninterruptible.
+        f_console_symbol_short_normal_s,  // validate.
+
+        // Option and Value.
+        f_console_symbol_short_normal_s,  // cgroup.
+        f_console_symbol_short_normal_s,  // pid.
+        f_console_symbol_short_normal_s,  // settings.
+        f_console_symbol_short_normal_s,  // socket.
+      };
+
+      const f_string_static_t expands[] = {
+        f_console_standard_short_light_s,
+        f_console_standard_short_dark_s,
+        f_console_standard_short_no_color_s,
+        f_console_standard_short_quiet_s,
+        f_console_standard_short_normal_s,
+        f_console_standard_short_verbose_s,
+        f_console_standard_short_debug_s,
+        controller_short_init_s,
+        controller_short_interruptible_s,
+        controller_short_daemon_s,
+        controller_short_simulate_s,
+        controller_short_uninterruptible_s,
+        controller_short_validate_s,
+
+        // Option and Value.
+        controller_short_cgroup_s,
+        controller_short_pid_s,
+        controller_short_settings_s,
+        controller_short_socket_s,
+      };
+
+      const uint8_t codes[] = {
+        f_console_standard_parameter_help_e,
+        f_console_standard_parameter_dark_e,
+        f_console_standard_parameter_no_color_e,
+        f_console_standard_parameter_verbosity_quiet_e,
+        f_console_standard_parameter_verbosity_normal_e,
+        f_console_standard_parameter_verbosity_verbose_e,
+        f_console_standard_parameter_verbosity_debug_e,
+        controller_parameter_init_e,
+        controller_parameter_interruptible_e,
+        controller_parameter_daemon_e,
+        controller_parameter_simulate_e,
+        controller_parameter_uninterruptible_e,
+        controller_parameter_validate_e,
+
+        // Option and Value.
+        controller_parameter_cgroup_e,
+        controller_parameter_pid_e,
+        controller_parameter_settings_e,
+        controller_parameter_socket_e,
+      };
+
+      // F_false = only with option, F_true with option and value.
+      const bool values[] = {
+        F_false, // light.
+        F_false, // dark.
+        F_false, // no_color.
+        F_false, // quiet.
+        F_false, // normal.
+        F_false, // verbose.
+        F_false, // debug.
+        F_false, // daemon.
+        F_false, // init.
+        F_false, // interruptible.
+        F_false, // simulate.
+        F_false, // uninterruptible.
+        F_false, // validate.
+
+        // Option and Value.
+        F_true, // cgroup.
+        F_true, // pid.
+        F_true, // settings.
+        F_true, // socket.
+      };
+
+      for (f_number_unsigned_t i = 0; i < 17; ++i) {
+
+        if (f_compare_dynamic_partial_string(options[i].string, source, options[i].used, content) == F_equal_to) {
+          if (values[i]) {
+            if (parameters->array[codes[i]].result & f_console_result_value_e) {
+              const f_number_unsigned_t index = parameters->array[codes[i]].values.array[parameters->array[codes[i]].values.used - 1];
+
+              status = f_memory_array_increase_by(symbols[i].used + expands[i].used + f_string_ascii_space_s.used + argv[index].used + 1, sizeof(f_char_t), (void **) &destination->string, &destination->used, &destination->size);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(symbols[i], destination);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(expands[i], destination);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(f_string_ascii_space_s, destination);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(argv[index], destination);
+              if (F_status_is_error(status)) return status;
+            }
+          }
+          else {
+            if (parameters->array[codes[i]].result & f_console_result_found_e) {
+              status = f_memory_array_increase_by(symbols[i].used + expands[i].used + 1, sizeof(f_char_t), (void **) &destination->string, &destination->used, &destination->size);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(symbols[i], destination);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(expands[i], destination);
+              if (F_status_is_error(status)) return status;
+            }
+          }
+
+          break;
+        }
+
+        {
+          f_string_static_t buffer = f_string_static_t_initialize;
+          buffer.used = options[i].used + controller_parameter_map_option_s.used;
+
+          f_char_t buffer_string[buffer.used];
+          buffer.string = buffer_string;
+
+          memcpy(buffer_string, options[i].string, sizeof(f_char_t) * options[i].used);
+          memcpy(buffer_string + options[i].used, controller_parameter_map_option_s.string, sizeof(f_char_t) * controller_parameter_map_option_s.used);
+
+          if (f_compare_dynamic_partial_string(buffer.string, source, buffer.used, content) == F_equal_to) {
+            if (values[i] && parameters->array[codes[i]].result & f_console_result_value_e || !values[i] && (parameters->array[codes[i]].result & f_console_result_found_e)) {
+              status = f_memory_array_increase_by(symbols[i].used + expands[i].used + 1, sizeof(f_char_t), (void **) &destination->string, &destination->used, &destination->size);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(symbols[i], destination);
+              if (F_status_is_error(status)) return status;
+
+              status = f_string_dynamic_append(expands[i], destination);
+              if (F_status_is_error(status)) return status;
+            }
+
+            break;
+          }
+        }
+
+        if (values[i]) {
+          f_string_static_t buffer = f_string_static_t_initialize;
+          buffer.used = options[i].used + controller_parameter_map_value_s.used;
+
+          f_char_t buffer_string[buffer.used];
+          buffer.string = buffer_string;
+
+          memcpy(buffer_string, options[i].string, sizeof(f_char_t) * options[i].used);
+          memcpy(buffer_string + options[i].used, controller_parameter_map_value_s.string, sizeof(f_char_t) * controller_parameter_map_value_s.used);
+
+          if (f_compare_dynamic_partial_string(buffer.string, source, buffer.used, content) == F_equal_to) {
+            if (parameters->array[codes[i]].result & f_console_result_value_e) {
+              const f_number_unsigned_t index = parameters->array[codes[i]].values.array[parameters->array[codes[i]].values.used - 1];
+
+              status = f_string_dynamic_append(argv[index], destination);
+              if (F_status_is_error(status)) return status;
+            }
+
+            break;
+          }
+        }
+      } // for
+    }
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_expand_iki_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/expand.h b/sources/c/main/rule/expand.h
new file mode 100644 (file)
index 0000000..4cb4d7b
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "expand" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_expand_h
+#define _controller_main_rule_expand_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Expand a single IKI variable into the buffer.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param action
+ *   The rule action data.
+ * @param process
+ *   The process information.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Errors (with error bit) from: controller_rule_expand_iki().
+ *
+ *   @see controller_rule_expand_iki()
+ */
+#ifndef _di_controller_rule_expand_
+  extern f_status_t controller_rule_expand(controller_global_t * const global, const controller_rule_action_t action, controller_instance_t * const instance);
+#endif // _di_controller_rule_expand_
+
+/**
+ * Expand a single IKI variable into the buffer.
+ *
+ * @param process
+ *   The process information.
+ * @param source
+ *   The source buffer holding the string referenced by the IKI data.
+ * @param vocabulary
+ *   The range representing the IKI variable vocabulary.
+ * @param content
+ *   The range representing the IKI variable content.
+ * @param destination
+ *   The buffer to expand into.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Errors (with error bit) from: f_environment_get().
+ *   Errors (with error bit) from: f_string_dynamic_append().
+ *
+ *   @see f_environment_get()
+ *   @see f_string_dynamic_append()
+ */
+#ifndef _di_controller_rule_expand_iki_
+  extern f_status_t controller_rule_expand_iki(controller_instance_t * const instance, const f_string_static_t source, const f_range_t vocabulary, const f_range_t content, f_string_dynamic_t * const destination);
+#endif // _di_controller_rule_expand_iki_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_expand_h
index 884baad7cab5ae01262223891158c9fdf17df603..e39a35c17300b0f20183c5b321b070920cd38b9b 100644 (file)
@@ -5,7 +5,9 @@ extern "C" {
 #endif
 
 #ifndef _di_controller_rule_instance_
-  f_status_t controller_rule_instance(const controller_global_t global, controller_instance_t * const instance) {
+  f_status_t controller_rule_instance(controller_global_t * const global, controller_instance_t * const instance) {
+
+    if (!global || !instance) return F_status_set_error(F_parameter);
 
     switch (instance->action) {
       case controller_rule_action_type_freeze_e:
@@ -20,16 +22,16 @@ extern "C" {
         break;
 
       default:
-        if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-          controller_lock_print(global.main->program.error.to, global.thread);
+        if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+          controller_lock_print(global->main->program.error.to, global->thread);
 
-          fl_print_format("%r%[%QUnsupported action type '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context);
-          fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_rule_action_type_name(instance->action), global.main->program.error.notable);
-          fl_print_format("%[' while attempting to execute rule.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s);
+          fl_print_format("%r%[%QUnsupported action type '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+          fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_rule_action_type_name(instance->action), global->main->program.error.notable);
+          fl_print_format("%[' while attempting to execute rule.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
 
-          controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+          controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-          controller_unlock_print_flush(global.main->program.error.to, global.thread);
+          controller_unlock_print_flush(global->main->program.error.to, global->thread);
         }
 
         return F_status_set_error(F_parameter);
@@ -49,7 +51,7 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
+      controller_rule_print_error(global->thread, &global->main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
 
       return status;
     }
@@ -57,7 +59,7 @@ extern "C" {
     status = f_string_dynamic_append(instance->rule.alias, &instance->cache.action.name_file);
 
     if (F_status_is_error(status)) {
-      controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
+      controller_rule_print_error(global->thread, &global->main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
 
       return status;
     }
@@ -69,7 +71,7 @@ extern "C" {
     }
 
     if (F_status_is_error(status)) {
-      controller_rule_print_error(global.thread, &global.main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
+      controller_rule_print_error(global->thread, &global->main->program.error, instance->cache.action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_true);
 
       return status;
     }
@@ -116,36 +118,36 @@ extern "C" {
 
       // i==0 is need, i==1 is want, i==2 is wish.
       // Loop through all dependencies: wait for depedency, execute dependency, fail due to missing required dependency, or skip unrequired missing dependencies.
-      for (i = 0; i < 3 && controller_thread_is_enabled_instance(instance, global.thread); ++i) {
+      for (i = 0; i < 3 && controller_thread_is_enabled_instance(instance, global->thread); ++i) {
 
-        for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_instance(instance, global.thread); ++j) {
+        for (j = 0; j < dynamics[i]->used && controller_thread_is_enabled_instance(instance, global->thread); ++j) {
 
           id_dependency = 0;
           dependency = 0;
           found = F_false;
 
-          status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.instance);
+          status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.instance);
 
           if (F_status_is_error(status_lock)) {
-            controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+            controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
           }
           else {
             status = controller_instance_prepare_instance_type(global, instance->type, instance->action, dynamics[i]->array[j], &id_dependency);
 
             if (F_status_is_error(status)) {
               if (F_status_set_fine(status) == F_lock) {
-                if (!controller_thread_is_enabled_instance_type(instance->type, global.thread)) {
+                if (!controller_thread_is_enabled_instance_type(instance->type, global->thread)) {
                   return F_status_set_error(F_interrupt);
                 }
               }
 
-              if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-                controller_lock_print(global.main->program.error.to, global.thread);
+              if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+                controller_lock_print(global->main->program.error.to, global->thread);
 
-                controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, dynamics[i]->array[j]);
-                controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_false);
+                controller_rule_item_print_error_rule_not_loaded(&global->main->program.error, dynamics[i]->array[j]);
+                controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_false);
 
-                controller_unlock_print_flush(global.main->program.error.to, global.thread);
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
               }
 
               return status;
@@ -157,37 +159,37 @@ extern "C" {
           if (status == F_true) {
             found = F_true;
 
-            dependency = global.thread->instances.array[id_dependency];
+            dependency = global->thread->instances.array[id_dependency];
 
-            status_lock = controller_main_lock_read_instance(instance, global.thread, &dependency->active);
+            status_lock = controller_main_lock_read_instance(instance, global->thread, &dependency->active);
 
             if (F_status_is_error(status_lock)) {
-              controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+              controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
               status = F_false;
               dependency = 0;
 
-              f_thread_unlock(&global.thread->lock.instance);
+              f_thread_unlock(&global->thread->lock.instance);
             }
             else {
-              f_thread_unlock(&global.thread->lock.instance);
+              f_thread_unlock(&global->thread->lock.instance);
 
-              status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule);
+              status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.rule);
 
               if (F_status_is_error(status_lock)) {
-                controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+                controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
                 status = F_false;
               }
               else {
-                status = controller_rule_find(dynamics[i]->array[j], global.setting->rules, &id_rule);
+                status = controller_rule_find(dynamics[i]->array[j], global->setting->rules, &id_rule);
 
-                f_thread_unlock(&global.thread->lock.rule);
+                f_thread_unlock(&global->thread->lock.rule);
               }
             }
           }
           else {
-            f_thread_unlock(&global.thread->lock.instance);
+            f_thread_unlock(&global->thread->lock.instance);
           }
 
           if (status != F_true) {
@@ -195,12 +197,12 @@ extern "C" {
             id_rule = 0;
 
             if (i == 0) {
-              controller_lock_print(global.main->program.error.to, global.thread);
+              controller_lock_print(global->main->program.error.to, global->thread);
 
-              controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], dynamics[i]->array[j], "is not found");
-              controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+              controller_rule_item_print_error_need_want_wish(&global->main->program.error, strings[i], dynamics[i]->array[j], "is not found");
+              controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-              controller_unlock_print_flush(global.main->program.error.to, global.thread);
+              controller_unlock_print_flush(global->main->program.error.to, global->thread);
 
               status = F_status_set_error(F_found_not);
 
@@ -213,22 +215,22 @@ extern "C" {
               }
             }
             else {
-              if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) {
-                controller_lock_print(global.main->program.warning.to, global.thread);
+              if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+                controller_lock_print(global->main->program.warning.to, global->thread);
 
-                controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], dynamics[i]->array[j], "is not found");
+                controller_rule_item_print_error_need_want_wish(&global->main->program.warning, strings[i], dynamics[i]->array[j], "is not found");
 
-                controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true);
+                controller_rule_print_rule_message_cache(&global->main->program.warning, instance->cache.action, F_true);
 
-                controller_unlock_print_flush(global.main->program.warning.to, global.thread);
+                controller_unlock_print_flush(global->main->program.warning.to, global->thread);
               }
             }
           }
           else if (found) {
-            status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule);
+            status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.rule);
 
             if (F_status_is_error(status_lock)) {
-              controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+              controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
               found = F_false;
               status = status_lock;
@@ -239,20 +241,20 @@ extern "C" {
 
             // The dependency may have write locks, which needs to be avoided, so copy the alias from the rule.
             f_string_static_t alias_other_buffer = f_string_static_t_initialize;
-            alias_other_buffer.used = global.setting->rules.array[id_rule].alias.used;
+            alias_other_buffer.used = global->setting->rules.array[id_rule].alias.used;
 
             f_char_t alias_other_buffer_string[alias_other_buffer.used + 1];
             alias_other_buffer.string = alias_other_buffer_string;
 
-            memcpy(alias_other_buffer_string, global.setting->rules.array[id_rule].alias.string, sizeof(f_char_t) * alias_other_buffer.used);
+            memcpy(alias_other_buffer_string, global->setting->rules.array[id_rule].alias.string, sizeof(f_char_t) * alias_other_buffer.used);
             alias_other_buffer_string[alias_other_buffer.used] = 0;
 
-            f_thread_unlock(&global.thread->lock.rule);
+            f_thread_unlock(&global->thread->lock.rule);
 
-            status_lock = controller_lock_read_instance(instance, global.thread, &dependency->lock);
+            status_lock = controller_lock_read_instance(instance, global->thread, &dependency->lock);
 
             if (F_status_is_error(status_lock)) {
-              controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+              controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
               status = status_lock;
             }
@@ -266,22 +268,22 @@ extern "C" {
               status = dependency->rule.status[instance->action];
             }
             else {
-              status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule);
+              status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.rule);
 
               if (F_status_is_error(status_lock)) {
-                controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+                controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
                 f_thread_unlock(&dependency->lock);
 
                 status = status_lock;
               }
-              else if (controller_rule_status_is_available(instance->action, global.setting->rules.array[id_rule])) {
-                f_thread_unlock(&global.thread->lock.rule);
+              else if (controller_rule_status_is_available(instance->action, global->setting->rules.array[id_rule])) {
+                f_thread_unlock(&global->thread->lock.rule);
                 f_thread_unlock(&dependency->lock);
 
                 options_instance = 0;
 
-                if (global.main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) {
+                if (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) {
                   options_instance |= controller_instance_option_simulate_d;
                 }
 
@@ -300,12 +302,12 @@ extern "C" {
 
                 if (F_status_is_error(status)) {
                   if (i == 0 || i == 1 || F_status_set_fine(status) == F_memory_not) {
-                    controller_lock_print(global.main->program.error.to, global.thread);
+                    controller_lock_print(global->main->program.error.to, global->thread);
 
-                    controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "failed during execution");
-                    controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+                    controller_rule_item_print_error_need_want_wish(&global->main->program.error, strings[i], alias_other_buffer, "failed during execution");
+                    controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-                    controller_unlock_print_flush(global.main->program.error.to, global.thread);
+                    controller_unlock_print_flush(global->main->program.error.to, global->thread);
 
                     if (!(dependency->options & controller_instance_option_simulate_d) || F_status_set_fine(status) == F_memory_not) {
                       f_thread_unlock(&dependency->active);
@@ -314,58 +316,58 @@ extern "C" {
                     }
                   }
                   else {
-                    if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) {
-                      controller_lock_print(global.main->program.warning.to, global.thread);
+                    if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+                      controller_lock_print(global->main->program.warning.to, global->thread);
 
-                      controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], alias_other_buffer, "failed during execution");
+                      controller_rule_item_print_error_need_want_wish(&global->main->program.warning, strings[i], alias_other_buffer, "failed during execution");
 
-                      controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true);
+                      controller_rule_print_rule_message_cache(&global->main->program.warning, instance->cache.action, F_true);
 
-                      controller_unlock_print_flush(global.main->program.warning.to, global.thread);
+                      controller_unlock_print_flush(global->main->program.warning.to, global->thread);
                     }
                   }
                 }
               }
               else {
-                status = global.setting->rules.array[id_rule].status[instance->action];
+                status = global->setting->rules.array[id_rule].status[instance->action];
 
-                f_thread_unlock(&global.thread->lock.rule);
+                f_thread_unlock(&global->thread->lock.rule);
                 f_thread_unlock(&dependency->lock);
               }
             }
 
-            if (!controller_thread_is_enabled_instance(instance, global.thread)) {
+            if (!controller_thread_is_enabled_instance(instance, global->thread)) {
               f_thread_unlock(&dependency->active);
 
               break;
             }
 
             if (F_status_is_error_not(status_lock)) {
-              status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule);
+              status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.rule);
 
               if (F_status_is_error(status_lock)) {
-                controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+                controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
               }
             }
 
             if (F_status_is_error(status_lock)) {
               if (F_status_is_error(status_lock)) {
-                controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "due to lock failure");
+                controller_rule_item_print_error_need_want_wish(&global->main->program.error, strings[i], alias_other_buffer, "due to lock failure");
               }
 
               status = status_lock;
             }
-            else if (controller_rule_status_is_error(instance->action, global.setting->rules.array[id_rule])) {
-              f_thread_unlock(&global.thread->lock.rule);
+            else if (controller_rule_status_is_error(instance->action, global->setting->rules.array[id_rule])) {
+              f_thread_unlock(&global->thread->lock.rule);
 
               if (i == 0 || i == 1) {
-                controller_lock_print(global.main->program.error.to, global.thread);
+                controller_lock_print(global->main->program.error.to, global->thread);
 
-                controller_rule_item_print_error_need_want_wish(&global.main->program.error, strings[i], alias_other_buffer, "is in a failed state");
+                controller_rule_item_print_error_need_want_wish(&global->main->program.error, strings[i], alias_other_buffer, "is in a failed state");
 
-                controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+                controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-                controller_unlock_print_flush(global.main->program.error.to, global.thread);
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
 
                 status = F_status_set_error(F_found_not);
 
@@ -376,19 +378,19 @@ extern "C" {
                 }
               }
               else {
-                if (global.main->program.warning.verbosity == f_console_verbosity_debug_e) {
-                  controller_lock_print(global.main->program.warning.to, global.thread);
+                if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+                  controller_lock_print(global->main->program.warning.to, global->thread);
 
-                  controller_rule_item_print_error_need_want_wish(&global.main->program.warning, strings[i], alias_other_buffer, "is in a failed state");
+                  controller_rule_item_print_error_need_want_wish(&global->main->program.warning, strings[i], alias_other_buffer, "is in a failed state");
 
-                  controller_rule_print_error_cache(&global.main->program.warning, instance->cache.action, F_true);
+                  controller_rule_print_rule_message_cache(&global->main->program.warning, instance->cache.action, F_true);
 
-                  controller_unlock_print_flush(global.main->program.warning.to, global.thread);
+                  controller_unlock_print_flush(global->main->program.warning.to, global->thread);
                 }
               }
             }
             else {
-              f_thread_unlock(&global.thread->lock.rule);
+              f_thread_unlock(&global->thread->lock.rule);
             }
           }
 
@@ -407,7 +409,7 @@ extern "C" {
       return status;
     }
 
-    if (!controller_thread_is_enabled_instance(instance, global.thread)) {
+    if (!controller_thread_is_enabled_instance(instance, global->thread)) {
       return F_status_set_error(F_interrupt);
     }
 
@@ -440,35 +442,35 @@ extern "C" {
         } // for
 
         if (missing) {
-          if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-            controller_lock_print(global.main->program.error.to, global.thread);
+          if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+            controller_lock_print(global->main->program.error.to, global->thread);
 
             if (instance->rule.items.used) {
-              fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context);
-              fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.name, global.main->program.error.notable);
-              fl_print_format("%[' has no '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_rule_action_type_name(instance->action), global.main->program.error.notable);
-              fl_print_format("%[' action to execute.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s);
+              fl_print_format("%r%[%QThe rule '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+              fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, instance->rule.name, global->main->program.error.notable);
+              fl_print_format("%[' has no '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_rule_action_type_name(instance->action), global->main->program.error.notable);
+              fl_print_format("%[' action to execute.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
             }
             else {
-              fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context);
-              fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.name, global.main->program.error.notable);
-              fl_print_format("%[ has no known '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format("%[%r %r%]", global.main->program.error.to, global.main->program.error.notable, controller_rule_s, controller_type_s, global.main->program.error.notable);
-              fl_print_format("%[' (such as '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_command_s, global.main->program.error.notable);
-              fl_print_format("%[', '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_service_s, global.main->program.error.notable);
-              fl_print_format("%[', '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_script_s, global.main->program.error.notable);
-              fl_print_format("%[', or '%]", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context);
-              fl_print_format(f_string_format_r_single_s.string, global.main->program.error.to, global.main->program.error.notable, controller_utility_s, global.main->program.error.notable);
-              fl_print_format("%[') to execute.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s);
+              fl_print_format("%r%[%QThe rule '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+              fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, instance->rule.name, global->main->program.error.notable);
+              fl_print_format("%[ has no known '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format("%[%r %r%]", global->main->program.error.to, global->main->program.error.notable, controller_rule_s, controller_type_s, global->main->program.error.notable);
+              fl_print_format("%[' (such as '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_command_s, global->main->program.error.notable);
+              fl_print_format("%[', '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_service_s, global->main->program.error.notable);
+              fl_print_format("%[', '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_script_s, global->main->program.error.notable);
+              fl_print_format("%[', or '%]", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+              fl_print_format(f_string_format_r_single_s.string, global->main->program.error.to, global->main->program.error.notable, controller_utility_s, global->main->program.error.notable);
+              fl_print_format("%[') to execute.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
             }
 
-            controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+            controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-            controller_unlock_print_flush(global.main->program.error.to, global.thread);
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
           }
 
           status = F_status_set_error(F_parameter);
@@ -483,7 +485,7 @@ extern "C" {
         }
 
         if (F_status_is_error(status)) {
-          controller_rule_item_print_error(global.thread, &global.main->program.error, instance->cache.action, F_true, F_status_set_fine(status));
+          controller_rule_item_print_error(global->thread, &global->main->program.error, instance->cache.action, F_true, F_status_set_fine(status));
         }
       }
     }
@@ -492,13 +494,13 @@ extern "C" {
 
     f_thread_unlock(&instance->lock);
 
-    status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+    status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
       if (F_status_set_fine(status) != F_interrupt) {
-        status = controller_lock_read_instance(instance, global.thread, &instance->lock);
+        status = controller_lock_read_instance(instance, global->thread, &instance->lock);
         if (F_status_is_error_not(status)) return status_lock;
       }
 
@@ -512,22 +514,22 @@ extern "C" {
       instance->rule.status[instance->action] = status;
     }
 
-    status_lock = controller_lock_write_instance(instance, global.thread, &global.thread->lock.rule);
+    status_lock = controller_lock_write_instance(instance, global->thread, &global->thread->lock.rule);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
       f_thread_unlock(&instance->lock);
 
-      status = controller_lock_read_instance(instance, global.thread, &instance->lock);
+      status = controller_lock_read_instance(instance, global->thread, &instance->lock);
       if (F_status_is_error_not(status)) return status_lock;
 
       return F_status_set_error(F_lock);
     }
 
     // Update the global rule status, which is stored separately from the rule status for this instance.
-    if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) {
-      controller_rule_t *rule = &global.setting->rules.array[id_rule];
+    if (controller_rule_find(instance->rule.alias, global->setting->rules, &id_rule) == F_true) {
+      controller_rule_t *rule = &global->setting->rules.array[id_rule];
 
       rule->status[instance->action] = instance->rule.status[instance->action];
 
@@ -546,13 +548,13 @@ extern "C" {
       } // for
     }
 
-    f_thread_unlock(&global.thread->lock.rule);
+    f_thread_unlock(&global->thread->lock.rule);
     f_thread_unlock(&instance->lock);
 
-    status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock);
+    status_lock = controller_lock_read_instance(instance, global->thread, &instance->lock);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
       return F_status_set_error(F_lock);
     }
@@ -562,9 +564,11 @@ extern "C" {
 #endif // _di_controller_rule_instance_
 
 #ifndef _di_controller_rule_instance_begin_
-  f_status_t controller_rule_instance_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache) {
+  f_status_t controller_rule_instance_begin(controller_global_t * const global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache) {
+
+    if (!global) return F_status_set_error(F_parameter);
 
-    if (!controller_thread_is_enabled_instance_type(type, global.thread)) {
+    if (!controller_thread_is_enabled_instance_type(type, global->thread)) {
       return F_status_set_error(F_interrupt);
     }
 
@@ -573,10 +577,10 @@ extern "C" {
 
     controller_instance_t *instance = 0;
 
-    status = controller_lock_read_instance_type(type, global.thread, &global.thread->lock.instance);
+    status = controller_lock_read_instance_type(type, global->thread, &global->thread->lock.instance);
 
     if (F_status_is_error(status)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status), F_true, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status), F_true, global->thread);
 
       return status;
     }
@@ -587,46 +591,46 @@ extern "C" {
       status = controller_instance_prepare(global, type != controller_instance_type_exit_e, action, alias_rule, &at);
 
       if (F_status_is_error(status)) {
-        f_thread_unlock(&global.thread->lock.instance);
+        f_thread_unlock(&global->thread->lock.instance);
 
-        if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-          controller_lock_print(global.main->program.error.to, global.thread);
+        if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+          controller_lock_print(global->main->program.error.to, global->thread);
 
-          controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, alias_rule);
-          controller_rule_print_error_cache(&global.main->program.error, cache.action, F_false);
+          controller_rule_item_print_error_rule_not_loaded(&global->main->program.error, alias_rule);
+          controller_rule_print_rule_message_cache(&global->main->program.error, cache.action, F_false);
 
-          controller_unlock_print_flush(global.main->program.error.to, global.thread);
+          controller_unlock_print_flush(global->main->program.error.to, global->thread);
         }
 
         return status;
       }
 
-      instance = global.thread->instances.array[at];
+      instance = global->thread->instances.array[at];
 
-      status = controller_lock_read_instance_type(type, global.thread, &instance->active);
+      status = controller_lock_read_instance_type(type, global->thread, &instance->active);
 
       if (F_status_is_error(status)) {
-        controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status), F_true, global.thread);
-        controller_rule_item_print_error(global.thread, &global.main->program.error, cache.action, F_false, F_status_set_fine(status));
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status), F_true, global->thread);
+        controller_rule_item_print_error(global->thread, &global->main->program.error, cache.action, F_false, F_status_set_fine(status));
 
-        f_thread_unlock(&global.thread->lock.instance);
+        f_thread_unlock(&global->thread->lock.instance);
 
         return status;
       }
 
-      status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+      status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
       if (F_status_is_error(status_lock)) {
-        controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
         f_thread_unlock(&instance->active);
-        f_thread_unlock(&global.thread->lock.instance);
+        f_thread_unlock(&global->thread->lock.instance);
 
         return status_lock;
       }
 
       // Once a write lock on the instance is achieved, it is safe to unlock the global instance read lock.
-      f_thread_unlock(&global.thread->lock.instance);
+      f_thread_unlock(&global->thread->lock.instance);
 
       // If the instance is already running, then there is nothing to do.
       if (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e) {
@@ -650,10 +654,10 @@ extern "C" {
 
     f_thread_unlock(&instance->lock);
 
-    status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+    status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
       f_thread_unlock(&instance->active);
 
@@ -691,9 +695,9 @@ extern "C" {
 
     instance->stack.used = 0;
 
-    instance->main_data = (void *) global.main;
-    instance->main_setting = (void *) global.setting;
-    instance->main_thread = (void *) global.thread;
+    instance->main_data = (void *) global->main;
+    instance->main_setting = (void *) global->setting;
+    instance->main_thread = (void *) global->thread;
 
     if (F_status_is_error_not(status) && stack.used) {
       if (instance->stack.size < stack.used) {
@@ -701,7 +705,7 @@ extern "C" {
       }
 
       if (F_status_is_error(status)) {
-        controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_memory_array_resize", F_true);
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_memory_array_resize", F_true);
       }
       else {
         for (f_number_unsigned_t i = 0; i < stack.used; ++i) {
@@ -723,7 +727,7 @@ extern "C" {
         status = f_string_dynamic_append(cache.action.name_item, &instance->cache.action.name_item);
       }
       else {
-        controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_append", F_true);
       }
     }
 
@@ -739,7 +743,7 @@ extern "C" {
         }
 
         if (F_status_is_error(status)) {
-          controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_thread_create", F_true);
+          controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_thread_create", F_true);
         }
       }
       else {
@@ -755,10 +759,10 @@ extern "C" {
 
     if (!action || F_status_is_error(status) && (instance->state == controller_instance_state_active_e || instance->state == controller_instance_state_busy_e)) {
       {
-        status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+        status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
         if (F_status_is_error(status_lock)) {
-          controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+          controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
           f_thread_unlock(&instance->active);
 
@@ -793,25 +797,27 @@ extern "C" {
 #ifndef _di_controller_rule_instance_do_
   f_status_t controller_rule_instance_do(const uint8_t options_force, controller_instance_t * const instance) {
 
+    if (!instance) return F_status_set_error(F_parameter);
+
     f_status_t status_lock = F_okay;
 
-    const controller_global_t global = macro_controller_global_t_initialize_1((controller_main_t *) instance->main_data, (controller_instance_t *) instance->main_setting, (controller_thread_t *) instance->main_thread);
+    controller_global_t * const global = macro_controller_global_t_initialize_1((controller_main_t *) instance->main_data, (controller_instance_t *) instance->main_setting, (controller_thread_t *) instance->main_thread);
 
     // The instance and active locks shall be held for the duration of this instanceing (aside from switching between read to/from write).
     if (options_force & controller_instance_option_asynchronous_d) {
-      status_lock = controller_lock_read_instance(instance, global.thread, &instance->active);
+      status_lock = controller_lock_read_instance(instance, global->thread, &instance->active);
 
       if (F_status_is_error(status_lock)) {
-        controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
         return status_lock;
       }
     }
 
-    status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock);
+    status_lock = controller_lock_read_instance(instance, global->thread, &instance->lock);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
       if (options_force & controller_instance_option_asynchronous_d) {
         f_thread_unlock(&instance->active);
@@ -826,10 +832,10 @@ extern "C" {
 
     const f_number_unsigned_t used_original_stack = instance->stack.used;
 
-    status_lock = controller_lock_read_instance(instance, global.thread, &global.thread->lock.rule);
+    status_lock = controller_lock_read_instance(instance, global->thread, &global->thread->lock.rule);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
       f_thread_unlock(&instance->lock);
 
@@ -840,15 +846,15 @@ extern "C" {
       return status_lock;
     }
 
-    if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) {
+    if (controller_rule_find(instance->rule.alias, global->setting->rules, &id_rule) == F_true) {
       f_thread_unlock(&instance->lock);
 
-      status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+      status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
       if (F_status_is_error(status_lock)) {
-        controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
-        f_thread_unlock(&global.thread->lock.rule);
+        f_thread_unlock(&global->thread->lock.rule);
 
         if (options_force & controller_instance_option_asynchronous_d) {
           f_thread_unlock(&instance->active);
@@ -859,16 +865,16 @@ extern "C" {
 
       controller_rule_delete(&instance->rule);
 
-      status = controller_rule_copy(global.setting->rules.array[id_rule], &instance->rule);
+      status = controller_rule_copy(global->setting->rules.array[id_rule], &instance->rule);
 
       f_thread_unlock(&instance->lock);
 
-      status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock);
+      status_lock = controller_lock_read_instance(instance, global->thread, &instance->lock);
 
       if (F_status_is_error(status_lock)) {
-        controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+        controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
-        f_thread_unlock(&global.thread->lock.rule);
+        f_thread_unlock(&global->thread->lock.rule);
 
         if (options_force & controller_instance_option_asynchronous_d) {
           f_thread_unlock(&instance->active);
@@ -877,10 +883,10 @@ extern "C" {
         return status_lock;
       }
 
-      f_thread_unlock(&global.thread->lock.rule);
+      f_thread_unlock(&global->thread->lock.rule);
 
       if (F_status_is_error(status)) {
-        controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "controller_rule_copy", F_true);
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "controller_rule_copy", F_true);
       }
       else if (!instance->action) {
 
@@ -894,19 +900,19 @@ extern "C" {
         return F_instance_not;
       }
       else {
-        for (f_number_unsigned_t i = 0; i < instance->stack.used && controller_thread_is_enabled_instance(instance, global.thread); ++i) {
+        for (f_number_unsigned_t i = 0; i < instance->stack.used && controller_thread_is_enabled_instance(instance, global->thread); ++i) {
 
           if (instance->stack.array[i] == id_rule) {
-            if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-              controller_lock_print(global.main->program.error.to, global.thread);
+            if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+              controller_lock_print(global->main->program.error.to, global->thread);
 
-              fl_print_format("%r%[%QThe rule '%]", global.main->program.error.to, f_string_eol_s, global.main->program.error.context, global.main->program.error.prefix, global.main->program.error.context);
-              fl_print_format(f_string_format_Q_single_s.string, global.main->program.error.to, global.main->program.error.notable, instance->rule.alias, global.main->program.error.notable);
-              fl_print_format("%[' is already on the execution dependency stack, this recursion is prohibited.%]%r", global.main->program.error.to, global.main->program.error.context, global.main->program.error.context, f_string_eol_s);
+              fl_print_format("%r%[%QThe rule '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+              fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, instance->rule.alias, global->main->program.error.notable);
+              fl_print_format("%[' is already on the execution dependency stack, this recursion is prohibited.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
 
-              controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_true);
+              controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_true);
 
-              controller_unlock_print_flush(global.main->program.error.to, global.thread);
+              controller_unlock_print_flush(global->main->program.error.to, global->thread);
             }
 
             // Never continue on circular recursion errors even in simulate mode.
@@ -916,7 +922,7 @@ extern "C" {
           }
         } // for
 
-        if (!controller_thread_is_enabled_instance(instance, global.thread)) {
+        if (!controller_thread_is_enabled_instance(instance, global->thread)) {
           f_thread_unlock(&instance->lock);
 
           if (options_force & controller_instance_option_asynchronous_d) {
@@ -930,15 +936,15 @@ extern "C" {
           status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_number_unsigned_t), (void **) &instance->stack.array, &instance->stack.used, &instance->stack.size);
 
           if (F_status_is_error(status)) {
-            controller_print_error(global.thread, &global.main->program.error, F_status_set_fine(status), "f_memory_array_increase", F_true);
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_memory_array_increase", F_true);
           }
           else {
             f_thread_unlock(&instance->lock);
 
-            status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+            status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
             if (F_status_is_error(status_lock)) {
-              controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+              controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
               if (options_force & controller_instance_option_asynchronous_d) {
                 f_thread_unlock(&instance->active);
@@ -951,10 +957,10 @@ extern "C" {
 
             f_thread_unlock(&instance->lock);
 
-            status_lock = controller_lock_read_instance(instance, global.thread, &instance->lock);
+            status_lock = controller_lock_read_instance(instance, global->thread, &instance->lock);
 
             if (F_status_is_error(status_lock)) {
-              controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_true, global.thread);
+              controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
 
               if (options_force & controller_instance_option_asynchronous_d) {
                 f_thread_unlock(&instance->active);
@@ -971,17 +977,17 @@ extern "C" {
       }
     }
     else {
-      f_thread_unlock(&global.thread->lock.rule);
+      f_thread_unlock(&global->thread->lock.rule);
 
       status = F_status_set_error(F_found_not);
 
-      if (global.main->program.error.verbosity > f_console_verbosity_quiet_e) {
-        controller_lock_print(global.main->program.error.to, global.thread);
+      if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+        controller_lock_print(global->main->program.error.to, global->thread);
 
-        controller_rule_item_print_error_rule_not_loaded(&global.main->program.error, instance->rule.alias);
-        controller_rule_print_error_cache(&global.main->program.error, instance->cache.action, F_false);
+        controller_rule_item_print_error_rule_not_loaded(&global->main->program.error, instance->rule.alias);
+        controller_rule_print_rule_message_cache(&global->main->program.error, instance->cache.action, F_false);
 
-        controller_unlock_print_flush(global.main->program.error.to, global.thread);
+        controller_unlock_print_flush(global->main->program.error.to, global->thread);
       }
     }
 
@@ -995,10 +1001,10 @@ extern "C" {
       return status;
     }
 
-    status_lock = controller_lock_write_instance(instance, global.thread, &global.thread->lock.rule);
+    status_lock = controller_lock_write_instance(instance, global->thread, &global->thread->lock.rule);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
       if (F_status_set_fine(status) != F_lock) {
         f_thread_unlock(&instance->lock);
@@ -1012,18 +1018,18 @@ extern "C" {
     }
 
     if (F_status_set_fine(status) == F_lock) {
-      if (controller_rule_find(instance->rule.alias, global.setting->rules, &id_rule) == F_true) {
-        global.setting->rules.array[id_rule].status[instance->action] = status;
+      if (controller_rule_find(instance->rule.alias, global->setting->rules, &id_rule) == F_true) {
+        global->setting->rules.array[id_rule].status[instance->action] = status;
       }
     }
 
-    f_thread_unlock(&global.thread->lock.rule);
+    f_thread_unlock(&global->thread->lock.rule);
 
     if (F_status_set_fine(status) != F_lock) {
       f_thread_unlock(&instance->lock);
     }
 
-    if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_instance(instance, global.thread)) {
+    if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_lock && !controller_thread_is_enabled_instance(instance, global->thread)) {
       if (options_force & controller_instance_option_asynchronous_d) {
         f_thread_unlock(&instance->active);
       }
@@ -1031,10 +1037,10 @@ extern "C" {
       return F_status_set_error(F_interrupt);
     }
 
-    status_lock = controller_lock_write_instance(instance, global.thread, &instance->lock);
+    status_lock = controller_lock_write_instance(instance, global->thread, &instance->lock);
 
     if (F_status_is_error(status_lock)) {
-      controller_lock_print_error_critical(&global.main->program.error, F_status_set_fine(status_lock), F_false, global.thread);
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
 
       if (options_force & controller_instance_option_asynchronous_d) {
         f_thread_unlock(&instance->active);
@@ -1063,7 +1069,7 @@ extern "C" {
       f_thread_unlock(&instance->active);
     }
 
-    if (controller_thread_is_enabled_instance(instance, global.thread)) {
+    if (controller_thread_is_enabled_instance(instance, global->thread)) {
       return status;
     }
 
index f77e8b60dd743ca4d74ded6af2a24e03e3a6c2e1..59b156ebf29a2b367d55c6ed6cbaa89f6be214a9 100644 (file)
@@ -30,6 +30,9 @@ extern "C" {
  *
  * @param global
  *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
  * @param instance
  *   The instance data for processing this rule.
  *
@@ -45,7 +48,7 @@ extern "C" {
  *   Errors (with error bit) from: controller_lock_write().
  */
 #ifndef _di_controller_rule_instance_
-  extern f_status_t controller_rule_instance(const controller_global_t global, controller_instance_t * const instance);
+  extern f_status_t controller_rule_instance(controller_global_t * const global, controller_instance_t * const instance);
 #endif // _di_controller_rule_instance_
 
 /**
@@ -53,6 +56,9 @@ extern "C" {
  *
  * @param global
  *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
  * @param options_force
  *   Force the given instance options, only supporting a subset of instance options.
  *
@@ -91,7 +97,7 @@ extern "C" {
  * @see f_thread_create()
  */
 #ifndef _di_controller_rule_instance_begin_
-  extern f_status_t controller_rule_instance_begin(const controller_global_t global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache);
+  extern f_status_t controller_rule_instance_begin(controller_global_t * const global, const uint8_t options_force, const f_string_static_t alias_rule, const uint8_t action, const uint8_t options, const uint8_t type, const f_number_unsigneds_t stack, const controller_cache_t cache);
 #endif // _di_controller_rule_instance_begin_
 
 /**
diff --git a/sources/c/main/rule/is.c b/sources/c/main/rule/is.c
new file mode 100644 (file)
index 0000000..02b6b51
--- /dev/null
@@ -0,0 +1,23 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_status_is_available_
+  f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule) {
+
+    return F_status_is_error_not(rule.status[0]) && rule.status[action] == F_known_not;
+  }
+#endif // _di_controller_rule_status_is_available_
+
+#ifndef _di_controller_rule_status_is_error_
+  f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule) {
+
+    return F_status_is_error(rule.status[0]) || F_status_is_error(rule.status[action]);
+  }
+#endif // _di_controller_rule_status_is_error_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/is.h b/sources/c/main/rule/is.h
new file mode 100644 (file)
index 0000000..67292da
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "is" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_is_h
+#define _controller_main_rule_is_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Check to see if the given Rule has status F_known_not for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_false.
+ *
+ * @param action
+ *   The Rule Action type.
+ * @param rule
+ *   The Rule.
+ *
+ * @return
+ *   F_true on available (status is F_known_not).
+ *   F_false on unavailable.
+ */
+#ifndef _di_controller_rule_status_is_available_
+  extern f_status_t controller_rule_status_is_available(const uint8_t action, const controller_rule_t rule);
+#endif // _di_controller_rule_status_is_available_
+
+/**
+ * Check to see if the given Rule has status is designated as an error for the given Rule Action.
+ *
+ * The global Rule status is checked for error and any errors on the global Rule status will result in F_true.
+ *
+ * @param action
+ *   The Rule Action type.
+ * @param rule
+ *   The Rule.
+ *
+ * @return
+ *   F_true if status represents an error.
+ *   F_false if status does not represent an error.
+ */
+#ifndef _di_controller_rule_status_is_error_
+  extern f_status_t controller_rule_status_is_error(const uint8_t action, const controller_rule_t rule);
+#endif // _di_controller_rule_status_is_error_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_is_h
diff --git a/sources/c/main/rule/item.c b/sources/c/main/rule/item.c
new file mode 100644 (file)
index 0000000..59bd3c6
--- /dev/null
@@ -0,0 +1,181 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_item_read_
+  f_status_t controller_rule_item_read(controller_global_t * const global, const bool is_normal, controller_cache_t * const cache, controller_rule_item_t * const item) {
+
+    if (!global || !cache || !item) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+    controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize_1(is_normal, global->thread);
+    f_state_t state = macro_f_state_t_initialize_1(controller_common_allocation_large_d, controller_common_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+    f_range_t range = macro_f_range_t_initialize_2(cache->buffer_item.used);
+    f_number_unsigned_t last = 0;
+
+    uint8_t type = 0;
+    uint8_t method = 0;
+    bool multiple = F_false;
+
+    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) {
+
+      fl_fss_extended_list_object_read(cache->buffer_item, &range, &cache->range_action, &cache->delimits, &state);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "fl_fss_extended_list_object_read", F_true);
+
+        break;
+      }
+
+      if (status == F_fss_found_object) {
+        multiple = F_true;
+      }
+      else {
+        range.start = last;
+        multiple = F_false;
+        cache->delimits.used = 0;
+
+        // The current line is not an Extended List object, so the next possibility is a Basic List (and Extended List, both use the same Object structure).
+        fl_fss_extended_object_read(cache->buffer_item, &range, &cache->range_action, 0, &cache->delimits, &state);
+
+        if (F_status_is_error(status)) {
+          controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "fl_fss_extended_object_read", F_true);
+
+          break;
+        }
+
+        // Nothing of importance here, so continue onto the next line.
+        if (status != F_fss_found_object) continue;
+      }
+
+      f_fss_apply_delimit(cache->delimits, &cache->buffer_item, &state);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_fss_apply_delimit", F_true);
+
+        break;
+      }
+
+      f_fss_count_lines(cache->buffer_item, cache->range_action.start, &cache->action.line_action, &state);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+
+        break;
+      }
+
+      cache->action.line_action += ++item->line;
+      cache->action.name_action.used = 0;
+
+      status = f_rip_dynamic_partial_nulless(cache->buffer_item, cache->range_action, &cache->action.name_action);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_rip_dynamic_partial_nulless", F_true);
+
+        break;
+      }
+
+      if (f_compare_dynamic(controller_freeze_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_freeze_e;
+      }
+      else if (f_compare_dynamic(controller_group_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_group_e;
+      }
+      else if (f_compare_dynamic(controller_kill_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_kill_e;
+      }
+      else if (f_compare_dynamic(controller_pause_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_pause_e;
+      }
+      else if (f_compare_dynamic(controller_pid_file_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_pid_file_e;
+      }
+      else if (f_compare_dynamic(controller_reload_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_reload_e;
+      }
+      else if (f_compare_dynamic(controller_rerun_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_rerun_e;
+      }
+      else if (f_compare_dynamic(controller_restart_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_restart_e;
+      }
+      else if (f_compare_dynamic(controller_resume_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_resume_e;
+      }
+      else if (f_compare_dynamic(controller_start_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_start_e;
+      }
+      else if (f_compare_dynamic(controller_stop_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_stop_e;
+      }
+      else if (f_compare_dynamic(controller_thaw_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_thaw_e;
+      }
+      else if (f_compare_dynamic(controller_user_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_user_e;
+      }
+      else if (f_compare_dynamic(controller_with_s, cache->action.name_action) == F_equal_to) {
+        type = controller_rule_action_type_with_e;
+      }
+      else {
+        if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+          controller_lock_print(global->main->program.warning.to, global->thread);
+
+          fl_print_format("%r%[%QUnknown rule item action '%]", global->main->program.warning.to, f_string_eol_s, global->main->program.warning.context, global->main->program.warning.prefix, global->main->program.warning.context);
+          fl_print_format(f_string_format_Q_single_s.string, global->main->program.warning.to, global->main->program.warning.notable, cache->action.name_action, global->main->program.warning.notable);
+          fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.warning.to, global->main->program.warning.context, global->main->program.warning.context, f_string_eol_s);
+
+          controller_rule_print_rule_message_cache(&global->main->program.warning, cache->action, F_true);
+
+          controller_unlock_print_flush(global->main->program.warning.to, global->thread);
+        }
+
+        continue;
+      }
+
+      if (multiple) {
+        if (type == controller_rule_action_type_group_e || type == controller_rule_action_type_pid_file_e || type == controller_rule_action_type_user_e) {
+
+          if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+            controller_lock_print(global->main->program.error.to, global->thread);
+
+            fl_print_format("%r%[%QFSS Extended List is not allowed for the rule item action '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+            fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->action.name_action, global->main->program.error.notable);
+            fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
+          }
+
+          status = F_status_set_error(F_support_not);
+          break;
+        }
+
+        method = controller_rule_action_method_extended_list_e;
+      }
+      else {
+        method = controller_rule_action_method_extended_e;
+      }
+
+      status = controller_rule_actions_increase_by(controller_common_allocation_small_d, &item->actions);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "controller_rule_actions_increase_by", F_true);
+
+        break;
+      }
+
+      status = controller_rule_action_read(global, is_normal, type, method, cache, item, &item->actions, &range);
+      if (F_status_is_error(status)) break;
+    } // for
+
+    return status;
+  }
+#endif // _di_controller_rule_item_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/item.h b/sources/c/main/rule/item.h
new file mode 100644 (file)
index 0000000..284d87c
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "item" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_item_h
+#define _controller_main_rule_item_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * 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.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param is_normal
+ *   If TRUE, then process as if this operates during a normal operation (entry and control).
+ *   If FALSE, then process as if this operates during a an exit operation.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param item
+ *   The processed item.
+ *
+ * @return
+ *   F_okay on success.
+ *   F_valid_not (with error bit) on invalid data.
+ *
+ *   Errors (with error bit) from: f_fss_count_lines().
+ *   Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ *
+ * @see controller_rule_action_read()
+ * @see f_fss_count_lines()
+ * @see f_string_dynamic_partial_append_nulless()
+ */
+#ifndef _di_controller_rule_item_read_
+  extern f_status_t controller_rule_item_read(controller_global_t * const global, const bool is_normal, controller_cache_t * const cache, controller_rule_item_t * const item);
+#endif // _di_controller_rule_item_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_item_h
diff --git a/sources/c/main/rule/parameter.c b/sources/c/main/rule/parameter.c
new file mode 100644 (file)
index 0000000..e706347
--- /dev/null
@@ -0,0 +1,125 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_parameters_read_
+  f_status_t controller_rule_parameters_read(controller_global_t * const global, const f_string_static_t buffer, f_range_t * const object, f_ranges_t * const content, controller_rule_action_t * const action, f_state_t * const state) {
+
+    if (!global || !object || !content || !action || !state) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    action->parameters.used = 0;
+    action->ikis.used = 0;
+
+    if (object && object->start <= object->stop) {
+      if (content) {
+        status = f_memory_array_increase_by(content->used + 1, sizeof(f_string_dynamic_t), (void **) &action->parameters.array, &action->parameters.used, &action->parameters.size);
+      }
+      else {
+        status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &action->parameters.array, &action->parameters.used, &action->parameters.size);
+      }
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), content ? "f_memory_array_increase_by" : "f_memory_array_increase", F_true);
+
+        return status;
+      }
+
+      if (content) {
+        status = f_memory_array_increase_by(content->used + 1, sizeof(f_iki_data_t), (void **) &action->ikis.array, &action->ikis.used, &action->ikis.size);
+      }
+      else {
+        status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_iki_data_t), (void **) &action->ikis.array, &action->ikis.used, &action->ikis.size);
+      }
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), content ? "f_memory_array_increase_by" : "f_memory_array_increase", F_true);
+
+        return status;
+      }
+
+      action->parameters.array[action->parameters.used].used = 0;
+      action->ikis.array[action->ikis.used].content.used = 0;
+      action->ikis.array[action->ikis.used].delimits.used = 0;
+      action->ikis.array[action->ikis.used].variable.used = 0;
+      action->ikis.array[action->ikis.used].vocabulary.used = 0;
+
+      status = f_string_dynamic_partial_append_nulless(buffer, *object, &action->parameters.array[0]);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+        return status;
+      }
+
+      ++action->parameters.used;
+      ++action->ikis.used;
+    }
+
+    if (content && content->used) {
+      status = f_memory_array_increase_by(content->used, sizeof(f_string_dynamic_t), (void **) &action->parameters.array, &action->parameters.used, &action->parameters.size);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_memory_array_increase_by", F_true);
+
+        return status;
+      }
+
+      status = f_memory_array_increase_by(content->used, sizeof(f_iki_data_t), (void **) &action->ikis.array, &action->ikis.used, &action->ikis.size);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_memory_array_increase_by", F_true);
+
+        return status;
+      }
+
+      f_range_t range = f_range_t_initialize;
+
+      for (f_number_unsigned_t i = 0; i < content->used; ++i) {
+
+        if (content->array[i].start > content->array[i].stop) continue;
+
+        action->parameters.array[action->parameters.used].used = 0;
+        action->ikis.array[action->ikis.used].content.used = 0;
+        action->ikis.array[action->ikis.used].delimits.used = 0;
+        action->ikis.array[action->ikis.used].variable.used = 0;
+        action->ikis.array[action->ikis.used].vocabulary.used = 0;
+
+        status = f_string_dynamic_partial_append_nulless(buffer, content->array[i], &action->parameters.array[action->parameters.used]);
+
+        if (F_status_is_error(status)) {
+          controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true);
+
+          return status;
+        }
+
+        if (action->parameters.array[action->parameters.used].used) {
+          range.start = 0;
+          range.stop = action->parameters.array[action->parameters.used].used - 1;
+
+          fl_iki_read(&action->parameters.array[action->parameters.used], &range, &action->ikis.array[action->ikis.used], state);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "fl_iki_read", F_true);
+
+            action->parameters.array[action->parameters.used].used = 0;
+
+            return status;
+          }
+        }
+
+        ++action->parameters.used;
+        ++action->ikis.used;
+      } // for
+    }
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_parameters_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/parameter.h b/sources/c/main/rule/parameter.h
new file mode 100644 (file)
index 0000000..8be0998
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "read" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_read_h
+#define _controller_main_rule_read_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Read the parameters for some rule action.
+ *
+ * The object and content ranges are merged together (in that order) as the action parameters.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param buffer
+ *   The buffer containing the content.
+ * @param object
+ *   (optional) The range representing where the object is found within the buffer.
+ *   Set pointer address to 0 to disable.
+ * @param content
+ *   (optional) The ranges representing where the content is found within the buffer.
+ *   Set pointer address to 0 to disable.
+ * @param action
+ *   The rule action.
+ * @param state
+ *   The state data for passing to fl_iki_read().
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Errors (with error bit) from: f_fss_count_lines().
+ *   Errors (with error bit) from: f_memory_array_increase().
+ *   Errors (with error bit) from: f_memory_array_increase_by().
+ *   Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ *   Errors (with error bit) from: f_memory_array_increase().
+ *   Errors (with error bit) from: fl_iki_read().
+ *
+ * @see f_fss_count_lines()
+ * @see f_memory_array_increase()
+ * @see f_memory_array_increase_by()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see f_memory_array_increase()
+ * @see f_memory_array_increase_by()
+ * @see fl_iki_read()
+ */
+#ifndef _di_controller_rule_parameters_read_
+  extern f_status_t controller_rule_parameters_read(controller_global_t * const global, const f_string_static_t buffer, f_range_t * const object, f_ranges_t * const content, controller_rule_action_t * const action, f_state_t * const state);
+#endif // _di_controller_rule_parameters_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_read_h
diff --git a/sources/c/main/rule/read.c b/sources/c/main/rule/read.c
new file mode 100644 (file)
index 0000000..c90006b
--- /dev/null
@@ -0,0 +1,340 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_read_
+  f_status_t controller_rule_read(controller_global_t * const global, const bool is_normal, const f_string_static_t alias, controller_cache_t * const cache, controller_entry_t * const entry, controller_rule_t * const rule) {
+
+    if (!global || !cache || !entry || !rule) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+
+    bool for_item = F_true;
+
+    rule->timeout_kill = entry->timeout_kill ? entry->timeout_kill : 0;
+    rule->timeout_start = entry->timeout_start ? entry->timeout_start : 0;
+    rule->timeout_stop = entry->timeout_stop ? entry->timeout_stop : 0;
+
+    rule->has = 0;
+    rule->group = 0;
+    rule->user = 0;
+    rule->nice = 0;
+
+    macro_f_time_simple_t_clear(rule->timestamp);
+
+    rule->alias.used = 0;
+    rule->engine.used = 0;
+    rule->engine_arguments.used = 0;
+    rule->name.used = 0;
+    rule->path.used = 0;
+
+    rule->define.used = 0;
+    rule->parameter.used = 0;
+    rule->environment.used = 0;
+
+    rule->affinity.used = 0;
+
+    if (rule->capability) {
+      f_capability_delete(&rule->capability);
+      rule->capability = 0;
+    }
+
+    rule->cgroup.as_new = F_false;
+    rule->cgroup.path.used = 0;
+    rule->cgroup.groups.used = 0;
+
+    macro_f_control_group_t_clear(rule->cgroup);
+
+    rule->groups.used = 0;
+    rule->limits.used = 0;
+
+    rule->scheduler.policy = 0;
+    rule->scheduler.priority = 0;
+
+    rule->ons.used = 0;
+    rule->items.used = 0;
+
+    cache->action.line_item = 0;
+    cache->action.line_action = 0;
+
+    cache->range_action.start = 1;
+    cache->range_action.stop = 0;
+
+    cache->comments.used = 0;
+    cache->delimits.used = 0;
+
+    cache->buffer_file.used = 0;
+    cache->buffer_item.used = 0;
+    cache->buffer_path.used = 0;
+
+    cache->content_items.used = 0;
+    cache->object_items.used = 0;
+
+    cache->action.name_action.used = 0;
+    cache->action.name_file.used = 0;
+    cache->action.name_item.used = 0;
+
+    {
+      f_number_unsigned_t i = 0;
+      f_number_unsigned_t j = 0;
+      f_number_unsigned_t k = 0;
+      f_number_unsigned_t l = 0;
+
+      for (i = 0; i < rule->cgroup.groups.size; ++i) {
+        rule->cgroup.groups.array[i].used = 0;
+      } // for
+
+      for (; i < controller_rule_action_type__enum_size_e; ++i) {
+        rule->status[i] = F_known_not;
+      } // for
+
+      for (i = 0; i < cache->content_items.size; ++i) {
+        cache->content_items.array[i].used = 0;
+      } // for
+
+      for (i = 0; i < rule->engine_arguments.size; ++i) {
+        rule->engine_arguments.array[i].used = 0;
+      } // for
+
+      for (i = 0; i < rule->ons.size; ++i) {
+        rule->ons.array[i].need.used = 0;
+        rule->ons.array[i].want.used = 0;
+        rule->ons.array[i].wish.used = 0;
+      } // for
+
+      for (i = 0; i < rule->items.size; ++i) {
+
+        rule->items.array[i].type = 0;
+        rule->items.array[i].with = 0;
+        rule->items.array[i].line = 0;
+        rule->items.array[i].pid_file.used = 0;
+        rule->items.array[i].actions.used = 0;
+
+        for (j = 0; j < controller_rule_action_execute_type__enum_size_e; ++j) {
+
+          rule->items.array[i].reruns[j].is = 0;
+
+          macro_controller_rule_rerun_item_initialize(rule->items.array[i].reruns[j].failure);
+          macro_controller_rule_rerun_item_initialize(rule->items.array[i].reruns[j].success);
+        } // for
+
+        for (j = 0; j < rule->items.array[i].actions.size; ++j) {
+
+          rule->items.array[i].actions.array[j].type = 0;
+          rule->items.array[i].actions.array[j].line = 0;
+          rule->items.array[i].actions.array[j].status = F_okay;
+          rule->items.array[i].actions.array[j].parameters.used = 0;
+          rule->items.array[i].actions.array[j].ikis.used = 0;
+
+          for (k = 0; k < rule->items.array[i].actions.array[j].parameters.size; ++k) {
+            rule->items.array[i].actions.array[j].parameters.array[k].used = 0;
+          } // for
+
+          for (k = 0; k < rule->items.array[i].actions.array[j].ikis.size; ++k) {
+
+            rule->items.array[i].actions.array[j].ikis.array[k].content.used = 0;
+            rule->items.array[i].actions.array[j].ikis.array[k].delimits.used = 0;
+            rule->items.array[i].actions.array[j].ikis.array[k].variable.used = 0;
+            rule->items.array[i].actions.array[j].ikis.array[k].vocabulary.used = 0;
+
+            for (l = 0; l < rule->items.array[i].actions.array[j].ikis.array[k].content.size; ++l) {
+
+              rule->items.array[i].actions.array[j].ikis.array[k].content.array[l].start = 1;
+              rule->items.array[i].actions.array[j].ikis.array[k].content.array[l].stop = 0;
+            } // for
+
+            for (l = 0; l < rule->items.array[i].actions.array[j].ikis.array[k].delimits.size; ++l) {
+              rule->items.array[i].actions.array[j].ikis.array[k].delimits.array[l] = 0;
+            } // for
+
+            for (l = 0; l < rule->items.array[i].actions.array[j].ikis.array[k].variable.size; ++l) {
+
+              rule->items.array[i].actions.array[j].ikis.array[k].variable.array[l].start = 1;
+              rule->items.array[i].actions.array[j].ikis.array[k].variable.array[l].stop = 0;
+            } // for
+
+            for (l = 0; l < rule->items.array[i].actions.array[j].ikis.array[k].vocabulary.size; ++l) {
+
+              rule->items.array[i].actions.array[j].ikis.array[k].vocabulary.array[l].start = 1;
+              rule->items.array[i].actions.array[j].ikis.array[k].vocabulary.array[l].stop = 0;
+            } // for
+          } // for
+        } // for
+      } // for
+    }
+
+    status = f_string_dynamic_append_nulless(alias, &rule->alias);
+
+    if (F_status_is_error(status)) {
+      controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_append_nulless", F_true);
+    }
+    else {
+      status = controller_file_load(global, F_true, controller_rules_s, rule->alias, controller_rule_s, cache);
+    }
+
+    if (F_status_is_error_not(status)) {
+      rule->timestamp = cache->timestamp;
+
+      if (cache->buffer_file.used) {
+        controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize_1(is_normal, global->thread);
+        f_state_t state = macro_f_state_t_initialize_1(controller_common_allocation_large_d, controller_common_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+        f_range_t range = macro_f_range_t_initialize_2(cache->buffer_file.used);
+
+        fll_fss_basic_list_read(cache->buffer_file, &range, &cache->object_items, &cache->content_items, &cache->delimits, 0, &cache->comments, &state);
+
+        if (F_status_is_error(status)) {
+          controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "fll_fss_basic_list_read", F_true);
+        }
+        else {
+          f_fss_apply_delimit(cache->delimits, &cache->buffer_file, &state);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_fss_apply_delimit", F_true);
+          }
+        }
+      }
+    }
+
+    if (F_status_is_error_not(status) && cache->object_items.used) {
+      status = controller_rule_items_increase_by(cache->object_items.used, &rule->items);
+
+      if (F_status_is_error(status)) {
+        controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "controller_rule_items_increase_by", F_true);
+      }
+      else {
+        f_number_unsigned_t i = 0;
+        f_number_unsigned_t j = 0;
+        f_state_t state = f_state_t_initialize;
+
+        for (; i < cache->object_items.used; ++i) {
+
+          cache->action.line_item = 0;
+          cache->action.line_action = 0;
+
+          cache->range_action.start = 1;
+          cache->range_action.stop = 0;
+
+          cache->comments.used = 0;
+          cache->delimits.used = 0;
+
+          cache->content_action.used = 0;
+
+          for (j = 0; j < cache->content_actions.used; ++j) {
+            cache->content_actions.array[j].used = 0;
+          } // for
+
+          cache->content_actions.used = 0;
+          cache->object_actions.used = 0;
+
+          cache->buffer_item.used = 0;
+          cache->buffer_path.used = 0;
+
+          cache->action.name_action.used = 0;
+          cache->action.name_item.used = 0;
+
+          for_item = F_true;
+
+          f_fss_count_lines(cache->buffer_file, cache->object_items.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_fss_count_lines", F_true);
+
+            break;
+          }
+
+          rule->items.array[rule->items.used].line = ++cache->action.line_item;
+
+          status = f_rip_dynamic_partial_nulless(cache->buffer_file, cache->object_items.array[i], &cache->action.name_item);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_rip_dynamic_partial_nulless", F_true);
+
+            break;
+          }
+
+          if (f_compare_dynamic(controller_settings_s, cache->action.name_item) == F_equal_to) {
+            rule->items.array[rule->items.used].type = 0;
+          }
+          else if (f_compare_dynamic(controller_command_s, cache->action.name_item) == F_equal_to) {
+            rule->items.array[rule->items.used].type = controller_rule_item_type_command_e;
+          }
+          else if (f_compare_dynamic(controller_script_s, cache->action.name_item) == F_equal_to) {
+            rule->items.array[rule->items.used].type = controller_rule_item_type_script_e;
+          }
+          else if (f_compare_dynamic(controller_service_s, cache->action.name_item) == F_equal_to) {
+            rule->items.array[rule->items.used].type = controller_rule_item_type_service_e;
+          }
+          else if (f_compare_dynamic(controller_utility_s, cache->action.name_item) == F_equal_to) {
+            rule->items.array[rule->items.used].type = controller_rule_item_type_utility_e;
+          }
+          else {
+            if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+              controller_lock_print(global->main->program.warning.to, global->thread);
+
+              fl_print_format("%r%[%QUnknown rule item '%]", global->main->program.warning.to, f_string_eol_s, global->main->program.warning.context, global->main->program.warning.prefix, global->main->program.warning.context);
+              fl_print_format(f_string_format_Q_single_s.string, global->main->program.warning.to, global->main->program.warning.notable, cache->action.name_item, global->main->program.warning.notable);
+              fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.warning.to, global->main->program.warning.context, global->main->program.warning.context, f_string_eol_s);
+
+              controller_rule_print_rule_message_cache(&global->main->program.warning, cache->action, F_true);
+
+              controller_unlock_print_flush(global->main->program.warning.to, global->thread);
+            }
+
+            continue;
+          }
+
+          status = f_string_dynamic_partial_append(cache->buffer_file, cache->content_items.array[i].array[0], &cache->buffer_item);
+
+          if (F_status_is_error(status)) {
+            controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_string_dynamic_partial_append", F_true);
+
+            break;
+          }
+
+          if (rule->items.array[rule->items.used].type) {
+
+            // Initialize the item with settings.
+            rule->items.array[rule->items.used].with = 0;
+
+            if (entry->session == controller_entry_session_new_e) {
+              rule->items.array[rule->items.used].with |= controller_with_session_new_d;
+            }
+            else {
+              rule->items.array[rule->items.used].with |= controller_with_session_same_d;
+            }
+
+            status = controller_rule_item_read(global, is_normal, cache, &rule->items.array[rule->items.used]);
+            if (F_status_is_error(status)) break;
+
+            ++rule->items.used;
+          }
+          else {
+            for_item = F_false;
+
+            status = controller_rule_setting_read(global, is_normal, *global->setting, cache, rule);
+
+            if (F_status_is_error(status)) {
+              if (F_status_set_fine(status) == F_memory_not) break;
+            }
+          }
+        } // for
+      }
+    }
+
+    if (F_status_is_error(status)) {
+      controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, for_item, F_status_set_fine(status));
+
+      rule->status[0] = controller_status_simplify_error(F_status_set_fine(status));
+
+      return rule->status[0];
+    }
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/read.h b/sources/c/main/rule/read.h
new file mode 100644 (file)
index 0000000..390b495
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "read" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_read_h
+#define _controller_main_rule_read_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Read the rule file, extracting all valid items.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param is_normal
+ *   If TRUE, then process as if this operates during a normal operation (entry and control).
+ *   If FALSE, then process as if this operates during a an exit operation.
+ * @param alias
+ *   The string identifying the rule.
+ *   This is constructed from the path parts to the file without the file extension and without the settings directory prefix.
+ *   "/etc/controller/rules/example/my.rule" would have a rule id of "example/my".
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param entry
+ *   The entry containing the rule being read.
+ * @param rule
+ *   The processed rule.
+ *   The rule status will be updated by this function.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   Simplified status (with error bit) from controller_status_simplify_error() on failure.
+ *
+ * @see controller_rule_items_increase_by().
+ * @see controller_rule_item_read().
+ * @see f_fss_count_lines().
+ * @see f_fss_apply_delimit().
+ * @see f_string_dynamic_partial_append().
+ * @see f_string_dynamic_partial_append_nulless().
+ * @see fll_fss_basic_list_read().
+ */
+#ifndef _di_controller_rule_read_
+  extern f_status_t controller_rule_read(controller_global_t * const global, const bool is_normal, const f_string_static_t alias, controller_cache_t * const cache, controller_entry_t * const entry, controller_rule_t * const rule);
+#endif // _di_controller_rule_read_
+
+/**
+ * Process a number from a rule file, incrementing index as necessary.
+ *
+ * This prints error messages as necessary.
+ *
+ * This is intended to be called by controller_rule_action_read().
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param name
+ *   The name representing the value whose number is being processed.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param index
+ *   The position within the content action array for some rule to process.
+ * @param number
+ *   The processed number will be saved here.
+ *
+ * @return
+ *   F_okay on success.
+ *
+ *   F_valid_not (with error bit) on failure due to invalid value.
+ *
+ *   Errors (with error bit) from: fl_conversion_dynamic_partial_to_signed_detect().
+ *
+ * @see controller_rule_action_read()
+ * @see fl_conversion_dynamic_partial_to_signed_detect()
+ */
+#ifndef _di_controller_rule_action_read_rerun_number_
+  extern f_status_t controller_rule_action_read_rerun_number(controller_global_t * const global, const f_string_t name, controller_cache_t * const cache, f_number_unsigned_t * const index, f_number_unsigned_t * const number);
+#endif // _di_controller_rule_action_read_rerun_number_
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_read_h
diff --git a/sources/c/main/rule/setting.c b/sources/c/main/rule/setting.c
new file mode 100644 (file)
index 0000000..7802ce3
--- /dev/null
@@ -0,0 +1,1752 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_setting_read_
+  f_status_t controller_rule_setting_read(controller_global_t * const global, const bool is_normal, controller_cache_t * const cache, controller_rule_t * const rule) {
+
+    if (!global || !cache || !rule) return F_status_set_error(F_parameter);
+
+    f_status_t status = F_okay;
+    f_status_t status_return = F_okay;
+
+    f_range_t range = macro_f_range_t_initialize_2(cache->buffer_item.used);
+    f_range_t range2 = f_range_t_initialize;
+
+    controller_state_interrupt_t custom = macro_controller_state_interrupt_t_initialize_1(is_normal, global->thread);
+    f_state_t state = macro_f_state_t_initialize_1(controller_common_allocation_large_d, controller_common_allocation_small_d, F_okay, 0, 0, 0, &controller_thread_signal_state_fss, 0, (void *) &custom, 0);
+
+    fll_fss_extended_read(cache->buffer_item, &range, &cache->object_actions, &cache->content_actions, 0, 0, &cache->delimits, 0, &state);
+
+    if (F_status_is_error(status)) {
+      controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "fll_fss_extended_read", F_true, F_false);
+
+      return status;
+    }
+
+    f_string_dynamic_t *setting_value = 0;
+    f_string_dynamics_t *setting_values = 0;
+    f_string_maps_t *setting_maps = 0;
+
+    f_number_unsigned_t i = 0;
+    f_number_unsigned_t j = 0;
+    uint8_t type = 0;
+    uint8_t action = 0;
+    bool empty_disallow = F_true;
+
+    // Save the current name item and line number to restore on return.
+    const f_number_unsigned_t line_item = cache->action.line_item;
+    const f_number_unsigned_t length_name_item = cache->action.name_item.used;
+
+    f_char_t name_item[length_name_item];
+    name_item[length_name_item] = 0;
+
+    memcpy(name_item, cache->action.name_item.string, sizeof(f_char_t) * length_name_item);
+
+    for (; i < cache->content_actions.used; ++i, type = 0) {
+
+      // The name_action is used to store all setting values for a single setting.
+      cache->action.name_action.used = 0;
+
+      // The name_item is used to store the setting name.
+      cache->action.name_item.used = 0;
+
+      status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->object_actions.array[i], &cache->action.name_item);
+
+      if (F_status_is_error(status)) {
+        controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+        if (F_status_set_fine(status) == F_memory_not) {
+          status_return = status;
+
+          break;
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = status;
+        }
+
+        // Get the current line number within the settings item.
+        cache->action.line_item = line_item;
+        f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+        cache->action.line_action = ++cache->action.line_item;
+
+        controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+        continue;
+      }
+
+      empty_disallow = F_true;
+
+      if (f_compare_dynamic(controller_affinity_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_affinity_e;
+      }
+      else if (f_compare_dynamic(controller_capability_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_capability_e;
+      }
+      else if (f_compare_dynamic(controller_cgroup_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_cgroup_e;
+      }
+      else if (f_compare_dynamic(controller_define_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_define_e;
+      }
+      else if (f_compare_dynamic(controller_engine_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_engine_e;
+      }
+      else if (f_compare_dynamic(controller_environment_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_environment_e;
+        empty_disallow = F_false;
+      }
+      else if (f_compare_dynamic(controller_group_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_group_e;
+      }
+      else if (f_compare_dynamic(controller_limit_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_limit_e;
+      }
+      else if (f_compare_dynamic(controller_name_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_name_e;
+      }
+      else if (f_compare_dynamic(controller_nice_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_nice_e;
+      }
+      else if (f_compare_dynamic(controller_on_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_on_e;
+      }
+      else if (f_compare_dynamic(controller_parameter_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_parameter_e;
+      }
+      else if (f_compare_dynamic(controller_path_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_path_e;
+      }
+      else if (f_compare_dynamic(controller_scheduler_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_scheduler_e;
+      }
+      else if (f_compare_dynamic(controller_timeout_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_timeout_e;
+      }
+      else if (f_compare_dynamic(controller_user_s, cache->action.name_item) == F_equal_to) {
+        type = controller_rule_setting_type_user_e;
+      }
+      else {
+        if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_lock_print(global->main->program.warning.to, global->thread);
+
+          fl_print_format("%r%[%QUnknown rule setting '%]", global->main->program.warning.to, f_string_eol_s, global->main->program.warning.context, global->main->program.warning.prefix, global->main->program.warning.context);
+          fl_print_format(f_string_format_Q_single_s.string, global->main->program.warning.to, global->main->program.warning.notable, cache->action.name_item, global->main->program.warning.notable);
+          fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.warning.to, global->main->program.warning.context, global->main->program.warning.context, f_string_eol_s);
+
+          controller_rule_print_rule_message_cache(&global->main->program.warning, cache->action, F_false);
+
+          controller_unlock_print_flush(global->main->program.warning.to, global->thread);
+        }
+
+        continue;
+      }
+
+      if (cache->content_actions.array[i].used) {
+        range2.start = cache->content_actions.array[i].array[0].start;
+        range2.stop = cache->content_actions.array[i].array[cache->content_actions.array[i].used - 1].stop;
+
+        status = f_string_dynamic_partial_append_nulless(cache->buffer_item, range2, &cache->action.name_action);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+
+            break;
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          continue;
+        }
+      }
+      else {
+        if (empty_disallow) {
+          if (global->main->program.warning.verbosity == f_console_verbosity_debug_e) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_lock_print(global->main->program.warning.to, global->thread);
+
+            fl_print_format("%r%[%QEmpty rule setting.%]%r", global->main->program.warning.to, f_string_eol_s, global->main->program.warning.context, global->main->program.warning.prefix, global->main->program.warning.context, f_string_eol_s);
+
+            controller_rule_print_rule_message_cache(&global->main->program.warning, cache->action, F_false);
+
+            controller_unlock_print_flush(global->main->program.warning.to, global->thread);
+          }
+
+          continue;
+        }
+      }
+
+      if (type == controller_rule_setting_type_affinity_e) {
+        if (!cache->content_actions.array[i].used) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires one or more Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        rule->affinity.used = 0;
+
+        f_number_signed_t number = 0;
+
+        for (j = 0; j < cache->content_actions.array[i].used; ++j, number = 0) {
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(int32_t), (void **) &rule->affinity.array, &rule->affinity.used, &rule->affinity.size);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+            break;
+          }
+
+          status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_actions.array[i].array[j], &number);
+
+          if (F_status_set_fine(status) == F_number_positive) {
+            status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[j]), &number);
+
+            // Restore error on parameter problem.
+            if (F_status_set_fine(status) == F_parameter) {
+              status = F_status_set_error(F_number_positive);
+            }
+          }
+
+          if (F_status_is_error(status)) {
+            status = F_status_set_fine(status);
+
+            if (status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_negative || status == F_number_decimal) {
+              if (status == F_number_underflow) {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too small for this system", i, line_item, global->thread, cache);
+              }
+              else if (status == F_number_overflow || status == F_number_positive) {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too large for this system", i, line_item, global->thread, cache);
+              }
+              else {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid number", cache->content_actions.array[i].array[j], ", only whole numbers are allowed for an affinity value", i, line_item, global->thread, cache);
+              }
+
+              status = F_status_set_error(F_valid_not);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+            else {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "fl_conversion_dynamic_partial_to_signed_detect", F_true, F_false);
+
+              status = F_status_set_error(status);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+
+            continue;
+          }
+
+          rule->affinity.array[rule->affinity.used++] = number;
+        } // for
+
+        controller_rule_setting_read_print_values(global, controller_affinity_s, i, cache);
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_define_e || type == controller_rule_setting_type_parameter_e) {
+        if (cache->content_actions.array[i].used != 2) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires exactly two Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (type == controller_rule_setting_type_define_e) {
+          setting_maps = &rule->define;
+        }
+        else if (type == controller_rule_setting_type_parameter_e) {
+          setting_maps = &rule->parameter;
+        }
+
+        status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_map_t), (void **) &setting_maps->array, &setting_maps->used, &setting_maps->size);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+
+            break;
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          continue;
+        }
+
+        setting_maps->array[setting_maps->used].key.used = 0;
+        setting_maps->array[setting_maps->used].value.used = 0;
+
+        status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &setting_maps->array[setting_maps->used].key);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+            break;
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          continue;
+        }
+
+        status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[1], &setting_maps->array[setting_maps->used].value);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+
+            break;
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          continue;
+        }
+
+        controller_rule_setting_read_print_mapping(global, type == controller_rule_setting_type_define_e ? controller_define_s : controller_parameter_s, setting_maps->array[setting_maps->used]);
+
+        ++setting_maps->used;
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_cgroup_e) {
+        if (cache->content_actions.array[i].used < 2 || rule->has & controller_rule_has_cgroup_d) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires two or more Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (f_compare_dynamic_partial_string(controller_existing_s.string, cache->buffer_item, controller_existing_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->cgroup.as_new = F_false;
+        }
+        else if (f_compare_dynamic_partial_string(controller_new_s.string, cache->buffer_item, controller_new_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->cgroup.as_new = F_true;
+        }
+        else {
+          controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unknown option", cache->content_actions.array[i].array[0], "", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        rule->cgroup.path.used = 0;
+
+        status = f_string_dynamic_append(global->setting->path_cgroup, &rule->cgroup.path);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_append", F_true, F_false);
+        }
+        else {
+          rule->cgroup.groups.used = 0;
+
+          for (j = 1; j < cache->content_actions.array[i].used; ++j) {
+
+            status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &rule->cgroup.groups.array, &rule->cgroup.groups.used, &rule->cgroup.groups.size);
+
+            if (F_status_is_error(status)) {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+              break;
+            }
+
+            rule->cgroup.groups.array[rule->cgroup.groups.used].used = 0;
+
+            status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[j], &rule->cgroup.groups.array[rule->cgroup.groups.used]);
+
+            if (F_status_is_error(status)) {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+              break;
+            }
+
+            ++rule->cgroup.groups.used;
+          } // for
+        }
+
+        if (F_status_is_error(status)) {
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+
+            break;
+          }
+
+          rule->cgroup.path.used = 0;
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          continue;
+        }
+
+        rule->has |= controller_rule_has_cgroup_d;
+
+        controller_rule_setting_read_print_values(global, controller_cgroup_s, i, cache);
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_limit_e) {
+        if (cache->content_actions.array[i].used != 3) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires three Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (f_compare_dynamic_partial_string(controller_as_s.string, cache->buffer_item, controller_as_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_as_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_core_s.string, cache->buffer_item, controller_core_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_core_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_cpu_s.string, cache->buffer_item, controller_cpu_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_cpu_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_data_s.string, cache->buffer_item, controller_data_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_data_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_fsize_s.string, cache->buffer_item, controller_fsize_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_fsize_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_locks_s.string, cache->buffer_item, controller_locks_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_locks_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_memlock_s.string, cache->buffer_item, controller_memlock_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_memlock_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_msgqueue_s.string, cache->buffer_item, controller_msgqueue_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_msgqueue_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_nice_s.string, cache->buffer_item, controller_nice_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_nice_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_nofile_s.string, cache->buffer_item, controller_nofile_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_nofile_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_nproc_s.string, cache->buffer_item, controller_nproc_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_nproc_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_rss_s.string, cache->buffer_item, controller_rss_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_rss_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_rtprio_s.string, cache->buffer_item, controller_rtprio_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_rtprio_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_rttime_s.string, cache->buffer_item, controller_rttime_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_rttime_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_sigpending_s.string, cache->buffer_item, controller_sigpending_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_sigpending_e;
+        }
+        else if (f_compare_dynamic_partial_string(controller_stack_s.string, cache->buffer_item, controller_stack_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          type = controller_resource_limit_type_stack_e;
+        }
+        else {
+          if (global->main->program.error.verbosity == f_console_verbosity_debug_e) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_lock_print(global->main->program.error.to, global->thread);
+
+            fl_print_format("%r%[%QUnknown resource limit type '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+            fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->action.name_action, global->main->program.error.notable);
+            fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+            controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_true);
+
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        for (j = 0; j < rule->limits.used; ++j) {
+
+          if (type == rule->limits.array[j].type) {
+            if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+              // Get the current line number within the settings item.
+              cache->action.line_item = line_item;
+              f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+              cache->action.line_action = ++cache->action.line_item;
+
+              controller_lock_print(global->main->program.error.to, global->thread);
+
+              fl_print_format("%r%[%QThe resource limit type is already specified%]%r", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context, f_string_eol_s);
+
+              controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+              controller_unlock_print_flush(global->main->program.error.to, global->thread);
+            }
+
+            status = F_status_set_error(F_valid_not);
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+          }
+        } // for
+
+        if (F_status_is_error(status)) continue;
+
+        status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_limit_set_t), (void **) &rule->limits.array, &rule->limits.used, &rule->limits.size);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+          if (F_status_set_fine(status) == F_memory_not) {
+            status_return = status;
+
+            break;
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = status;
+          }
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+          continue;
+        }
+
+        f_number_signed_t number = 0;
+
+        for (j = 1; j < 3; ++j, number = 0) {
+
+          status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_actions.array[i].array[j], &number);
+
+          if (F_status_set_fine(status) == F_number_positive) {
+            status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[j]), &number);
+
+            // Restore error on parameter problem.
+            if (F_status_set_fine(status) == F_parameter) {
+              status = F_status_set_error(F_number_positive);
+            }
+          }
+
+          if (F_status_is_error(status)) {
+            status = F_status_set_fine(status);
+
+            if (status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_negative || status == F_number_positive || status == F_number_decimal) {
+              if (status == F_number_underflow) {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too small for this system", i, line_item, global->thread, cache);
+              }
+              else if (status == F_number_overflow) {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[j], ", the number is too large for this system", i, line_item, global->thread, cache);
+              }
+              else {
+                controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[j], ", only whole numbers are allowed for a resource limit value", i, line_item, global->thread, cache);
+              }
+
+              status = F_status_set_error(F_valid_not);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+            else {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "fl_conversion_dynamic_partial_to_signed_detect", F_true, F_false);
+
+              status = F_status_set_error(status);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+
+            break;
+          }
+
+          if (j == 1) {
+            rule->limits.array[rule->limits.used].value.rlim_cur = number;
+          }
+          else {
+            rule->limits.array[rule->limits.used].value.rlim_max = number;
+          }
+        } // for
+
+        if (F_status_is_error(status)) continue;
+
+        rule->limits.array[rule->limits.used++].type = type;
+
+        controller_rule_setting_read_print_values(global, controller_limit_s, i, cache);
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_name_e || type == controller_rule_setting_type_path_e || type == controller_rule_setting_type_engine_e) {
+
+        if (type == controller_rule_setting_type_name_e) {
+          setting_value = &rule->name;
+        }
+        else if (type == controller_rule_setting_type_path_e) {
+          setting_value = &rule->path;
+        }
+        else if (type == controller_rule_setting_type_engine_e) {
+          setting_value = &rule->engine;
+        }
+
+        if (setting_value->used || !cache->content_actions.array[i].used) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires one or more Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (type == controller_rule_setting_type_name_e || type == controller_rule_setting_type_engine_e) {
+          status = f_rip_dynamic_partial_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
+
+          if (type == controller_rule_setting_type_engine_e) {
+            rule->engine_arguments.used = 0;
+
+            if (cache->content_actions.array[i].used > 1) {
+              status = f_memory_array_increase_by(cache->content_actions.array[i].used - 1, sizeof(f_string_dynamic_t), (void **) &rule->engine_arguments.array, &rule->engine_arguments.used, &rule->engine_arguments.size);
+
+              for (j = 1; F_status_is_error_not(status) && j < cache->content_actions.array[i].used; ++j, ++rule->engine_arguments.used) {
+
+                rule->engine_arguments.array[rule->engine_arguments.used].used = 0;
+
+                status = f_string_dynamic_partial_append(cache->buffer_item, cache->content_actions.array[i].array[j], &rule->engine_arguments.array[rule->engine_arguments.used]);
+              } // for
+            }
+          }
+
+          if (F_status_is_error(status)) {
+            setting_value->used = 0;
+
+            if (type == controller_rule_setting_type_engine_e) {
+              rule->engine_arguments.used = 0;
+            }
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          status = controller_validate_has_graph(*setting_value);
+
+          if (status == F_false || F_status_set_fine(status) == F_complete_not_utf) {
+            if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+              // Get the current line number within the settings item.
+              cache->action.line_item = line_item;
+              f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+              cache->action.line_action = ++cache->action.line_item;
+            }
+
+            if (status == F_false) {
+              if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+                controller_lock_print(global->main->program.error.to, global->thread);
+
+                fl_print_format("%r%[%QRule setting has an invalid name '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+                fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, *setting_value, global->main->program.error.notable);
+                fl_print_format("%[', there must be at least 1 graph character.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+                controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
+              }
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = F_status_set_error(F_valid_not);
+              }
+            }
+            else {
+
+              // This function should only return F_complete_not_utf on error.
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_complete_not_utf, "controller_validate_has_graph", F_true, F_false);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+
+              controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+            }
+
+            setting_value->used = 0;
+
+            continue;
+          }
+
+          controller_rule_setting_read_print_value(global, type == controller_rule_setting_type_name_e ? controller_name_s : controller_engine_s, f_string_empty_s, *setting_value, 0);
+        }
+        else if (type == controller_rule_setting_type_path_e) {
+          status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], setting_value);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+
+            setting_value->used = 0;
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          controller_rule_setting_read_print_value(global, controller_path_s, f_string_empty_s, *setting_value, 0);
+        }
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_scheduler_e) {
+        if (cache->content_actions.array[i].used < 1 || cache->content_actions.array[i].used > 2 || rule->has & controller_rule_has_scheduler_d) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires either one or two Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (f_compare_dynamic_partial_string(controller_batch_s.string, cache->buffer_item, controller_batch_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_BATCH;
+          rule->scheduler.priority = 0;
+        }
+        else if (f_compare_dynamic_partial_string(controller_deadline_s.string, cache->buffer_item, controller_deadline_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_DEADLINE;
+          rule->scheduler.priority = 49;
+        }
+        else if (f_compare_dynamic_partial_string(controller_fifo_s.string, cache->buffer_item, controller_fifo_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_FIFO;
+          rule->scheduler.priority = 49;
+        }
+        else if (f_compare_dynamic_partial_string(controller_idle_s.string, cache->buffer_item, controller_idle_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_IDLE;
+          rule->scheduler.priority = 0;
+        }
+        else if (f_compare_dynamic_partial_string(controller_other_s.string, cache->buffer_item, controller_other_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_OTHER;
+          rule->scheduler.priority = 0;
+        }
+        else if (f_compare_dynamic_partial_string(controller_round_robin_s.string, cache->buffer_item, controller_round_robin_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          rule->scheduler.policy = SCHED_RR;
+          rule->scheduler.priority = 49;
+        }
+        else {
+          controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unknown scheduler", cache->content_actions.array[i].array[0], "", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (cache->content_actions.array[i].used > 1) {
+          const bool zero_only = rule->scheduler.policy == SCHED_BATCH || rule->scheduler.policy == SCHED_IDLE || rule->scheduler.policy == SCHED_OTHER;
+
+          f_number_signed_t number = 0;
+
+          status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_actions.array[i].array[1], &number);
+
+          if (F_status_set_fine(status) == F_number_positive) {
+            status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[1]), &number);
+
+            // Restore error on parameter problem.
+            if (F_status_set_fine(status) == F_parameter) {
+              status = F_status_set_error(F_number_positive);
+            }
+          }
+
+          if (F_status_is_error(status) || (zero_only && number) || (!zero_only && (number < 1 || number > 99))) {
+            status = F_status_set_fine(status);
+
+            if ((zero_only && number) || (!zero_only && (number < 1 || number > 99)) || status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_negative || status == F_number_positive) {
+              if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+                // Get the current line number within the settings item.
+                cache->action.line_item = line_item;
+                f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+                cache->action.line_action = ++cache->action.line_item;
+
+                controller_lock_print(global->main->program.error.to, global->thread);
+
+                fl_print_format("%r%[%QRule setting has an invalid number '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+                fl_print_format(f_string_format_Q_range_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global->main->program.error.notable);
+
+                if (zero_only) {
+                  fl_print_format("%[', only%] ", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+                  fl_print_format("%[0%]%[ is", global->main->program.error.to, global->main->program.error.notable, global->main->program.error.notable, global->main->program.error.context);
+                }
+                else {
+                  fl_print_format("%[', only the whole numbers inclusively between%] ", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+                  fl_print_format("%[1%] %[and%] ", global->main->program.error.to, global->main->program.error.notable, global->main->program.error.notable, global->main->program.error.context, global->main->program.error.context);
+                  fl_print_format("%[99%] %[are", global->main->program.error.to, global->main->program.error.notable, global->main->program.error.notable, global->main->program.error.context);
+                }
+
+                fl_print_format(" allowed for the designated scheduler.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+                controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
+              }
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = F_status_set_error(F_valid_not);
+              }
+            }
+            else {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, status, "fl_conversion_dynamic_partial_to_signed_detect", F_true, F_false);
+              status = F_status_set_error(status);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+
+            continue;
+          }
+
+          rule->scheduler.priority = number;
+        }
+
+        rule->has |= controller_rule_has_scheduler_d;
+
+        controller_rule_setting_read_print_values(global, controller_scheduler_s, i, cache);
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_timeout_e) {
+        if (cache->content_actions.array[i].used != 2) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires exactly two Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        uint8_t timeout_code = 0;
+
+        if (f_compare_dynamic_partial_string(controller_kill_s.string, cache->buffer_item, controller_kill_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          timeout_code = controller_rule_timeout_code_kill_d;
+        }
+        else if (f_compare_dynamic_partial_string(controller_start_s.string, cache->buffer_item, controller_start_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          timeout_code = controller_rule_timeout_code_start_d;
+        }
+        else if (f_compare_dynamic_partial_string(controller_stop_s.string, cache->buffer_item, controller_stop_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+          timeout_code = controller_rule_timeout_code_stop_d;
+        }
+        else {
+          if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_lock_print(global->main->program.error.to, global->thread);
+
+            fl_print_format("%r%[%QRule setting's first value has '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+            fl_print_format(f_string_format_Q_range_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->buffer_item, cache->content_actions.array[i].array[0], global->main->program.error.notable);
+            fl_print_format("%[' but only supports %r, %r, and %r.%]%r", global->main->program.error.to, global->main->program.error.context, controller_kill_s, controller_start_s, controller_stop_s, global->main->program.error.context, f_string_eol_s);
+
+            controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        f_number_unsigned_t number = 0;
+
+        status = fl_conversion_dynamic_partial_to_unsigned_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_actions.array[i].array[1], &number);
+
+        if (F_status_set_fine(status) == F_number_positive) {
+          status = fl_conversion_dynamic_partial_to_unsigned_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[1]), &number);
+
+          // Restore error on parameter problem.
+          if (F_status_set_fine(status) == F_parameter) {
+            status = F_status_set_error(F_number_positive);
+          }
+        }
+
+        if (F_status_is_error(status)) {
+          status = F_status_set_fine(status);
+
+          if (status == F_number_overflow) {
+            controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an unsupported number", cache->content_actions.array[i].array[1], ", the number is too large for this system", i, line_item, global->thread, cache);
+          }
+          else if (status == F_data_not || status == F_number || status == F_number_underflow || status == F_number_negative || status == F_number_positive || status == F_number_decimal) {
+            controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid number", cache->content_actions.array[i].array[1], ", only positive whole numbers are allowed", i, line_item, global->thread, cache);
+          }
+          else {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, status, "fl_conversion_dynamic_partial_to_signed_detect", F_true, F_false);
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+        }
+        else {
+          if (timeout_code == controller_rule_timeout_code_kill_d) {
+            rule->timeout_kill = number;
+          }
+          else if (timeout_code == controller_rule_timeout_code_start_d) {
+            rule->timeout_start = number;
+          }
+          else {
+            rule->timeout_stop = number;
+          }
+
+          if (global->main->program.error.verbosity == f_console_verbosity_debug_e || (global->main->program.error.verbosity == f_console_verbosity_verbose_e && (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e))) {
+            f_string_static_t name_sub = controller_stop_s;
+
+            if (timeout_code == controller_rule_timeout_code_kill_d) {
+              name_sub = controller_kill_s;
+            }
+            else if (timeout_code == controller_rule_timeout_code_start_d) {
+              name_sub = controller_start_s;
+            }
+
+            cache->action.generic.used = 0;
+
+            status = f_rip_dynamic_partial_nulless(cache->buffer_item, cache->content_actions.array[i].array[1], &cache->action.generic);
+
+            if (F_status_is_error(status)) {
+              controller_print_error(global->thread, &global->main->program.error, F_status_set_fine(status), "f_rip_dynamic_partial_nulless", F_true);
+
+              break;
+            }
+
+            controller_rule_setting_read_print_value(global, controller_timeout_s, name_sub, cache->action.generic, 0);
+          }
+        }
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_capability_e || type == controller_rule_setting_type_nice_e || type == controller_rule_setting_type_user_e) {
+        if (cache->content_actions.array[i].used != 1 || type == controller_rule_setting_type_capability_e && rule->capability || type == controller_rule_setting_type_group_e && (rule->has & controller_rule_has_group_d) || type == controller_rule_setting_type_nice_e && (rule->has & controller_rule_has_nice_d) || type == controller_rule_setting_type_user_e && (rule->has & controller_rule_has_user_d)) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires exactly one Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        if (type == controller_rule_setting_type_capability_e) {
+          cache->action.generic.used = 0;
+
+          status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+          if (F_status_is_error(status)) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_lock_print(global->main->program.error.to, global->thread);
+
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+            controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = F_status_set_error(F_valid_not);
+            }
+          }
+
+          status = f_capability_from_text(cache->action.generic, &rule->capability);
+
+          if (F_status_is_error(status) && F_status_set_fine(status) != F_support_not) {
+            if (F_status_set_fine(status) == F_memory_not) {
+
+              // Get the current line number within the settings item.
+              cache->action.line_item = line_item;
+              f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+              cache->action.line_action = ++cache->action.line_item;
+
+              controller_lock_print(global->main->program.error.to, global->thread);
+
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_capability_from_text", F_true, F_false);
+
+              controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+              controller_unlock_print_flush(global->main->program.error.to, global->thread);
+
+              status_return = status;
+
+              break;
+            }
+
+            controller_rule_setting_read_print_error(&global->main->program.error, "failed to process the capabilities", i, line_item, global->thread, cache);
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = F_status_set_error(F_valid_not);
+            }
+
+            continue;
+          }
+
+          controller_rule_setting_read_print_value(global, controller_capability_s, f_string_empty_s, cache->action.generic, 0);
+        }
+        else if (type == controller_rule_setting_type_nice_e) {
+          f_number_signed_t number = 0;
+
+          status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, cache->content_actions.array[i].array[0], &number);
+
+          if (F_status_set_fine(status) == F_number_positive) {
+            status = fl_conversion_dynamic_partial_to_signed_detect(fl_conversion_data_base_10_c, cache->buffer_item, controller_range_after_number_sign(cache->buffer_item, cache->content_actions.array[i].array[0]), &number);
+
+            // Restore error on parameter problem.
+            if (F_status_set_fine(status) == F_parameter) {
+              status = F_status_set_error(F_number_positive);
+            }
+          }
+
+          if (F_status_is_error(status) || number < -20 || number > 19) {
+            status = F_status_set_fine(status);
+
+            if (number < -20 || number > 19 || status == F_data_not || status == F_number || status == F_number_overflow || status == F_number_underflow || status == F_number_decimal) {
+              if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+                // Get the current line number within the settings item.
+                cache->action.line_item = line_item;
+                f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+                cache->action.line_action = ++cache->action.line_item;
+
+                controller_lock_print(global->main->program.error.to, global->thread);
+
+                fl_print_format("%r%[%QRule setting has an invalid number '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+                fl_print_format(f_string_format_Q_range_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->buffer_item, cache->content_actions.array[i].array[0], global->main->program.error.notable);
+                fl_print_format("%[', only the whole numbers inclusively between%] ", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+                fl_print_format("%[-20%]", global->main->program.error.to, global->main->program.error.notable, global->main->program.error.notable);
+                fl_print_format(" %[and%] ", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context);
+                fl_print_format("%[19%]", global->main->program.error.to, global->main->program.error.notable, global->main->program.error.notable);
+                fl_print_format(" %[are allowed.%]%r", global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+                controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
+              }
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = F_status_set_error(F_valid_not);
+              }
+            }
+            else {
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, status, "fl_conversion_dynamic_partial_to_signed_detect", F_true, F_false);
+              status = F_status_set_error(status);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+          }
+          else {
+            rule->nice = number;
+            rule->has |= controller_rule_has_nice_d;
+
+            if ((global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e) || global->main->program.error.verbosity == f_console_verbosity_verbose_e) {
+              cache->action.generic.used = 0;
+
+              status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+              if (F_status_is_error(status)) {
+
+                // Get the current line number within the settings item.
+                cache->action.line_item = line_item;
+                f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+                cache->action.line_action = ++cache->action.line_item;
+
+                controller_lock_print(global->main->program.error.to, global->thread);
+
+                controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+                controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
+
+                if (F_status_set_fine(status) == F_memory_not) {
+                  status_return = status;
+
+                  break;
+                }
+
+                if (F_status_is_error_not(status_return)) {
+                  status_return = F_status_set_error(F_valid_not);
+                }
+              }
+
+              if (F_status_is_error_not(status)) {
+                controller_rule_setting_read_print_value(global, controller_nice_s, f_string_empty_s, cache->action.generic, 0);
+              }
+            }
+          }
+        }
+        else if (type == controller_rule_setting_type_user_e) {
+          uid_t number = 0;
+
+          status = controller_get_id_user(cache->buffer_item, cache->content_actions.array[i].array[0], cache, &number);
+
+          if (F_status_is_error(status)) {
+            status = F_status_set_fine(status);
+
+            if (status == F_exist_not) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid user", cache->content_actions.array[i].array[0], ", because no user was found by that name", i, line_item, global->thread, cache);
+            }
+            else if (status == F_number_too_large) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is too large", i, line_item, global->thread, cache);
+            }
+            else if (status == F_number) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid user", cache->content_actions.array[i].array[0], ", because the given ID is not a valid supported number", i, line_item, global->thread, cache);
+            }
+            else {
+
+              // Get the current line number within the settings item.
+              cache->action.line_item = line_item;
+              f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+              cache->action.line_action = ++cache->action.line_item;
+
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, status, "controller_get_id_user", F_true, F_false);
+
+              controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = F_status_set_error(status);
+            }
+          }
+          else {
+            rule->user = number;
+            rule->has |= controller_rule_has_user_d;
+
+            if (global->main->program.error.verbosity == f_console_verbosity_debug_e || (global->main->program.error.verbosity == f_console_verbosity_verbose_e && (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e))) {
+              cache->action.generic.used = 0;
+
+              status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[0], &cache->action.generic);
+
+              controller_rule_setting_read_print_value(global, controller_user_s, f_string_empty_s, cache->action.generic, 0);
+            }
+          }
+        }
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_group_e) {
+        if (!cache->content_actions.array[i].used) {
+          controller_rule_setting_read_print_error(&global->main->program.error, "requires one or more Content", i, line_item, global->thread, cache);
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        gid_t number = 0;
+
+        rule->groups.used = 0;
+
+        for (j = 0; j < cache->content_actions.array[i].used; ++j) {
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_int32s_t), (void **) &rule->groups.array, &rule->groups.used, &rule->groups.size);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          status = controller_get_id_group(cache->buffer_item, cache->content_actions.array[i].array[j], cache, &number);
+
+          if (F_status_is_error(status)) {
+            status = F_status_set_fine(status);
+
+            if (status == F_exist_not) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid group", cache->content_actions.array[i].array[j], ", because no group was found by that name", i, line_item, global->thread, cache);
+            }
+            else if (status == F_number_too_large) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid group", cache->content_actions.array[i].array[j], ", because the given ID is too large", i, line_item, global->thread, cache);
+            }
+            else if (status == F_number) {
+              controller_rule_setting_read_print_error_with_range(&global->main->program.error, " has an invalid group", cache->content_actions.array[i].array[j], ", because the given ID is not a valid supported number", i, line_item, global->thread, cache);
+            }
+            else {
+
+              // Get the current line number within the settings item.
+              cache->action.line_item = line_item;
+              f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+              cache->action.line_action = ++cache->action.line_item;
+
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, status, "f_account_group_id_by_name", F_true, F_false);
+
+              controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = F_status_set_error(status);
+            }
+          }
+          else {
+            if (rule->has & controller_rule_has_group_d) {
+              rule->groups.array[rule->groups.used++] = number;
+            }
+            else {
+              rule->group = number;
+              rule->has |= controller_rule_has_group_d;
+            }
+          }
+        } // for
+
+        controller_rule_setting_read_print_values(global, controller_group_s, i, cache);
+
+        continue;
+      }
+
+      if (type == controller_rule_setting_type_environment_e) {
+        setting_values = &rule->environment;
+
+        for (j = 0; j < cache->content_actions.array[i].used; ++j) {
+
+          status = f_memory_array_increase(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &setting_values->array, &setting_values->used, &setting_values->size);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase", F_true, F_false);
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          setting_values->array[setting_values->used].used = 0;
+
+          status = f_string_dynamic_partial_append_nulless(cache->buffer_item, cache->content_actions.array[i].array[j], &setting_values->array[setting_values->used]);
+
+          if (F_status_is_error(status)) {
+            controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_string_dynamic_partial_append_nulless", F_true, F_false);
+
+            setting_values->array[setting_values->used].used = 0;
+
+            if (F_status_set_fine(status) == F_memory_not) {
+              status_return = status;
+
+              break;
+            }
+
+            if (F_status_is_error_not(status_return)) {
+              status_return = status;
+            }
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          status = controller_validate_environment_name(setting_values->array[setting_values->used]);
+
+          if (status == F_false || F_status_set_fine(status) == F_complete_not_utf) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            if (status == F_false) {
+              if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+                controller_lock_print(global->main->program.error.to, global->thread);
+
+                fl_print_format("%r%[%QRule setting has an invalid environment variable name '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+                fl_print_format(f_string_format_Q_single_s.string, global->main->program.error.to, global->main->program.error.notable, setting_values->array[setting_values->used], global->main->program.error.notable);
+                fl_print_format(f_string_format_sentence_end_quote_s.string, global->main->program.error.to, global->main->program.error.context, global->main->program.error.context, f_string_eol_s);
+
+                controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+                controller_unlock_print_flush(global->main->program.error.to, global->thread);
+              }
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = F_status_set_error(F_valid_not);
+              }
+            }
+            else {
+
+              // This function should only return F_complete_not_utf on error.
+              controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_complete_not_utf, "controller_validate_environment_name", F_true, F_false);
+
+              if (F_status_is_error_not(status_return)) {
+                status_return = status;
+              }
+            }
+
+            setting_values->array[setting_values->used].used = 0;
+
+            controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+            continue;
+          }
+
+          ++setting_values->used;
+        } // for
+
+        rule->has |= controller_rule_has_environment_d;
+
+        if (cache->content_actions.array[i].used) {
+          controller_rule_setting_read_print_values(global, controller_environment_s, i, cache);
+        }
+        else {
+          if (global->main->program.error.verbosity == f_console_verbosity_debug_e || (global->main->program.error.verbosity == f_console_verbosity_verbose_e && (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e))) {
+            controller_lock_print(global->main->program.output.to, global->thread);
+
+            fl_print_format("%rProcessing rule item action '%[%r%]' setting value to an empty set.%r", global->main->program.output.to, f_string_eol_s, global->main->program.context.set.title, controller_environment_s, global->main->program.context.set.title, f_string_eol_s);
+
+            controller_unlock_print_flush(global->main->program.output.to, global->thread);
+          }
+        }
+
+        continue;
+      }
+
+      // The "on" Rule Setting.
+      if (cache->content_actions.array[i].used != 4) {
+        controller_rule_setting_read_print_error(&global->main->program.error, "requires exactly four Content", i, line_item, global->thread, cache);
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = F_status_set_error(F_valid_not);
+        }
+
+        continue;
+      }
+
+      if (f_compare_dynamic_partial_string(controller_freeze_s.string, cache->buffer_item, controller_freeze_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_freeze_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_kill_s.string, cache->buffer_item, controller_kill_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_kill_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_pause_s.string, cache->buffer_item, controller_pause_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_pause_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_reload_s.string, cache->buffer_item, controller_reload_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_reload_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_restart_s.string, cache->buffer_item, controller_restart_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_restart_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_resume_s.string, cache->buffer_item, controller_resume_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_resume_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_start_s.string, cache->buffer_item, controller_start_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_start_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_stop_s.string, cache->buffer_item, controller_stop_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_stop_e;
+      }
+      else if (f_compare_dynamic_partial_string(controller_thaw_s.string, cache->buffer_item, controller_thaw_s.used, cache->content_actions.array[i].array[0]) == F_equal_to) {
+        action = controller_rule_action_type_thaw_e;
+      }
+      else {
+        if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+          // Get the current line number within the settings item.
+          cache->action.line_item = line_item;
+          f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+          cache->action.line_action = ++cache->action.line_item;
+
+          f_thread_mutex_lock(&global->thread->lock.print);
+
+          controller_lock_print(global->main->program.error.to, global->thread);
+
+          fl_print_format("%r%[%QRule setting's second value has '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+          fl_print_format(f_string_format_Q_range_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global->main->program.error.notable);
+          fl_print_format("%[' but only supports %r, %r, %r, %r, %r", global->main->program.error.to, global->main->program.error.context, controller_freeze_s, controller_kill_s, controller_pause_s, controller_reload_s, controller_restart_s);
+          fl_print_format("%r, %r, %r, and %r.%]%r", global->main->program.error.to, controller_resume_s, controller_start_s, controller_stop_s, controller_thaw_s, global->main->program.error.context, f_string_eol_s);
+
+          controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+          controller_unlock_print_flush(global->main->program.error.to, global->thread);
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = F_status_set_error(F_valid_not);
+        }
+
+        continue;
+      }
+
+      for (j = 0; j < rule->ons.used; ++j) {
+        if (rule->ons.array[j].action == action) break;
+      } // for
+
+      if (j == rule->ons.used) {
+        status = controller_rule_ons_increase(&rule->ons);
+      }
+
+      if (F_status_is_error(status)) {
+        controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "controller_rule_ons_increase", F_true, F_false);
+      }
+      else {
+        if (f_compare_dynamic_partial_string(controller_need_s.string, cache->buffer_item, controller_need_s.used, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].need;
+        }
+        else if (f_compare_dynamic_partial_string(controller_want_s.string, cache->buffer_item, controller_want_s.used, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].want;
+        }
+        else if (f_compare_dynamic_partial_string(controller_wish_s.string, cache->buffer_item, controller_wish_s.used, cache->content_actions.array[i].array[1]) == F_equal_to) {
+          setting_values = &rule->ons.array[j].wish;
+        }
+        else {
+          if (global->main->program.error.verbosity > f_console_verbosity_quiet_e) {
+
+            // Get the current line number within the settings item.
+            cache->action.line_item = line_item;
+            f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+            cache->action.line_action = ++cache->action.line_item;
+
+            controller_lock_print(global->main->program.error.to, global->thread);
+
+            fl_print_format("%r%[%QRule setting's second value has '%]", global->main->program.error.to, f_string_eol_s, global->main->program.error.context, global->main->program.error.prefix, global->main->program.error.context);
+            fl_print_format(f_string_format_Q_range_single_s.string, global->main->program.error.to, global->main->program.error.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global->main->program.error.notable);
+            fl_print_format("%[' but only supports %r, %r, and %r.%]%r", global->main->program.error.to, global->main->program.error.context, controller_need_s, controller_want_s, controller_wish_s, global->main->program.error.context, f_string_eol_s);
+
+            controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_false);
+
+            controller_unlock_print_flush(global->main->program.error.to, global->thread);
+          }
+
+          if (F_status_is_error_not(status_return)) {
+            status_return = F_status_set_error(F_valid_not);
+          }
+
+          continue;
+        }
+
+        status = f_memory_array_increase_by(controller_common_allocation_small_d, sizeof(f_string_dynamic_t), (void **) &setting_values->array, &setting_values->used, &setting_values->size);
+
+        if (F_status_is_error(status)) {
+          controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_memory_array_increase_by", F_true, F_false);
+        }
+      }
+
+      if (F_status_is_error(status)) {
+        if (F_status_set_fine(status) == F_memory_not) {
+          status_return = status;
+
+          break;
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = status;
+        }
+
+        // Get the current line number within the settings item.
+        cache->action.line_item = line_item;
+        f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+        cache->action.line_action = ++cache->action.line_item;
+
+        controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+        continue;
+      }
+
+      status = controller_rule_id_construct(global, cache->buffer_item, cache->content_actions.array[i].array[2], cache->content_actions.array[i].array[3], &setting_values->array[setting_values->used]);
+
+      if (F_status_is_error(status)) {
+        setting_values->array[setting_values->used].used = 0;
+
+        if (F_status_set_fine(status) == F_memory_not) {
+          status_return = status;
+          break;
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = status;
+        }
+
+        // Get the current line number within the settings item.
+        cache->action.line_item = line_item;
+        f_fss_count_lines(cache->buffer_item, cache->object_actions.array[i].start, &cache->action.line_item, &global->main->setting.state);
+
+        cache->action.line_action = ++cache->action.line_item;
+
+        controller_rule_item_print_error(global->thread, &global->main->program.error, cache->action, F_false, F_status_set_fine(status));
+
+        continue;
+      }
+
+      cache->buffer_path.used = 0;
+
+      status = f_file_name_base(setting_values->array[setting_values->used], &cache->buffer_path);
+
+      if (F_status_is_error(status)) {
+        setting_values->array[setting_values->used].used = 0;
+
+        controller_rule_print_error(global->thread, &global->main->program.error, cache->action, F_status_set_fine(status), "f_file_name_base", F_true, F_false);
+
+        if (F_status_set_fine(status) == F_memory_not) {
+          status_return = status;
+
+          break;
+        }
+
+        if (F_status_is_error_not(status_return)) {
+          status_return = status;
+        }
+
+        continue;
+      }
+
+      rule->ons.array[j].action = action;
+
+      ++setting_values->used;
+
+      if (j == rule->ons.used) {
+        ++rule->ons.used;
+      }
+
+      if (global->main->program.error.verbosity == f_console_verbosity_debug_e || (global->main->program.error.verbosity == f_console_verbosity_verbose_e && (global->main->program.parameters.array[controller_parameter_simulate_e].result & f_console_result_found_e))) {
+        controller_lock_print(global->main->program.output.to, global->thread);
+
+        fl_print_format("%rProcessing rule item action '%[%r%]', adding ", global->main->program.output.to, f_string_eol_s, global->main->program.context.set.title, controller_on_s, global->main->program.context.set.title);
+        fl_print_format("'%[%/Q%]' of ", global->main->program.output.to, global->main->program.context.set.notable, cache->buffer_item, cache->content_actions.array[i].array[1], global->main->program.context.set.notable);
+        fl_print_format("'%[%/Q%]/", global->main->program.output.to, global->main->program.context.set.important, cache->buffer_item, cache->content_actions.array[i].array[2], global->main->program.context.set.important);
+        fl_print_format("%[%/Q%]'.%r", global->main->program.output.to, global->main->program.context.set.important, cache->buffer_item, cache->content_actions.array[i].array[3], global->main->program.context.set.important, f_string_eol_s);
+
+        controller_unlock_print_flush(global->main->program.output.to, global->thread);
+      }
+    } // for
+
+    // Restore the current name item and line number, which there should already be enough allocated space for.
+    memcpy(cache->action.name_item.string, name_item, sizeof(f_char_t) * length_name_item);
+
+    cache->action.name_item.string[length_name_item] = 0;
+    cache->action.name_item.used = length_name_item;
+
+    cache->action.line_item = line_item;
+
+    return status_return;
+  }
+#endif // _di_controller_rule_setting_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/setting.h b/sources/c/main/rule/setting.h
new file mode 100644 (file)
index 0000000..a8b07e1
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "setting" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_setting_h
+#define _controller_main_rule_setting_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Read the content within the buffer, extracting all valid settings.
+ *
+ * This will perform additional FSS read functions as appropriate.
+ *
+ * Errors from this are not considered fatal, but the first error code encountered is returned.
+ * Memory failure errors are always immediately returned.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param is_normal
+ *   If TRUE, then process as if this operates during a normal operation (entry and control).
+ *   If FALSE, then process as if this operates during a an exit operation.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ * @param rule
+ *   The processed rule.
+ *
+ * @return
+ *    F_okay on success.
+ *
+ *    F_valid_not (with error bit) on success but there were one or more invalid settings encountered.
+ *
+ *   Errors (with error bit) from: f_memory_array_increase().
+ *   Errors (with error bit) from: f_rip_dynamic_partial_nulless().
+ *   Errors (with error bit) from: f_string_dynamic_partial_append_nulless().
+ *   Errors (with error bit) from: fll_fss_extended_read().
+ *
+ *   Errors (with error bit) from: controller_path_canonical_relative().
+ *
+ * @see f_memory_array_increase()
+ * @see f_rip_dynamic_partial_nulless()
+ * @see f_string_dynamic_partial_append_nulless()
+ * @see fll_fss_extended_read()
+ *
+ * @see controller_path_canonical_relative()
+ */
+#ifndef _di_controller_rule_setting_read_
+  extern f_status_t controller_rule_setting_read(controller_global_t * const global, const bool is_normal, controller_cache_t * const cache, controller_rule_t * const rule);
+#endif // _di_controller_rule_setting_read_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_setting_h
diff --git a/sources/c/main/rule/validate.c b/sources/c/main/rule/validate.c
new file mode 100644 (file)
index 0000000..fc4e96d
--- /dev/null
@@ -0,0 +1,505 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_validate_
+  void controller_rule_validate(controller_global_t * const global, const controller_rule_t rule, const uint8_t action, const uint8_t options, controller_cache_t * const cache) {
+
+    if (!global || !cache) return F_status_set_error(F_parameter);
+
+    controller_main_t * const main = global->main;
+
+    switch (action) {
+      case controller_rule_action_type_freeze_e:
+      case controller_rule_action_type_kill_e:
+      case controller_rule_action_type_pause_e:
+      case controller_rule_action_type_reload_e:
+      case controller_rule_action_type_restart_e:
+      case controller_rule_action_type_resume_e:
+      case controller_rule_action_type_start_e:
+      case controller_rule_action_type_stop_e:
+      case controller_rule_action_type_thaw_e:
+        break;
+
+      default:
+        if (main->program.error.verbosity > f_console_verbosity_quiet_e) {
+          controller_lock_print(main->program.error.to, global->thread);
+
+          fl_print_format("%r%[%QUnsupported action type '%]", main->program.error.to, f_string_eol_s, main->program.error.context, main->program.error.prefix, main->program.error.context);
+          fl_print_format(f_string_format_r_single_s.string, main->program.error.to, main->program.error.notable, controller_rule_action_type_name(action), main->program.error.notable);
+          fl_print_format("%[' while attempting to validate rule execution.%]%r", main->program.error.to, main->program.error.context, main->program.error.context, f_string_eol_s);
+
+          controller_rule_print_rule_message_cache(&global->main->program.error, cache->action, F_true);
+
+          controller_unlock_print_flush(main->program.error.to, global->thread);
+        }
+
+        return;
+    }
+
+    f_number_unsigned_t i = 0;
+    f_number_unsigned_t j = 0;
+
+    // Find at least one of the requested action.
+    {
+      bool missing = F_true;
+
+      for (; i < rule.items.used; ++i) {
+
+        for (j = 0; j < rule.items.array[i].actions.used; ++j) {
+
+          if (!action || rule.items.array[i].actions.array[j].type == action) {
+            missing = F_false;
+
+            break;
+          }
+        } // for
+      } // for
+
+      if (missing) {
+        controller_lock_print(main->program.output.to, global->thread);
+
+        if (rule.items.used) {
+          fl_print_format("%rRule '", main->program.output.to, f_string_eol_s);
+          fl_print_format("%[%Q%]' has no '", main->program.output.to, main->program.context.set.title, rule.name, main->program.context.set.title);
+          fl_print_format("%[%r%]' action to execute and would '", main->program.output.to, main->program.context.set.title, controller_rule_action_type_name(action), main->program.context.set.title);
+          fl_print_format("%[%r%]' because it is '", main->program.output.to, main->program.context.set.important, options & controller_process_option_require_d ? controller_fail_s : controller_succeed_s, main->program.context.set.important);
+          fl_print_format("%[%r%]'.%r", main->program.output.to, main->program.context.set.important, options & controller_process_option_require_d ? controller_required_s : controller_optional_s, main->program.context.set.important, f_string_eol_s);
+        }
+        else {
+          fl_print_format("%rRule '", main->program.output.to, f_string_eol_s);
+          fl_print_format("%[%Q%]' has no known '", main->program.output.to, main->program.context.set.title, rule.name, main->program.context.set.title);
+          fl_print_format("%[%r %r%]' (such as ", main->program.output.to, main->program.context.set.title, controller_rule_s, controller_type_s, main->program.context.set.title);
+          fl_print_format("'%[%r%]', ", main->program.output.to, main->program.context.set.title, controller_command_s, main->program.context.set.title);
+          fl_print_format("'%[%r%]', ", main->program.output.to, main->program.context.set.title, controller_service_s, main->program.context.set.title);
+          fl_print_format("'%[%r%]', or ", main->program.output.to, main->program.context.set.title, controller_script_s, main->program.context.set.title);
+          fl_print_format("'%[%r%]'", main->program.output.to, main->program.context.set.title, controller_utility_s, main->program.context.set.title);
+          fl_print_format(") and would '%[%r%]' because it is '", main->program.output.to, main->program.context.set.important, options & controller_process_option_require_d ? controller_fail_s : controller_succeed_s, main->program.context.set.important);
+          fl_print_format("%[%r%]'.%r", main->program.output.to, main->program.context.set.important, options & controller_process_option_require_d ? controller_required_s : controller_optional_s, main->program.context.set.important, f_string_eol_s);
+        }
+
+        controller_unlock_print_flush(main->program.output.to, global->thread);
+      }
+    }
+
+    controller_lock_print(main->program.output.to, global->thread);
+
+    fl_print_format("%rRule %[%Q%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.title, rule.alias, main->program.context.set.title, f_string_eol_s);
+
+    // Name.
+    fl_print_format("  %[%r%] %Q%r", main->program.output.to, main->program.context.set.important, controller_name_s, main->program.context.set.important, rule.name, f_string_eol_s);
+
+    // Capability.
+    fl_print_format("  %[%r%] ", main->program.output.to, main->program.context.set.important, controller_capability_s, main->program.context.set.important);
+
+    if (f_capability_supported()) {
+      if (rule.capability) {
+        cache->action.generic.used = 0;
+
+        if (F_status_is_error_not(f_capability_to_text(rule.capability, &cache->action.generic))) {
+          f_print_dynamic_safely(cache->action.generic, main->program.output.to);
+        }
+      }
+
+      f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+    }
+    else {
+      fl_print_format("%[(unsupported)%]%r", main->program.output.to, main->program.context.set.warning, main->program.context.set.warning, f_string_eol_s);
+    }
+
+    // Control Group.
+    fl_print_format("  %[%r%]", main->program.output.to, main->program.context.set.important, controller_cgroup_s, main->program.context.set.important);
+
+    if (rule.has & controller_rule_has_cgroup_d) {
+      fl_print_format(" %r", main->program.output.to, rule.cgroup.as_new ? controller_new_s : controller_existing_s);
+
+      for (i = 0; i < rule.cgroup.groups.used; ++i) {
+
+        if (rule.cgroup.groups.array[i].used) {
+          fl_print_format(" %Q", main->program.output.to, rule.cgroup.groups.array[i]);
+        }
+      } // for
+    }
+
+    f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+    // How.
+    fl_print_format("  %[%r%] %r%r", main->program.output.to, main->program.context.set.important, controller_how_s, main->program.context.set.important, options & controller_process_option_asynchronous_d ? controller_asynchronous_s : controller_synchronous_s, f_string_eol_s);
+
+    // Nice.
+    fl_print_format("  %[%r%]", main->program.output.to, main->program.context.set.important, controller_nice_s, main->program.context.set.important);
+
+    if (rule.has & controller_rule_has_nice_d) {
+      fl_print_format(" %i", main->program.output.to, rule.nice);
+    }
+
+    f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+    // Scheduler.
+    fl_print_format("  %[%r%]", main->program.output.to, main->program.context.set.important, controller_scheduler_s, main->program.context.set.important);
+
+    if (rule.has & controller_rule_has_scheduler_d) {
+      f_string_static_t policy = f_string_static_t_initialize;
+
+      if (rule.scheduler.policy == SCHED_BATCH) {
+        policy = controller_batch_s;
+      }
+      else if (rule.scheduler.policy == SCHED_DEADLINE) {
+        policy = controller_deadline_s;
+      }
+      else if (rule.scheduler.policy == SCHED_FIFO) {
+        policy = controller_fifo_s;
+      }
+      else if (rule.scheduler.policy == SCHED_IDLE) {
+        policy = controller_idle_s;
+      }
+      else if (rule.scheduler.policy == SCHED_OTHER) {
+        policy = controller_other_s;
+      }
+      else if (rule.scheduler.policy == SCHED_RR) {
+        policy = controller_round_robin_s;
+      }
+
+      fl_print_format(" %r %i", main->program.output.to, policy, rule.scheduler.priority);
+    }
+
+    f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+    // Engine.
+    if (rule.engine_arguments.used) {
+      fl_print_format("  %[%r%] %Q", main->program.output.to, main->program.context.set.important, controller_engine_s, main->program.context.set.important, rule.engine);
+
+      for (i = 0; i < rule.engine_arguments.used; ++i) {
+
+        if (rule.engine_arguments.array[i].used) {
+          fl_print_format(" %Q", main->program.output.to, rule.engine_arguments.array[i]);
+        }
+      } // for
+
+      fl_print_format("%r", main->program.output.to, f_string_eol_s);
+    }
+    else {
+      fl_print_format("  %[%r%] %Q%r", main->program.output.to, main->program.context.set.important, controller_engine_s, main->program.context.set.important, rule.engine, f_string_eol_s);
+    }
+
+    // User.
+    fl_print_format("  %[%r%]", main->program.output.to, main->program.context.set.important, controller_user_s, main->program.context.set.important);
+
+    if (rule.has & controller_rule_has_user_d) {
+      fl_print_format(" %i", main->program.output.to, rule.user);
+    }
+
+    f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+    // Wait.
+    fl_print_format("  %[%r%] %r%r", main->program.output.to, main->program.context.set.important, controller_wait_s, main->program.context.set.important, options & controller_process_option_wait_d ? controller_yes_s : controller_no_s, f_string_eol_s);
+
+    // Affinity.
+    fl_print_format("  %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_affinity_s, main->program.context.set.important, f_string_eol_s);
+
+    for (i = 0; i < rule.affinity.used; ++i) {
+      fl_print_format("    %i%r", main->program.output.to, rule.affinity.array[i], f_string_eol_s);
+    } // for
+
+    // Define.
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_define_s, main->program.context.set.important, f_string_eol_s);
+
+    for (i = 0; i < rule.define.used; ++i) {
+
+      if (rule.define.array[i].key.used && rule.define.array[i].value.used) {
+        fl_print_format("    %Q %[=%] %Q%r", main->program.output.to, rule.define.array[i].key, main->program.context.set.important, main->program.context.set.important, rule.define.array[i].value, f_string_eol_s);
+      }
+    } // for
+
+    // Environment.
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_environment_s, main->program.context.set.important, f_string_eol_s);
+
+    for (i = 0; i < rule.environment.used; ++i) {
+
+      if (rule.environment.array[i].used) {
+        fl_print_format("    %Q%r", main->program.output.to, rule.environment.array[i], f_string_eol_s);
+      }
+    } // for
+
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_parameter_s, main->program.context.set.important, f_string_eol_s);
+
+    // Parameter.
+    for (i = 0; i < rule.parameter.used; ++i) {
+
+      if (rule.parameter.array[i].key.used && rule.parameter.array[i].value.used) {
+        fl_print_format("    %Q %[=%] %Q%r", main->program.output.to, rule.parameter.array[i].key, main->program.context.set.important, main->program.context.set.important, rule.parameter.array[i].value, f_string_eol_s);
+      }
+    } // for
+
+    // Group.
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_group_s, main->program.context.set.important, f_string_eol_s);
+
+    if (rule.has & controller_rule_has_group_d) {
+      fl_print_format("    %i%r", main->program.output.to, rule.group, f_string_eol_s);
+
+      for (i = 0; i < rule.groups.used; ++i) {
+        fl_print_format("    %i%r", main->program.output.to, rule.groups.array[i], f_string_eol_s);
+      } // for
+    }
+
+    // Limit.
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_limit_s, main->program.context.set.important, f_string_eol_s);
+
+    for (i = 0; i < rule.limits.used; ++i) {
+      fl_print_format("    %Q %[=%] %un %un%r", main->program.output.to, controller_rule_setting_limit_type_name(rule.limits.array[i].type), main->program.context.set.important, main->program.context.set.important, rule.limits.array[i].value.rlim_cur, rule.limits.array[i].value.rlim_max, f_string_eol_s);
+    } // for
+
+    // On.
+    fl_print_format("  }%r  %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_on_s, main->program.context.set.important, f_string_eol_s);
+
+    for (i = 0; i < rule.ons.used; ++i) {
+
+      fl_print_format("    %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_action_s, main->program.context.set.important, f_string_eol_s);
+
+      {
+        f_string_static_t action = f_string_static_t_initialize;
+
+        if (rule.ons.array[i].action == controller_rule_action_type_freeze_e) {
+          action = controller_freeze_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_kill_e) {
+          action = controller_kill_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_pause_e) {
+          action = controller_pause_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_reload_e) {
+          action = controller_reload_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_restart_e) {
+          action = controller_restart_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_resume_e) {
+          action = controller_resume_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_start_e) {
+          action = controller_start_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_stop_e) {
+          action = controller_stop_s;
+        }
+        else if (rule.ons.array[i].action == controller_rule_action_type_thaw_e) {
+          action = controller_thaw_s;
+        }
+
+        fl_print_format("      %[%r%] %r%r", main->program.output.to, main->program.context.set.important, controller_type_s, main->program.context.set.important, action, f_string_eol_s);
+      }
+
+      fl_print_format("      %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_need_s, main->program.context.set.important, f_string_eol_s);
+
+      for (j = 0; j < rule.ons.array[i].need.used; ++j) {
+
+        if (rule.ons.array[i].need.array[j].used) {
+          fl_print_format("        %Q%r", main->program.output.to, rule.ons.array[i].need.array[j], f_string_eol_s);
+        }
+      } // for
+
+      fl_print_format("      }%r      %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_want_s, main->program.context.set.important, f_string_eol_s);
+
+      for (j = 0; j < rule.ons.array[i].want.used; ++j) {
+
+        if (rule.ons.array[i].want.array[j].used) {
+          fl_print_format("        %Q%r", main->program.output.to, rule.ons.array[i].want.array[j], f_string_eol_s);
+        }
+      } // for
+
+      fl_print_format("      }%r      %[%r%] {%r", main->program.output.to, f_string_eol_s, main->program.context.set.important, controller_wish_s, main->program.context.set.important, f_string_eol_s);
+
+      for (j = 0; j < rule.ons.array[i].wish.used; ++j) {
+
+        if (rule.ons.array[i].wish.array[j].used) {
+          fl_print_format("        %Q%r", main->program.output.to, rule.ons.array[i].wish.array[j], f_string_eol_s);
+        }
+      } // for
+
+      fl_print_format("      }%r    }%r", main->program.output.to, f_string_eol_s, f_string_eol_s);
+    } // for
+
+    fl_print_format("  }%r", main->program.output.to, f_string_eol_s);
+
+    // Items.
+    if (rule.items.used) {
+      controller_rule_action_t *action = 0;
+      controller_rule_item_t *item = 0;
+      controller_rule_rerun_item_t *rerun_item = 0;
+
+      f_number_unsigned_t j = 0;
+      f_number_unsigned_t k = 0;
+      f_number_unsigned_t l = 0;
+
+      for (i = 0; i < rule.items.used; ++i) {
+
+        item = &rule.items.array[i];
+
+        fl_print_format("  %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_item_s, main->program.context.set.important, f_string_eol_s);
+
+        // Type.
+        fl_print_format("    %[%r%] %Q%r", main->program.output.to, main->program.context.set.important, controller_type_s, main->program.context.set.important, controller_rule_item_type_name(item->type), f_string_eol_s);
+
+        // Pid file.
+        fl_print_format("    %[%r%]", main->program.output.to, main->program.context.set.important, controller_pid_file_s, main->program.context.set.important);
+        if (item->pid_file.used) {
+          fl_print_format(" %Q", main->program.output.to, item->pid_file);
+        }
+        f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+        // With.
+        fl_print_format("    %[%r%]", main->program.output.to, main->program.context.set.important, controller_with_s, main->program.context.set.important);
+        if (item->with & controller_with_full_path_d) {
+          fl_print_format(" %r", main->program.output.to, controller_full_path_s);
+        }
+        if (item->with & controller_with_session_new_d) {
+          fl_print_format(" %r", main->program.output.to, controller_session_new_s);
+        }
+        if (item->with & controller_with_session_same_d) {
+          fl_print_format(" %r", main->program.output.to, controller_session_same_s);
+        }
+        f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+
+        // Actions.
+        for (j = 0; j < item->actions.used; ++j) {
+
+          action = &item->actions.array[j];
+
+          fl_print_format("    %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_action_s, main->program.context.set.important, f_string_eol_s);
+          fl_print_format("      %[%r%] %r%r", main->program.output.to, main->program.context.set.important, controller_type_s, main->program.context.set.important, controller_rule_action_type_name(action->type), f_string_eol_s);
+
+          if (item->type == controller_rule_item_type_script_e || item->type == controller_rule_item_type_utility_e) {
+            fl_print_format("      %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_parameter_s, main->program.context.set.important, f_string_eol_s);
+
+            if (action->parameters.used) {
+              if (action->parameters.array[0].used) {
+                f_print_terminated("        ", main->program.output.to);
+
+                for (k = 0; k < action->parameters.array[0].used; ++k) {
+
+                  if (action->parameters.array[0].string[k] == f_fss_eol_s.string[0]) {
+                    if (k + 1 < action->parameters.array[0].used) {
+                      fl_print_format("%r        ", main->program.output.to, f_string_eol_s);
+                    }
+                  }
+                  else {
+                    f_print_character_safely(action->parameters.array[0].string[k], main->program.output.to);
+                  }
+                } // for
+              }
+
+              f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+            }
+
+            fl_print_format("      }%r", main->program.output.to, f_string_eol_s);
+          }
+          else {
+            for (k = 0; k < action->parameters.used; ++k) {
+              fl_print_format("      %[%r%] %Q%r", main->program.output.to, main->program.context.set.important, controller_parameter_s, main->program.context.set.important, action->parameters.array[k], f_string_eol_s);
+            } // for
+          }
+
+          if (action->ikis.used) {
+            fl_print_format("      %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_iki_s, main->program.context.set.important, f_string_eol_s);
+
+            for (k = 0; k < action->ikis.used; ++k) {
+
+              for (l = 0; l < action->ikis.array[j].vocabulary.used; ++l) {
+
+                fl_print_format("        %[[%]%ul%[]%]", main->program.output.to, main->program.context.set.important, main->program.context.set.important, k, main->program.context.set.important, main->program.context.set.important);
+                fl_print_format(" %/Q %[:%] %/Q%r", main->program.output.to, action->parameters.array[k], action->ikis.array[k].vocabulary.array[l], main->program.context.set.important, main->program.context.set.important, action->parameters.array[k], action->ikis.array[k].content.array[l], f_string_eol_s);
+              } // for
+            } // for
+
+            fl_print_format("      }%r", main->program.output.to, f_string_eol_s);
+          }
+
+          fl_print_format("    }%r", main->program.output.to, f_string_eol_s);
+        } // for
+
+        // Rerun.
+        fl_print_format("    %[%r%] {%r", main->program.output.to, main->program.context.set.important, controller_rerun_s, main->program.context.set.important, f_string_eol_s);
+        for (j = 0; j < controller_rule_action_execute_type__enum_size_e; ++j) {
+
+          for (k = 0; k < 2; ++k) {
+            if (!k && (item->reruns[j].is & controller_rule_rerun_is_failure_d)) {
+              rerun_item = &item->reruns[j].failure;
+            }
+            else if (k && (item->reruns[j].is & controller_rule_rerun_is_success_d)) {
+              rerun_item = &item->reruns[j].success;
+            }
+            else {
+              rerun_item = 0;
+              continue;
+            }
+
+            fl_print_format("      %[", main->program.output.to, main->program.context.set.important);
+            switch (j) {
+              case controller_rule_action_execute_type_freeze_e:
+                f_print_dynamic_raw(controller_freeze_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_kill_e:
+                f_print_dynamic_raw(controller_kill_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_pause_e:
+                f_print_dynamic_raw(controller_pause_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_reload_e:
+                f_print_dynamic_raw(controller_reload_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_restart_e:
+                f_print_dynamic_raw(controller_restart_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_resume_e:
+                f_print_dynamic_raw(controller_resume_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_start_e:
+                f_print_dynamic_raw(controller_start_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_stop_e:
+                f_print_dynamic_raw(controller_stop_s, main->program.output.to);
+                break;
+
+              case controller_rule_action_execute_type_thaw_e:
+                f_print_dynamic_raw(controller_thaw_s, main->program.output.to);
+                break;
+
+              default:
+                break;
+            }
+
+            fl_print_format("%] %r", main->program.output.to, main->program.context.set.important, k ? controller_success_s : controller_failure_s);
+            fl_print_format(" %r %ul %r %ul", main->program.output.to, controller_delay_s, rerun_item->delay, controller_max_s, rerun_item->max);
+
+            if (!k && (item->reruns[j].is & controller_rule_rerun_is_failure_reset_d) || k && (item->reruns[j].is & controller_rule_rerun_is_success_reset_d)) {
+              fl_print_format(" %r", main->program.output.to, controller_reset_s);
+            }
+
+            f_print_dynamic_raw(f_string_eol_s, main->program.output.to);
+          } // for
+        } // for
+        fl_print_format("    }%r", main->program.output.to, f_string_eol_s);
+
+        fl_print_format("  }%r", main->program.output.to, f_string_eol_s);
+      } // for
+    }
+
+    fl_print_format("}%r", main->program.output.to, f_string_eol_s);
+
+    controller_unlock_print_flush(main->program.output.to, global->thread);
+  }
+#endif // _di_controller_rule_validate_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/validate.h b/sources/c/main/rule/validate.h
new file mode 100644 (file)
index 0000000..e8934ee
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "validate" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_validate_h
+#define _controller_main_rule_validate_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Perform a simulated execution of the given rule.
+ *
+ * This simply prints information about the rule.
+ *
+ * This automatically sets the rule's status to F_complete.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param rule
+ *   The rule to process.
+ * @param action
+ *   The action to perform based on the action type codes.
+ * @param options
+ *   A number using bits to represent specific boolean options.
+ *   If no bits set, then operate normally in a synchronous manner.
+ *   If bit controller_process_option_simulate_d, then the rule execution is in simulation mode (printing a message that the rule would be executed but does not execute the rule).
+ *   If bit controller_process_option_asynchronous_d, then run asynchronously.
+ * @param cache
+ *   A structure for containing and caching relevant data.
+ */
+#ifndef _di_controller_rule_validate_
+  extern void controller_rule_validate(controller_global_t * const global, const controller_rule_t rule, const uint8_t action, const uint8_t options, controller_cache_t * const cache);
+#endif // _di_controller_rule_validate_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_validate_h
diff --git a/sources/c/main/rule/wait.c b/sources/c/main/rule/wait.c
new file mode 100644 (file)
index 0000000..2f8e675
--- /dev/null
@@ -0,0 +1,221 @@
+#include "../controller.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_controller_rule_wait_all_
+  f_status_t controller_rule_wait_all(controller_global_t * const global, const bool is_normal, const bool required) {
+
+    if (!global) return F_status_set_error(F_parameter);
+
+    f_status_t status_lock = controller_lock_read(is_normal, global->thread, &global->thread->lock.instance);
+
+    if (F_status_is_error(status_lock)) {
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
+
+      return status_lock;
+    }
+
+    if (!global->thread->instances.used) {
+      f_thread_unlock(&global->thread->lock.instance);
+
+      return F_data_not;
+    }
+
+    f_status_t status = F_okay;
+
+    bool required_not_run = F_false;
+    bool skip = F_false;
+
+    f_number_unsigned_t i = 0;
+    f_number_unsigned_t j = 0;
+
+    // Vuild a list of what to wait for so that anything new after this point will not be waited for.
+    const f_number_unsigned_t instance_total = global->thread->instances.used;
+    controller_data_t *instance_list[instance_total];
+
+    for (; i < instance_total; ++i) {
+      instance_list[i] = global->thread->instances.array[i];
+    } // for
+
+    f_thread_unlock(&global->thread->lock.instance);
+
+    for (i = 0; i < instance_total; ++i) {
+
+      if (!controller_thread_is_enabled(is_normal, global->thread)) break;
+
+      // Re-establish global instance read lock to wait for or protect from the cleanup thread while checking the read instance.
+      status_lock = controller_lock_read(is_normal, global->thread, &global->thread->lock.instance);
+      if (F_status_is_error(status_lock)) break;
+
+      if (!instance_list[i]) {
+        f_thread_unlock(&global->thread->lock.instance);
+
+        continue;
+      }
+
+      status_lock = controller_lock_read(is_normal, global->thread, &instance_list[i]->active);
+
+      if (F_status_is_error(status_lock)) {
+        f_thread_unlock(&global->thread->lock.instance);
+
+        break;
+      }
+
+      // Once the active lock is obtained, then the main instance read lock can be safely released.
+      f_thread_unlock(&global->thread->lock.instance);
+
+      status_lock = controller_lock_read(is_normal, global->thread, &instance_list[i]->lock);
+
+      if (F_status_is_error(status_lock)) {
+        f_thread_unlock(&instance_list[i]->active);
+
+        break;
+      }
+
+      if (required) {
+        if (!(instance_list[i]->options & controller_instance_option_require_d)) {
+          f_thread_unlock(&instance_list[i]->lock);
+          f_thread_unlock(&instance_list[i]->active);
+
+          continue;
+        }
+      }
+
+      if (!instance_list[i]->state || instance_list[i]->state == controller_instance_state_idle_e || instance_list[i]->state == controller_instance_state_done_e) {
+
+        if (instance_list[i]->state == controller_instance_state_done_e) {
+          f_thread_unlock(&instance_list[i]->lock);
+
+          status_lock = controller_lock_write(is_normal, global->thread, &instance_list[i]->lock);
+
+          if (F_status_is_error(status_lock)) {
+            controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_false, global->thread);
+
+            f_thread_unlock(&instance_list[i]->active);
+
+            return status_lock;
+          }
+
+          if (instance_list[i]->state == controller_instance_state_done_e) {
+            f_thread_unlock(&instance_list[i]->active);
+
+            if (f_thread_lock_write_try(&instance_list[i]->active) == F_okay) {
+              controller_thread_join(&instance_list[i]->id_thread);
+
+              instance_list[i]->state = controller_instance_state_idle_e;
+
+              f_thread_unlock(&instance_list[i]->active);
+
+              f_thread_mutex_lock(&instance_list[i]->wait_lock);
+              f_thread_condition_signal_all(&instance_list[i]->wait);
+              f_thread_mutex_unlock(&instance_list[i]->wait_lock);
+            }
+
+            status_lock = controller_lock_read(is_normal, global->thread, &instance_list[i]->active);
+
+            if (F_status_is_error(status_lock)) {
+              f_thread_unlock(&instance_list[i]->lock);
+
+              break;
+            }
+          }
+
+          f_thread_unlock(&instance_list[i]->lock);
+
+          status_lock = controller_lock_read(is_normal, global->thread, &instance_list[i]->lock);
+          if (F_status_is_error(status_lock)) break;
+        }
+
+        if (instance_list[i]->options & controller_instance_option_require_d) {
+          if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
+            status = F_status_set_error(F_require);
+
+            f_thread_unlock(&instance_list[i]->lock);
+            f_thread_unlock(&instance_list[i]->active);
+
+            break;
+          }
+          else if (controller_rule_status_is_available(instance_list[i]->action, instance_list[i]->rule)) {
+            required_not_run = F_true;
+          }
+        }
+
+        f_thread_unlock(&instance_list[i]->lock);
+        f_thread_unlock(&instance_list[i]->active);
+
+        if (F_status_set_fine(status) == F_require) break;
+
+        continue;
+      }
+
+      if (!controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule) && (instance_list[i]->state == controller_instance_state_active_e || instance_list[i]->state == controller_instance_state_busy_e)) {
+        f_thread_unlock(&instance_list[i]->lock);
+
+        status = controller_instance_wait(global, instance_list[i]);
+
+        if (F_status_set_fine(status) == F_interrupt) {
+          f_thread_unlock(&instance_list[i]->active);
+
+          break;
+        }
+
+        status_lock = controller_lock_read(is_normal, global->thread, &instance_list[i]->lock);
+
+        if (F_status_is_error(status_lock)) {
+          f_thread_unlock(&instance_list[i]->active);
+
+          break;
+        }
+
+        if ((instance_list[i]->options & controller_instance_option_require_d)) {
+          f_thread_unlock(&instance_list[i]->lock);
+
+          if (controller_rule_status_is_error(instance_list[i]->action, instance_list[i]->rule)) {
+            status = F_status_set_error(F_require);
+
+            f_thread_unlock(&instance_list[i]->active);
+
+            break;
+          }
+        }
+        else {
+          f_thread_unlock(&instance_list[i]->lock);
+        }
+      }
+      else {
+        f_thread_unlock(&instance_list[i]->lock);
+      }
+
+      f_thread_unlock(&instance_list[i]->active);
+
+      if (F_status_set_fine(status) == F_interrupt || F_status_set_fine(status) == F_require) break;
+    } // for
+
+    if (F_status_is_error(status_lock)) {
+      controller_lock_print_error_critical(&global->main->program.error, F_status_set_fine(status_lock), F_true, global->thread);
+
+      return status_lock;
+    }
+
+    if (!controller_thread_is_enabled(is_normal, global->thread)) return F_status_set_error(F_interrupt);
+    if (F_status_set_fine(status) == F_require) return status;
+    if (required_not_run) return F_require;
+
+    return F_okay;
+  }
+#endif // _di_controller_rule_wait_all_
+
+#ifndef _di_controller_rule_wait_all_instance_type_
+  f_status_t controller_rule_wait_all_instance_type(controller_global_t * const global, const uint8_t type, const bool required) {
+
+    if (!global) return F_status_set_error(F_parameter);
+
+    return controller_rule_wait_all(global, type != controller_data_type_exit_e, required);
+  }
+#endif // _di_controller_rule_wait_all_instance_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/sources/c/main/rule/wait.h b/sources/c/main/rule/wait.h
new file mode 100644 (file)
index 0000000..9c2f474
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * FLL - Level 3
+ *
+ * Project: Controller
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Provides the rule "wait" functionality.
+ *
+ * This is auto-included and should not need to be explicitly included.
+ */
+#ifndef _controller_main_rule_wait_h
+#define _controller_main_rule_wait_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Wait until all currently running Rule processes are complete.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param is_normal
+ *   If TRUE, then process as if this operates during a normal operation (entry and control).
+ *   If FALSE, then process as if this operates during a an exit operation.
+ *   This is ignored when caller is not NULL.
+ * @param required
+ *   If TRUE, then only process required rules and if a required rule has failed, return.
+ *   If FALSE, process all waits, returning normally (required rules still result in failure).
+ *
+ * @return
+ *   F_okay on success.
+ *   F_data_not on success and nothing to do.
+ *   F_require on success, but a required rule has not been run yet.
+ *
+ *   F_interrupt (with error bit) on receiving a process signal, such as an interrupt signal.
+ *   F_require (with error bit set) if a required process is in failed status when required is TRUE.
+ */
+#ifndef _di_controller_rule_wait_all_
+  extern f_status_t controller_rule_wait_all(controller_global_t * const global, const bool is_normal, const bool required);
+#endif // _di_controller_rule_wait_all_
+
+/**
+ * Wait until all currently running Rule processes are complete for some process type.
+ *
+ * @param global
+ *   The global data.
+ *   Must not be NULL.
+ *
+ *   This does not alter global.main.setting.state.status.
+ * @param type
+ *   The process type to use when checking if thread is enabled.
+ * @param required
+ *   If TRUE, then only process required rules and if a required rule has failed, return.
+ *   If FALSE, process all waits, returning normally.
+ *
+ * @return
+ *   Success from controller_rule_wait_all().
+ *
+ *   Errors (with error bit) from: controller_rule_wait_all().
+ *
+ * @see controller_rule_wait_all()
+ */
+#ifndef _di_controller_rule_wait_all_instance_type_
+  extern f_status_t controller_rule_wait_all_instance_type(controller_global_t * const global, const uint8_t type, const bool required);
+#endif // _di_controller_rule_wait_all_instance_type_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _controller_main_rule_wait_h
index f7fff4c270bae238bb8870985f05e83eec1cdd4d..95461ecdc4410d6410c725c7798ca0d1fb76788c 100644 (file)
@@ -24,6 +24,7 @@ extern "C" {
  *   If FALSE, then instance as if this operates during a an exit operation.
  * @param thread
  *   The thread data.
+ *   Must not be NULL.
  *
  * @return
  *   F_true when enabled.
@@ -38,8 +39,10 @@ extern "C" {
  *
  * @param instance
  *   The instance to use when checking if thread is enabled.
+ *   Must not be NULL.
  * @param thread
  *   The thread data.
+ *   Must not be NULL.
  *
  * @return
  *   F_true when enabled.
@@ -51,6 +54,24 @@ extern "C" {
   extern f_status_t controller_main_thread_is_enabled_instance(controller_instance_t * const instance, controller_thread_t * const thread);
 #endif // _di_controller_main_thread_is_enabled_instance_
 
+/**
+ * Check to see if thread is enabled for the normal operations like entry and control or for exit operations for some instance type.
+ *
+ * @param type
+ *   The instance type to use when checking if thread is enabled.
+ * @param thread
+ *   The thread data.
+ *   Must not be NULL.
+ *
+ * @return
+ *   Success from controller_main_thread_is_enabled().
+ *
+ * @see controller_main_thread_is_enabled()
+ */
+#ifndef _di_controller_main_thread_is_enabled_instance_type_
+  extern f_status_t controller_main_thread_is_enabled_instance_type(const uint8_t type, controller_thread_t * const thread);
+#endif // _di_controller_main_thread_is_enabled_instance_type_
+
 #ifdef __cplusplus
 } // extern "C"
 #endif