]> Kevux Git Server - fll/commitdiff
Progress: featureless make and related
authorKevin Day <thekevinday@gmail.com>
Thu, 30 Jul 2020 03:26:14 +0000 (22:26 -0500)
committerKevin Day <thekevinday@gmail.com>
Thu, 30 Jul 2020 03:26:14 +0000 (22:26 -0500)
It turns out I need custom functions for processing the mode like the chmod program works.
During the process of writing this I discovered several different ways chmod accepts arguments that I wasn't aware.
As a result I changed my logic repeatedly and this code is in a very much incomplete state.

This is being committed primarily to avoid losing any data (more so than usual).
There will be a lot of clean up when I get back to completing this.

19 files changed:
build/level_0/settings
build/monolithic/settings
build/scripts/bootstrap-example.sh
level_0/f_account/c/account.c [new file with mode: 0644]
level_0/f_account/c/account.h [new file with mode: 0644]
level_0/f_account/data/build/defines [new file with mode: 0644]
level_0/f_account/data/build/dependencies [new file with mode: 0644]
level_0/f_account/data/build/settings [new file with mode: 0644]
level_0/f_file/c/file.c
level_0/f_file/c/file.h
level_0/f_pipe/c/pipe.h
level_3/fake/c/fake.h
level_3/fake/c/private-make.c
level_3/fake/c/private-make.h
level_3/fake/data/build/dependencies
level_3/fake/data/build/fakefile
level_3/fake/data/build/settings
level_3/fake/documents/fakefile.txt
level_3/fake/specifications/fakefile.txt

index c9c7e113fa5bb607669d37b73916d86a07e31d8f..6f9dd388cac7d250cee8f16452ccbc5200297971 100644 (file)
@@ -20,9 +20,9 @@ build_language c
 build_linker ar
 build_libraries -lc
 build_libraries-level
-build_sources_library console.c conversion.c directory.c private-directory.c environment.c private-environment.c file.c private-file.c fss.c iki.c memory.c path.c private-path.c pipe.c print.c serialize.c private-serialize.c socket.c utf.c private-utf.c
+build_sources_library account.c console.c conversion.c directory.c private-directory.c environment.c private-environment.c file.c private-file.c fss.c iki.c memory.c path.c private-path.c pipe.c print.c serialize.c private-serialize.c socket.c utf.c private-utf.c
 build_sources_program
-build_sources_headers color.h console.h conversion.h directory.h directory_type.h environment.h file.h fss.h fss-common.h fss-named.h fss-nest.h fss-quoted.h fss-set.h iki.h iki-common.h memory.h memory-structure.h path.h pipe.h print.h serialize.h socket.h status.h status_array.h string.h string_common.h string_dynamic.h string_map.h string_quantity.h string_range.h type.h type_array.h utf.h utf-common.h
+build_sources_headers account.h color.h console.h conversion.h directory.h directory_type.h environment.h file.h fss.h fss-common.h fss-named.h fss-nest.h fss-quoted.h fss-set.h iki.h iki-common.h memory.h memory-structure.h path.h pipe.h print.h serialize.h socket.h status.h status_array.h string.h string_common.h string_dynamic.h string_map.h string_quantity.h string_range.h type.h type_array.h utf.h utf-common.h
 build_sources_script
 build_sources_setting
 build_script yes
index bce523dada21bc17a0e2e7b0b22eb93f119ea5e8..88b8e738a6ee39d16b16bd8a33c86698135844e3 100644 (file)
@@ -20,9 +20,9 @@ build_language c
 build_linker ar
 build_libraries -lc
 build_libraries-monolithic
-build_sources_library level_0/console.c level_0/conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/private-file.c level_0/fss.c level_0/iki.c level_0/memory.c level_0/path.c level_0/private-path.c level_0/pipe.c level_0/print.c level_0/serialize.c level_0/private-serialize.c level_0/socket.c level_0/utf.c level_0/private-utf.c level_1/color.c level_1/console.c level_1/conversion.c level_1/directory.c level_1/private-directory.c level_1/environment.c level_1/private-fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/iki.c level_1/print.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/fss.c level_2/private-fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/path.c level_2/program.c level_2/status.c
+build_sources_library level_0/account.c level_0/console.c level_0/conversion.c level_0/directory.c level_0/private-directory.c level_0/environment.c level_0/private-environment.c level_0/file.c level_0/private-file.c level_0/fss.c level_0/iki.c level_0/memory.c level_0/path.c level_0/private-path.c level_0/pipe.c level_0/print.c level_0/serialize.c level_0/private-serialize.c level_0/socket.c level_0/utf.c level_0/private-utf.c level_1/color.c level_1/console.c level_1/conversion.c level_1/directory.c level_1/private-directory.c level_1/environment.c level_1/private-fss.c level_1/fss_basic.c level_1/fss_basic_list.c level_1/fss_extended.c level_1/fss_extended_list.c level_1/iki.c level_1/print.c level_1/status.c level_1/string.c level_1/private-string.c level_1/utf.c level_1/private-utf.c level_1/utf_file.c level_1/private-utf_file.c level_2/execute.c level_2/private-execute.c level_2/file.c level_2/fss.c level_2/private-fss.c level_2/fss_basic.c level_2/fss_basic_list.c level_2/fss_extended.c level_2/fss_extended_list.c level_2/fss_status.c level_2/path.c level_2/program.c level_2/status.c
 build_sources_program
-build_sources_headers level_0/color.h level_0/console.h level_0/conversion.h level_0/directory.h level_0/directory_type.h level_0/environment.h level_0/file.h level_0/fss.h level_0/fss-common.h level_0/fss-named.h level_0/fss-nest.h level_0/fss-quoted.h level_0/fss-set.h level_0/iki.h level_0/iki-common.h level_0/memory.h level_0/memory-structure.h level_0/path.h level_0/pipe.h level_0/print.h level_0/serialize.h level_0/socket.h level_0/status.h level_0/status_array.h level_0/string.h level_0/string_common.h level_0/string_dynamic.h level_0/string_map.h level_0/string_quantity.h level_0/string_range.h level_0/type.h level_0/type_array.h level_0/utf.h level_0/utf-common.h level_1/color.h level_1/console.h level_1/conversion.h level_1/directory.h level_1/environment.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_macro.h level_1/fss_status.h level_1/iki.h level_1/print.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/path.h level_2/program.h level_2/status.h
+build_sources_headers level_0/account.h level_0/color.h level_0/console.h level_0/conversion.h level_0/directory.h level_0/directory_type.h level_0/environment.h level_0/file.h level_0/fss.h level_0/fss-common.h level_0/fss-named.h level_0/fss-nest.h level_0/fss-quoted.h level_0/fss-set.h level_0/iki.h level_0/iki-common.h level_0/memory.h level_0/memory-structure.h level_0/path.h level_0/pipe.h level_0/print.h level_0/serialize.h level_0/socket.h level_0/status.h level_0/status_array.h level_0/string.h level_0/string_common.h level_0/string_dynamic.h level_0/string_map.h level_0/string_quantity.h level_0/string_range.h level_0/type.h level_0/type_array.h level_0/utf.h level_0/utf-common.h level_1/color.h level_1/console.h level_1/conversion.h level_1/directory.h level_1/environment.h level_1/fss.h level_1/fss_basic.h level_1/fss_basic_list.h level_1/fss_extended.h level_1/fss_extended_list.h level_1/fss_macro.h level_1/fss_status.h level_1/iki.h level_1/print.h level_1/status.h level_1/string.h level_1/utf.h level_1/utf_file.h level_2/execute.h level_2/file.h level_2/fss.h level_2/fss_basic.h level_2/fss_basic_list.h level_2/fss_extended.h level_2/fss_extended_list.h level_2/fss_status.h level_2/path.h level_2/program.h level_2/status.h
 build_sources_script
 build_sources_setting
 build_script yes
index 7b66ff1e370ba0523075e277dbf0ce3401d7f0e3..0b32a1aba51d8d10efb0fbe0e9a6f2651593e9a5 100644 (file)
@@ -7,7 +7,7 @@
 # Instead this provides a functional example on what commands to perform to perform the bootstrap.
 #
 # This only accepts a two arguments (both are required):
-# 1) One of "individual", "level", "monolithic", "fake-individual", "fake-level", or "fake-monolithic"..
+# 1) One of "individual", "level", "monolithic", "fake-individual", "fake-level", or "fake-monolithic".
 # 2) The version number of the project, such as "0.5.0".
 #
 # This will create a directory at he present working directory of the script caller called "fll" where everything will be installed.
@@ -23,7 +23,7 @@ if [[ $1 == "individual" ]] ; then
   bash build/scripts/package.sh build -i
 
   if [[ $? -eq 0 ]] ; then
-    for i in f_type f_status f_memory f_string f_utf f_color f_console f_conversion f_directory f_environment f_file f_fss f_iki f_path f_pipe f_print f_serialize f_socket fl_color fl_console fl_conversion fl_directory fl_environment fl_fss fl_iki fl_print fl_status fl_string fl_utf fl_utf_file fll_execute fll_file fll_fss fll_path fll_program fll_status ; do
+    for i in f_type f_status f_memory f_string f_utf f_account f_color f_console f_conversion f_directory f_environment f_file f_fss f_iki f_path f_pipe f_print f_serialize f_socket fl_color fl_console fl_conversion fl_directory fl_environment fl_fss fl_iki fl_print fl_status fl_string fl_utf fl_utf_file fll_execute fll_file fll_fss fll_path fll_program fll_status ; do
       echo && echo "Processing $i." &&
 
       cd package/individual/$i-$2/ &&
diff --git a/level_0/f_account/c/account.c b/level_0/f_account/c/account.c
new file mode 100644 (file)
index 0000000..b0974b7
--- /dev/null
@@ -0,0 +1,159 @@
+#include <level_0/account.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _di_f_account_id_group_by_name_
+  f_return_status f_account_id_group_by_name(const f_string name, gid_t *id) {
+    f_status status = F_none;
+
+    const size_t length_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+    // note: pointer seems pointless except that it is used to determine if the name was found.
+    struct group grp;
+    struct group *pointer;
+
+    size_t length = length_max;
+
+    {
+      if (length == -1) {
+        length = f_account_pwd_length_fallback_first;
+      }
+
+      // must be set to 0 to avoid problems due to the design of getgrnam()/getgrnam_r().
+      errno = 0;
+
+      char buffer[length];
+      int result = getgrnam_r(name, &grp, buffer, length, &pointer);
+
+      if (result) {
+        if (errno == EINTR) return F_status_set_error(F_interrupted);
+        if (errno == EIO) return F_status_set_error(F_input_output);
+        if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
+        if (errno == ENFILE) return F_status_set_error(F_file_open_max);
+        if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+
+        if (errno == ERANGE) {
+          if (length_max > 0) return F_status_set_error(F_buffer_too_small);
+        }
+        else {
+          return F_status_set_error(F_failure);
+        }
+      }
+      else {
+        if (!pointer) {
+          return F_exist_not;
+        }
+
+        *id = grp.gr_gid;
+
+        return F_none;
+      }
+    }
+
+    length = f_account_pwd_length_fallback_second;
+
+    char buffer[f_account_pwd_length_fallback_second];
+
+    int result = getgrnam_r(name, &grp, buffer, length, &pointer);
+
+    if (result) {
+      if (errno == EINTR) return F_status_set_error(F_interrupted);
+      if (errno == EIO) return F_status_set_error(F_input_output);
+      if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
+      if (errno == ENFILE) return F_status_set_error(F_file_open_max);
+      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+      if (errno == ERANGE) return F_status_set_error(F_buffer_too_small);
+
+      return F_status_set_error(F_failure);
+    }
+
+    if (!pointer) {
+      return F_exist_not;
+    }
+
+    *id = grp.gr_gid;
+
+    return F_none;
+  }
+#endif // _di_f_account_id_group_by_name_
+
+#ifndef _di_f_account_id_user_by_name_
+  f_return_status f_account_id_user_by_name(const f_string name, uid_t *id) {
+    f_status status = F_none;
+
+    const size_t length_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+
+    // note: pointer seems pointless except that it is used to determine if the name was found.
+    struct passwd password;
+    struct passwd *pointer;
+
+    size_t length = length_max;
+
+    {
+      if (length == -1) {
+        length = f_account_pwd_length_fallback_first;
+      }
+
+      // must be set to 0 to avoid problems due to the design of getpwnam()/getpwnam_r().
+      errno = 0;
+
+      char buffer[length];
+      int result = getpwnam_r(name, &password, buffer, length, &pointer);
+
+      if (result) {
+        if (errno == EINTR) return F_status_set_error(F_interrupted);
+        if (errno == EIO) return F_status_set_error(F_input_output);
+        if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
+        if (errno == ENFILE) return F_status_set_error(F_file_open_max);
+        if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+
+        if (errno == ERANGE) {
+          if (length_max > 0) return F_status_set_error(F_buffer_too_small);
+        }
+        else {
+          return F_status_set_error(F_failure);
+        }
+      }
+      else {
+        if (!pointer) {
+          return F_exist_not;
+        }
+
+        *id = password.pw_uid;
+
+        return F_none;
+      }
+    }
+
+    length = f_account_pwd_length_fallback_second;
+
+    char buffer[f_account_pwd_length_fallback_second];
+
+    int result = getpwnam_r(name, &password, buffer, length, &pointer);
+
+    if (result) {
+      if (errno == EINTR) return F_status_set_error(F_interrupted);
+      if (errno == EIO) return F_status_set_error(F_input_output);
+      if (errno == EMFILE) return F_status_set_error(F_file_descriptor_max);
+      if (errno == ENFILE) return F_status_set_error(F_file_open_max);
+      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+      if (errno == ERANGE) return F_status_set_error(F_buffer_too_small);
+
+      return F_status_set_error(F_failure);
+    }
+
+    if (!pointer) {
+      return F_exist_not;
+    }
+
+    *id = password.pw_uid;
+
+    return F_none;
+  }
+#endif // _di_f_account_id_user_by_name_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_account/c/account.h b/level_0/f_account/c/account.h
new file mode 100644 (file)
index 0000000..a06397a
--- /dev/null
@@ -0,0 +1,99 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Pipe
+ * API Version: 0.5
+ * Licenses: lgplv2.1
+ *
+ * Provides account related functionality (users, groups, roles, etc..).
+ *
+ * @todo POSIX as of POSIX.1-2001, at least getpwnam() does not call "not found" an error.
+ *       For now, using getpwnam_r() in place of getpwnam() (and similar) seems the safest way to avoid this bad design.
+ */
+#ifndef _F_account_h
+#define _F_account_h
+
+// libc includes
+#include <sys/types.h>
+#include <grp.h>
+#include <pwd.h>
+#include <unistd.h>
+
+// fll-0 includes
+#include <level_0/type.h>
+#include <level_0/status.h>
+#include <level_0/memory.h>
+#include <level_0/string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Custom defines for f_account.
+ *
+ * f_account_pwd_length_fallback_first: provide a fallback max length for when sysconf(_SC_GETPW_R_SIZE_MAX) return -1 (aka: infinite).
+ * f_account_pwd_length_fallback_second: provide a fallback max length for when f_account_pwd_length_fallback_first is too small.
+ */
+#ifndef _di_f_account_defines_
+  #define f_account_pwd_length_fallback_first  8192
+  #define f_account_pwd_length_fallback_second 32767
+#endif // _di_f_account_defines_
+
+/**
+ * Get the group account id by the group name.
+ *
+ * @param name
+ *   The group name.
+ * @param id
+ *   The id associated with the given name.
+ *
+ * @return
+ *   F_none on success.
+ *   F_buffer_too_small (with error bit) if the buffer is too small to store the account data.
+ *   F_exist_non if no group by that name exists.
+ *   F_file_descriptor_max (with error bit) if max file descriptors was reached.
+ *   F_file_open_max (with error bit) too many open files.
+ *   F_input_output (with error bit) if an I/O error occurred.
+ *   F_interrupted (with error bit) when program received an interrupt signal, halting operation.
+ *   F_memory_out (with error bit) if out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_failure (with error bit) on any other failure.
+ *
+ * @see getgrnam_r()
+ */
+#ifndef _di_f_account_id_group_by_name_
+  extern f_return_status f_account_id_group_by_name(const f_string name, gid_t *id);
+#endif // _di_f_account_id_group_by_name_
+
+/**
+ * Get the user account id by the user name.
+ *
+ * @param name
+ *   The user name.
+ * @param id
+ *   The id associated with the given name.
+ *
+ * @return
+ *   F_none on success.
+ *   F_buffer_too_small (with error bit) if the buffer is too small to store the account data.
+ *   F_exist_non if no user by that name exists.
+ *   F_file_descriptor_max (with error bit) if max file descriptors was reached.
+ *   F_file_open_max (with error bit) too many open files.
+ *   F_input_output (with error bit) if an I/O error occurred.
+ *   F_interrupted (with error bit) when program received an interrupt signal, halting operation.
+ *   F_memory_out (with error bit) if out of memory.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_failure (with error bit) on any other failure.
+ *
+ * @see getpwnam_r()
+ */
+#ifndef _di_f_account_id_user_by_name_
+  extern f_return_status f_account_id_user_by_name(const f_string name, uid_t *id);
+#endif // _di_f_account_id_user_by_name_
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _F_account_h
diff --git a/level_0/f_account/data/build/defines b/level_0/f_account/data/build/defines
new file mode 100644 (file)
index 0000000..c665317
--- /dev/null
@@ -0,0 +1,2 @@
+# fss-0000
+
diff --git a/level_0/f_account/data/build/dependencies b/level_0/f_account/data/build/dependencies
new file mode 100644 (file)
index 0000000..d798245
--- /dev/null
@@ -0,0 +1,6 @@
+# fss-0000
+
+f_type
+f_status
+f_memory
+f_string
diff --git a/level_0/f_account/data/build/settings b/level_0/f_account/data/build/settings
new file mode 100644 (file)
index 0000000..82ded25
--- /dev/null
@@ -0,0 +1,52 @@
+# fss-0001
+
+project_name f_account
+
+version_major 0
+version_minor 5
+version_micro 0
+version_target major
+
+environment
+
+process_pre
+process_post
+
+modes individual
+modes_default individual
+
+build_compiler gcc
+build_language c
+build_linker ar
+build_libraries -lc
+build_libraries-individual
+build_sources_library account.c
+build_sources_program
+build_sources_headers account.h
+build_sources_script
+build_sources_setting
+build_script yes
+build_shared yes
+build_static yes
+
+path_headers level_0
+path_library_script script
+path_library_shared shared
+path_library_static static
+path_program_script script
+path_program_shared shared
+path_program_static static
+
+search_exclusive yes
+search_shared yes
+search_static yes
+
+defines_all
+defines_static
+defines_shared
+
+flags_all -z now -g
+flags_shared
+flags_static
+flags_library -fPIC
+flags_program -fPIE
index d110ec183f0fc8f830ea7cbc60873eae7cb9478d..03efa956d1e20a1d4b8b57edd32fd81164953945 100644 (file)
@@ -399,6 +399,65 @@ extern "C" {
   }
 #endif // _di_f_file_flush_
 
+#ifndef _di_f_file_is_
+  f_return_status f_file_is(const f_string path, const int type) {
+    #ifndef _di_level_0_parameter_checking_
+      if (path == 0) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    struct stat file_stat;
+
+    memset(&file_stat, 0, sizeof(struct stat));
+
+    if (stat(path, &file_stat) < 0) {
+      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
+      if (errno == EFAULT) return F_status_set_error(F_buffer);
+      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
+      if (errno == ENOTDIR) return F_status_set_error(F_directory);
+      if (errno == ENOENT) return F_file_found_not;
+      if (errno == EACCES) return F_status_set_error(F_access_denied);
+      if (errno == ELOOP) return F_status_set_error(F_loop);
+
+      return F_status_set_error(F_file_stat);
+    }
+
+    if (f_macro_file_type_get(file_stat.st_mode) == type) return F_true;
+
+    return F_false;
+  }
+#endif // _di_f_file_is_
+
+#ifndef _di_f_file_is_at_
+  f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const int flag) {
+    #ifndef _di_level_0_parameter_checking_
+      if (path == 0) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    struct stat file_stat;
+
+    memset(&file_stat, 0, sizeof(struct stat));
+
+    if (fstatat(at_id, path, &file_stat, flag) < 0) {
+      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
+      if (errno == EFAULT) return F_status_set_error(F_buffer);
+      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
+      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
+      if (errno == ENOTDIR) return F_status_set_error(F_directory);
+      if (errno == ENOENT) return F_file_found_not;
+      if (errno == EACCES) return F_status_set_error(F_access_denied);
+      if (errno == ELOOP) return F_status_set_error(F_loop);
+      if (errno == EBADF) return F_status_set_error(F_directory_descriptor);
+
+      return F_status_set_error(F_file_stat);
+    }
+
+    if (file_stat.st_mode == (S_IFMT & S_IFDIR)) return F_true;
+
+    return F_false;
+  }
+#endif // _di_f_file_is_at_
+
 #ifndef _di_f_file_link_
   f_return_status f_file_link(const f_string target, const f_string point) {
     #ifndef _di_level_0_parameter_checking_
@@ -509,64 +568,321 @@ extern "C" {
   }
 #endif // _di_f_file_link_read_at_
 
-#ifndef _di_f_file_is_
-  f_return_status f_file_is(const f_string path, const int type) {
+#ifndef _di_f_file_mode_from_string_
+  f_return_status f_file_mode_from_string(const f_string string, f_file_mode *mode, uint8_t *replace) {
     #ifndef _di_level_0_parameter_checking_
-      if (path == 0) return F_status_set_error(F_parameter);
+      if (string == 0) return F_status_set_error(F_parameter);
+      if (string[0] == 0) return F_status_set_error(F_parameter);
+      if (mode == 0) return F_status_set_error(F_parameter);
+      if (replace == 0) return F_status_set_error(F_parameter);
     #endif // _di_level_0_parameter_checking_
 
-    struct stat file_stat;
-
-    memset(&file_stat, 0, sizeof(struct stat));
+    uint8_t syntax = 0;
+
+    *mode = 0;
+    *replace = 0;
+
+    switch (string[0]) {
+
+      case '+':
+      case '-':
+      case '=':
+        switch (string[1]) {
+
+          case 'r':
+          case 'w':
+          case 'x':
+          case 'X':
+          case 's':
+          case 't':
+            syntax = 1;
+            break;
+
+          case '0':
+          case '1':
+          case '2':
+          case '3':
+          case '4':
+          case '5':
+          case '6':
+          case '7':
+            syntax = 2;
+            break;
+
+          default:
+            return F_status_set_error(F_syntax);
+        }
 
-    if (stat(path, &file_stat) < 0) {
-      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
-      if (errno == EFAULT) return F_status_set_error(F_buffer);
-      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
-      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
-      if (errno == ENOTDIR) return F_status_set_error(F_directory);
-      if (errno == ENOENT) return F_file_found_not;
-      if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == ELOOP) return F_status_set_error(F_loop);
+        syntax = 1;
+        break;
+
+      case 'u':
+      case 'g':
+      case 'o':
+      case 'a':
+        syntax = 1;
+        break;
+
+      case '0':
+      case '1':
+      case '2':
+      case '3':
+      case '4':
+      case '5':
+      case '6':
+      case '7':
+        syntax = 2;
+        break;
+
+      default:
+        return F_status_set_error(F_syntax);
+    }
 
-      return F_status_set_error(F_file_stat);
+    if (syntax == 1) {
+      uint8_t on = 0; // 1 = user, 2 = group, 4 = world/sticky, 7 = all.
+      uint8_t how = 0; // 1 = add, 2 = replace, 3 = subtract, 4 = umask add, 5 = umask replace, 6 = umask subtract.
+      bool active = F_false;
+
+      f_file_mode mask = 0;
+      f_file_mode what = 0;
+
+      for (f_string_length i = 0; syntax && i < string[i]; i++) {
+
+        switch (string[i]) {
+          case 'o':
+            if (active) {
+              syntax = 0;
+              break;
+            }
+
+            on = 1;
+            mask |= f_file_mode_block_world;
+            break;
+
+          case 'g':
+            if (active) {
+              syntax = 0;
+              break;
+            }
+
+            on = 2;
+            mask |= f_file_mode_block_group;
+            break;
+
+          case 'u':
+            if (active) {
+              syntax = 0;
+              break;
+            }
+
+            on = 4;
+            mask |= f_file_mode_block_owner;
+            break;
+
+          case 'a':
+            if (active) {
+              syntax = 0;
+              break;
+            }
+
+            on = 7;
+            mask = f_file_mode_block_owner | f_file_mode_block_group | f_file_mode_block_world;
+            break;
+
+          case '+':
+          case '-':
+          case '=':
+            active = F_true;
+
+            if (string[i] == '+') {
+              how = on ? 1 : 4;
+            }
+            else if (string[i] == '-') {
+              how = on ? 3 : 6;
+            }
+            else {
+              how = on ? 2 : 5;
+
+              // only the parts designated by the mask should be replaced.
+              *mode -= (*mode) & mask;
+            }
+
+            if (!on) {
+              on = 7;
+              mask = f_file_mode_block_owner | f_file_mode_block_group | f_file_mode_block_world;
+            }
+
+            for (i++; i < string[i]; i++) {
+
+              if (string[i] == 'r') {
+                what = f_file_mode_mask_bit_read;
+              }
+              else if (string[i] == 'w') {
+                what = f_file_mode_mask_bit_write;
+              }
+              else if (string[i] == 'x') {
+                what = f_file_mode_mask_bit_execute;
+              }
+              else if (string[i] == 'X') {
+                what = f_file_mode_mask_bit_execute_only;
+              }
+              else if (string[i] == 's') {
+                if (on & 4) {
+                  what = f_file_mode_mask_bit_set_owner;
+                }
+                else if (on & 2) {
+                  what = f_file_mode_mask_bit_set_group;
+                }
+                else {
+                  what = 0;
+                }
+              }
+              else if (string[i] == 't') {
+                if (on & 1) {
+                  what = f_file_mode_mask_bit_sticky;
+                }
+                else {
+                  what = 0;
+                }
+              }
+              else if (string[i] == ',') {
+                active = F_false;
+                on = 0;
+                how = 0;
+                mask = 0;
+                break;
+              }
+              else if (string[i] == '+' || string[i] == '-' || string[i] == '=') {
+                // have the outer loop resume at this character after it increments.
+                i--;
+                break;
+              }
+              else {
+                syntax = 0;
+                break;
+              }
+
+              if (how == 1 || how == 2) {
+                *mode |= what & mask & f_file_mode_mask_how_add;
+              }
+              else if (how == 3) {
+                *mode |= what & mask & f_file_mode_mask_how_subtract;
+              }
+              else if (how == 4 || how == 5) {
+                *mode |= what & mask & f_file_mode_mask_how_umask_add;
+              }
+              else if (how == 6) {
+                *mode |= what & mask & f_file_mode_mask_how_umask_subtract;
+              }
+            } // for
+
+            break;
+
+        default:
+          syntax = 0;
+          break;
+        }
+      } // for
     }
+    else if (syntax == 2) {
+      // 1 = add, 2 = replace, 3 = subtract, 4 = umask add, 5 = umask replace, 6 = umask subtract.
+      uint8_t how = 0;
 
-    if (f_macro_file_type_get(file_stat.st_mode) == type) return F_true;
+      mode_t classic = 0;
 
-    return F_false;
-  }
-#endif // _di_f_file_is_
+      f_string_length i = 0;
 
-#ifndef _di_f_file_is_at_
-  f_return_status f_file_is_at(const int at_id, const f_string path, const int type, const int flag) {
-    #ifndef _di_level_0_parameter_checking_
-      if (path == 0) return F_status_set_error(F_parameter);
-    #endif // _di_level_0_parameter_checking_
+      if (string[0] == '+') {
+        how = 1;
+        i = 1;
+      }
+      else if (string[0] == '-') {
+        how = 3;
+        i = 1;
+      }
+      else if (string[0] == '=' || string[0] == '0') {
+        how = 2;
+        i = 1;
+      }
+      else {
+        how = 5;
+      }
 
-    struct stat file_stat;
+      for (; string[i] == '0'; i++) {
+        // seek past leading '0's.
+      } // for
 
-    memset(&file_stat, 0, sizeof(struct stat));
+      if (string[i]) {
+        f_string_length j = 0;
 
-    if (fstatat(at_id, path, &file_stat, flag) < 0) {
-      if (errno == ENAMETOOLONG) return F_status_set_error(F_name);
-      if (errno == EFAULT) return F_status_set_error(F_buffer);
-      if (errno == ENOMEM) return F_status_set_error(F_memory_out);
-      if (errno == EOVERFLOW) return F_status_set_error(F_number_overflow);
-      if (errno == ENOTDIR) return F_status_set_error(F_directory);
-      if (errno == ENOENT) return F_file_found_not;
-      if (errno == EACCES) return F_status_set_error(F_access_denied);
-      if (errno == ELOOP) return F_status_set_error(F_loop);
-      if (errno == EBADF) return F_status_set_error(F_directory_descriptor);
+        for (; string[i + j] && j < 4; j++) {
 
-      return F_status_set_error(F_file_stat);
+          if (j) {
+            classic <<= 3;
+          }
+
+          switch (string[i]) {
+            case '0':
+              // already is a zero.
+              break;
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+              // this assumes ASCII/UTF-8.
+              classic |= string[i] - 0x30;
+
+              break;
+
+            default:
+              j = 4;
+              break;
+          }
+        } // for
+
+        if (j == 4) {
+          syntax = 0;
+        }
+
+        // @fixme: classic is a different structure than mode masks, properly expand. (maybe just use f_file_mode instead of classic and shift by 6 instead of 3.)
+        if (syntax) {
+          if (how == 1) {
+            *mode = classic & f_file_mode_mask_how_add;
+          }
+          else if (how == 2) {
+            *mode = classic & f_file_mode_mask_how_add;
+            *replace = f_file_mode_replace_all;
+          }
+          else if (how == 3) {
+            *mode = classic & f_file_mode_mask_how_subtract;
+          }
+          else if (how == 5) {
+            *mode = classic & f_file_mode_mask_how_add;
+            *replace = f_file_mode_replace_umask_all;
+          }
+        }
+      }
+      else {
+        *replace = f_file_mode_replace_all;
+      }
     }
 
-    if (file_stat.st_mode == (S_IFMT & S_IFDIR)) return F_true;
+    if (syntax) {
+      return F_none;
+    }
 
-    return F_false;
+    *mode = 0;
+    *replace = 0;
+
+    return F_status_set_error(F_syntax);
   }
-#endif // _di_f_file_is_at_
+#endif // _di_f_file_mode_from_string_
+
+// @todo: needs f_file_mode_to_mode_t() to convert f_file_mode to a mode_t (requires f_file_mode, a source mode_t, and a destination mode_t).
 
 #ifndef _di_f_file_name_base_
   f_return_status f_file_name_base(const f_string path, const f_string_length length, f_string_dynamic *name_base) {
index 080b1bc06cda4edcf2de4f8d7301e34bb4c0face..c0a8479a3ce82b0624066cd1bb5b45ce034bd92a 100644 (file)
@@ -245,8 +245,50 @@ extern "C" {
 
 /**
  * File mode related functionality.
+ *
+ * The f_file_mode type properties are 8-bit types with the following structure:
+ *   @todo finish documentation.
+ *
+ *   There should only be a single bit for each 'r', 'w', 'x', and 'X' bit (as well as 'S', 's', and 't'):
+ *     'r' = read bit.
+ *     'w' = write bit.
+ *     'x' = execute bit.
+ *     'X' = execute only if already execute bit.
+ *     'S' = set user bit (setuid).
+ *     's' = set group bit (setgid).
+ *     't' = sticky bit.
  */
 #ifndef _di_f_file_mode_
+  typedef uint32_t f_file_mode;
+
+  #define f_file_mode_block_special 0x77000000 // 0111 0111 0000 0000 0000 0000 0000 0000
+  #define f_file_mode_block_owner   0x00ff0000 // 0000 0000 1111 1111 0000 0000 0000 0000
+  #define f_file_mode_block_group   0x0000ff00 // 0000 0000 0000 0000 1111 1111 0000 0000
+  #define f_file_mode_block_world   0x000000ff // 0000 0000 0000 0000 0000 0000 1111 1111
+  #define f_file_mode_block_all     0x77ffffff // 0111 0111 1111 1111 1111 1111 1111 1111
+
+  #define f_file_mode_mask_how_add            0x070f0f0f // 0000 0111 0000 1111 0000 1111 0000 1111
+  #define f_file_mode_mask_how_subtract       0x70f0f0f0 // 0111 0000 1111 0000 1111 0000 1111 0000
+  #define f_file_mode_mask_how_umask_add      0x0f0f0f0f // 0000 1111 0000 1111 0000 1111 0000 1111
+  #define f_file_mode_mask_how_umask_subtract 0x78f0f0f0 // 0111 1000 1111 0000 1111 0000 1111 0000
+
+  #define f_file_mode_mask_bit_execute      0x00111111 // 0000 0000 0001 0001 0001 0001 0001 0001
+  #define f_file_mode_mask_bit_execute_only 0x00888888 // 0000 0000 1000 1000 1000 1000 1000 1000
+  #define f_file_mode_mask_bit_read         0x00444444 // 0000 0000 0100 0100 0100 0100 0100 0100
+  #define f_file_mode_mask_bit_set_group    0x22000000 // 0010 0010 0000 0000 0000 0000 0000 0000
+  #define f_file_mode_mask_bit_set_owner    0x44000000 // 0100 0100 0000 0000 0000 0000 0000 0000
+  #define f_file_mode_mask_bit_sticky       0x11000000 // 0001 0001 0000 0000 0000 0000 0000 0000
+  #define f_file_mode_mask_bit_write        0x00222222 // 0000 0000 0010 0010 0010 0010 0010 0010
+
+  #define f_file_mode_replace_owner       0x1  // 0000 0001
+  #define f_file_mode_replace_group       0x2  // 0000 0010
+  #define f_file_mode_replace_world       0x4  // 0000 0100
+  #define f_file_mode_replace_umask_owner 0x8  // 0000 1000
+  #define f_file_mode_replace_umask_group 0x10 // 0001 0000
+  #define f_file_mode_replace_umask_world 0x20 // 0010 0000
+
+  #define f_file_mode_replace_all       0x7  // 0000 0111
+  #define f_file_mode_replace_umask_all 0x38 // 0011 1000
 
   // file permission modes.
   #define f_file_mode_owner_rwx S_IRWXU
@@ -1017,54 +1059,6 @@ extern "C" {
 #endif // _di_f_file_is_at_
 
 /**
- * Get the base name of a file path.
- *
- * @param path
- *   The path file name.
- *   Need not be NULL terminated.
- * @param length
- *   The length of the path string.
- * @param name_base
- *   The resulting base name as per basename().
- *   The base name is appended onto this.
- *
- * @return
- *   F_none on success.
- *   F_memory_reallocation (with error bit) on memory reallocation error.
- *   F_parameter (with error bit) if a parameter is invalid.
- *   F_string_too_large (with error bit) if string is too large to store in the buffer.
- *
- * @see basename()
- */
-#ifndef _di_f_file_name_base_
-  extern f_return_status f_file_name_base(const f_string path, const f_string_length length, f_string_dynamic *name_base);
-#endif // _di_f_file_name_base_
-
-/**
- * Get the directory name of a file path.
- *
- * @param path
- *   The path file name.
- *   Need not be NULL terminated.
- * @param length
- *   The length of the path string.
- * @param name_directory
- *   The resulting base name as per dirname().
- *   The directory name is appended onto this.
- *
- * @return
- *   F_none on success.
- *   F_memory_reallocation (with error bit) on memory reallocation error.
- *   F_parameter (with error bit) if a parameter is invalid.
- *   F_string_too_large (with error bit) if string is too large to store in the buffer.
- *
- * @see dirname()
- */
-#ifndef _di_f_file_name_directory_
-  extern f_return_status f_file_name_directory(const f_string path, const f_string_length length, f_string_dynamic *name_directory);
-#endif // _di_f_file_name_directory_
-
-/**
  * Create a symbolic link to a file.
  *
  * This will not replace existing files/links.
@@ -1288,6 +1282,124 @@ extern "C" {
 #endif // _di_f_file_link_read_at_
 
 /**
+ * Get the file mode id from a string syntax.
+ *
+ * The string syntax is defined as follows:
+ *   '([ugoa]*[-+=]{0,1}([rwxXst]|[ugo])+([,][ugoa]*[-+=]{0,1}([rwxXst]|[ugo])+)*)|([-+=]0*[0-7]{1,4})'.
+ *
+ * Such that:
+ *   'u' = apply to user.
+ *   'g' = apply to group.
+ *   'o' = apply to other/world.
+ *   'a' = apply to all (user, group, and other/world).
+ *   '-' = remove the specified modes.
+ *   '+' = add the specified modes.
+ *   '=' = overwrite all existing modes with this set.
+ *   'r' = read mode.
+ *   'w' = write mode.
+ *   'x' = execute mode.
+ *   'X' = execute mode, only if already executable.
+ *   's' = set-gid/set-uid mode.
+ *   't' = sticky-bit mode.
+ *   '0' = no mode.
+ *   '1' = execute mode.
+ *   '2' = write mode.
+ *   '3' = execute and write mode.
+ *   '4' = read mode.
+ *   '5' = execute and read mode.
+ *   '6' = read and write mode.
+ *   '7' = execute, read, and write mode.
+ *
+ * When there are 4 digits with a non-zero leading digit (such as 2000 or 002000):
+ *   '1' = sticky-bit mode.
+ *   '2' = set-gid mode.
+ *   '3' = sticky-bit and set-gid mode.
+ *   '4' = set-uid mode.
+ *   '5' = sticky-bit and set-uid mode.
+ *   '6' = set-uid and set-gid mode.
+ *   '7' = sticky-bit, set-uid, and set-gid mode.
+ *
+ * When using digits, each set of 0-7 represents the following:
+ *   [1-7] = apply to other/world.
+ *   [1-7][0-7] = first ([1-7]) to group and second ([0-7]) to other/world.
+ *   [1-7][0-7][0-7] = first ([1-7]) to owner, second ([0-7]) to group, and third ([0-7]) to other/world.
+ *   [1-7][0-7][0-7][0-7] = first ([1-7]) to stick/set-uid/set-gid, second ([0-7]) to owner, third ([0-7]) to owner, and fourth ([0-7]) to other/world.
+ *
+ * When there is a leading 0 when using digits, then this mask will ignore the current umask settings.
+ * Otherwise, the current umask is intended to be respected.
+ *
+ * When '+', '-', or '=' are specified without a leading 'a', 'u', 'g', or 'o', then the mode operations should be performed against the current umask.
+ * These are designated with the umask hows, such as f_file_mode_how_umask_replace.
+ *
+ * @param string
+ *   A NULL terminated string designating the desired mode, following the above string syntax.
+ * @param mode
+ *   The determined mode.
+ *   This uses bitwise data.
+ * @param replace
+ *   The determined modes that are to be replaced, such as: f_file_mode_replace_owner.
+ *   This uses bitwise data.
+ *
+ * @return
+ *   F_none on success.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_syntax (with error bit) if the string fails to follow the syntax rules.
+ *
+ *   The parameters how, mode_normal, and mode_executable are all set to 0 on error.
+ */
+#ifndef _di_f_file_mode_from_string_
+  extern f_return_status f_file_mode_from_string(const f_string string, f_file_mode *mode, uint8_t *replace);
+#endif // _di_f_file_mode_from_string_
+
+/**
+ * Get the base name of a file path.
+ *
+ * @param path
+ *   The path file name.
+ *   Need not be NULL terminated.
+ * @param length
+ *   The length of the path string.
+ * @param name_base
+ *   The resulting base name as per basename().
+ *   The base name is appended onto this.
+ *
+ * @return
+ *   F_none on success.
+ *   F_memory_reallocation (with error bit) on memory reallocation error.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_string_too_large (with error bit) if string is too large to store in the buffer.
+ *
+ * @see basename()
+ */
+#ifndef _di_f_file_name_base_
+  extern f_return_status f_file_name_base(const f_string path, const f_string_length length, f_string_dynamic *name_base);
+#endif // _di_f_file_name_base_
+
+/**
+ * Get the directory name of a file path.
+ *
+ * @param path
+ *   The path file name.
+ *   Need not be NULL terminated.
+ * @param length
+ *   The length of the path string.
+ * @param name_directory
+ *   The resulting base name as per dirname().
+ *   The directory name is appended onto this.
+ *
+ * @return
+ *   F_none on success.
+ *   F_memory_reallocation (with error bit) on memory reallocation error.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_string_too_large (with error bit) if string is too large to store in the buffer.
+ *
+ * @see dirname()
+ */
+#ifndef _di_f_file_name_directory_
+  extern f_return_status f_file_name_directory(const f_string path, const f_string_length length, f_string_dynamic *name_directory);
+#endif // _di_f_file_name_directory_
+
+/**
  * Open a particular file and save its stream.
  *
  * This will open the file and obtain the file descriptor.
@@ -1691,37 +1803,6 @@ extern "C" {
 #endif // _di_f_file_stat_by_id_
 
 /**
- * Write until entire buffer is written.
- *
- * @param file
- *   The file to write to.
- *   The file must already be open.
- * @param buffer
- *   The buffer to write to the file.
- * @param written
- *   The total bytes written.
- *   Set pointer to 0 to not use.
- *
- * @return
- *   F_none on success.
- *   F_none_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file).
- *   F_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation.
- *   F_buffer (with error bit) if the buffer is invalid.
- *   F_file (with error bit) if file descriptor is in an error state.
- *   F_file_closed (with error bit) if file is not open.
- *   F_file_descriptor (with error bit) if the file descriptor is invalid.
- *   F_file_type_directory (with error bit) if file descriptor represents a directory.
- *   F_input_output (with error bit) on I/O error.
- *   F_interrupted (with error bit) if interrupt was received.
- *   F_parameter (with error bit) if a parameter is invalid.
- *
- * @see write()
- */
-#ifndef _di_f_file_write_
-  extern f_return_status f_file_write(const f_file file, const f_string_static buffer, f_string_length *written);
-#endif // _di_f_file_write_
-
-/**
  * Update the files access and modification timestamp, creating the file if it does not already exist.
  *
  * When the file is created, it is created as a regular file.
@@ -1862,6 +1943,37 @@ extern "C" {
 #endif // _di_f_file_type_at_
 
 /**
+ * Write until entire buffer is written.
+ *
+ * @param file
+ *   The file to write to.
+ *   The file must already be open.
+ * @param buffer
+ *   The buffer to write to the file.
+ * @param written
+ *   The total bytes written.
+ *   Set pointer to 0 to not use.
+ *
+ * @return
+ *   F_none on success.
+ *   F_none_stop on success but no data was written (written == 0) (not an error and often happens if file type is not a regular file).
+ *   F_block (with error bit) if file descriptor is set to non-block and the write would result in a blocking operation.
+ *   F_buffer (with error bit) if the buffer is invalid.
+ *   F_file (with error bit) if file descriptor is in an error state.
+ *   F_file_closed (with error bit) if file is not open.
+ *   F_file_descriptor (with error bit) if the file descriptor is invalid.
+ *   F_file_type_directory (with error bit) if file descriptor represents a directory.
+ *   F_input_output (with error bit) on I/O error.
+ *   F_interrupted (with error bit) if interrupt was received.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *
+ * @see write()
+ */
+#ifndef _di_f_file_write_
+  extern f_return_status f_file_write(const f_file file, const f_string_static buffer, f_string_length *written);
+#endif // _di_f_file_write_
+
+/**
  * Write until a single block is filled or entire buffer is written.
  *
  * To check how much was write into the buffer, record buffer->used before execution and compare to buffer->used after execution.
index 7677579fd3f468ab2b9a306b4cea5768422376ea..f4727900876323c91bb057c523f38073c19e6bb1 100644 (file)
@@ -17,8 +17,8 @@
 #include <sys/stat.h>
 
 // fll-0 includes
-#include <level_0/status.h>
 #include <level_0/type.h>
+#include <level_0/status.h>
 
 #ifdef __cplusplus
 extern "C" {
index ec9ce249dc67687e168916f9bfa287f9447d63e6..0d571226a76d96b5b514314dfec85702cde7a913 100644 (file)
@@ -55,6 +55,7 @@
 #include <level_0/memory.h>
 #include <level_0/string.h>
 #include <level_0/utf.h>
+#include <level_0/account.h>
 #include <level_0/console.h>
 #include <level_0/directory.h>
 #include <level_0/environment.h>
index 50c65ad259bd40e0a96989d94508f7f108971bc5..598fe1c56f5c6f09cd22a9e23b4166d56b82b7f5 100644 (file)
@@ -43,6 +43,133 @@ extern "C" {
   }
 #endif // _di_fake_make_assure_inside_project_
 
+#ifndef _di_fake_make_get_id_group_
+  f_return_status fake_make_get_id_group(const fake_data data, const f_string_static buffer, gid_t *id) {
+    const f_string_range range = f_macro_string_range_initialize(buffer.used);
+
+    f_number_unsigned number = 0;
+
+    f_status status = fl_conversion_string_to_number_unsigned(buffer.string, &number, range);
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if (status == F_number) {
+        status = f_account_id_group_by_name(buffer.string, id);
+
+        if (F_status_is_error(status)) {
+          fake_print_error(data, status, "f_account_id_group_by_name", F_true);
+          return F_status_set_error(status);
+        }
+        else if (status == F_exist_not) {
+          if (data.verbosity != fake_verbosity_quiet) {
+            fprintf(f_type_error, "%c", f_string_eol[0]);
+            fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The group name '");
+            fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", buffer.string);
+            fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' was not found.");
+          }
+
+          return F_status_set_error(F_failure);
+        }
+
+        return F_none;
+      }
+
+      fake_print_error(data, status, "fl_conversion_string_to_number_unsigned", F_true);
+      return F_status_set_error(status);
+    }
+    else if (number > f_type_size_32_unsigned) {
+      if (data.verbosity != fake_verbosity_quiet) {
+        fprintf(f_type_error, "%c", f_string_eol[0]);
+        fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The number '");
+        fl_color_print(f_type_error, data.context.notable, data.context.reset, "%llu", number);
+        fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' is too large.");
+      }
+    }
+
+    *id = (gid_t) number;
+    return status;
+  }
+#endif // _di_fake_make_get_id_group_
+
+#ifndef _di_fake_make_get_id_mode_
+  f_return_status fake_make_get_id_mode(const fake_data data, const f_string_static buffer, f_file_mode *mode, uint8_t *replace) {
+    if (!buffer.used) {
+      fake_print_error(data, F_parameter, "fake_make_get_id_mode", F_true);
+      return F_status_set_error(F_parameter);
+    }
+
+    f_status status = f_file_mode_from_string(buffer.string, mode, replace);
+
+    if (F_status_is_error(status)) {
+      if (data.verbosity != fake_verbosity_quiet) {
+        if (F_status_set_fine(status) == F_syntax) {
+          fprintf(f_type_error, "%c", f_string_eol[0]);
+          fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The mode '");
+          fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", buffer.string);
+          fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' is invalid.");
+        }
+        else {
+          fake_print_error(data, status, "f_file_mode_from_string", F_true);
+        }
+      }
+
+      return status;
+    }
+
+    return F_none;
+  }
+#endif // _di_fake_make_get_id_mode_
+
+#ifndef _di_fake_make_get_id_owner_
+  f_return_status fake_make_get_id_owner(const fake_data data, const f_string_static buffer, uid_t *id) {
+    const f_string_range range = f_macro_string_range_initialize(buffer.used);
+
+    f_number_unsigned number = 0;
+
+    f_status status = fl_conversion_string_to_number_unsigned(buffer.string, &number, range);
+
+    if (F_status_is_error(status)) {
+      status = F_status_set_fine(status);
+
+      if (status == F_number) {
+        status = f_account_id_user_by_name(buffer.string, id);
+
+        if (F_status_is_error(status)) {
+          fake_print_error(data, status, "f_account_id_user_by_name", F_true);
+          return F_status_set_error(status);
+        }
+        else if (status == F_exist_not) {
+          if (data.verbosity != fake_verbosity_quiet) {
+            fprintf(f_type_error, "%c", f_string_eol[0]);
+            fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The user name '");
+            fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", buffer.string);
+            fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' was not found.");
+          }
+
+          return F_status_set_error(F_failure);
+        }
+
+        return F_none;
+      }
+
+      fake_print_error(data, status, "fl_conversion_string_to_number_unsigned", F_true);
+      return F_status_set_error(status);
+    }
+    else if (number > f_type_size_32_unsigned) {
+      if (data.verbosity != fake_verbosity_quiet) {
+        fprintf(f_type_error, "%c", f_string_eol[0]);
+        fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The number '");
+        fl_color_print(f_type_error, data.context.notable, data.context.reset, "%llu", number);
+        fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' is too large.");
+      }
+    }
+
+    *id = (uid_t) number;
+    return status;
+  }
+#endif // _di_fake_make_get_id_owner_
+
 #ifndef _di_fake_make_load_fakefile_
   void fake_make_load_fakefile(const fake_data data, fake_make_data *data_make, f_status *status) {
     if (F_status_is_error(*status)) return;
@@ -1122,11 +1249,14 @@ extern "C" {
       f_macro_string_static_initialize(fake_make_operation_else, fake_make_operation_else_length),
       f_macro_string_static_initialize(fake_make_operation_fail, fake_make_operation_fail_length),
       f_macro_string_static_initialize(fake_make_operation_group, fake_make_operation_group_length),
+      f_macro_string_static_initialize(fake_make_operation_group, fake_make_operation_groups_length),
       f_macro_string_static_initialize(fake_make_operation_if, fake_make_operation_if_length),
       f_macro_string_static_initialize(fake_make_operation_link, fake_make_operation_link_length),
       f_macro_string_static_initialize(fake_make_operation_mode, fake_make_operation_mode_length),
+      f_macro_string_static_initialize(fake_make_operation_mode, fake_make_operation_modes_length),
       f_macro_string_static_initialize(fake_make_operation_operate, fake_make_operation_operate_length),
       f_macro_string_static_initialize(fake_make_operation_owner, fake_make_operation_owner_length),
+      f_macro_string_static_initialize(fake_make_operation_owner, fake_make_operation_owners_length),
       f_macro_string_static_initialize(fake_make_operation_pop, fake_make_operation_pop_length),
       f_macro_string_static_initialize(fake_make_operation_print, fake_make_operation_print_length),
       f_macro_string_static_initialize(fake_make_operation_run, fake_make_operation_run_length),
@@ -1148,11 +1278,14 @@ extern "C" {
       f_macro_string_range_initialize(fake_make_operation_else_length),
       f_macro_string_range_initialize(fake_make_operation_fail_length),
       f_macro_string_range_initialize(fake_make_operation_group_length),
+      f_macro_string_range_initialize(fake_make_operation_groups_length),
       f_macro_string_range_initialize(fake_make_operation_if_length),
       f_macro_string_range_initialize(fake_make_operation_link_length),
       f_macro_string_range_initialize(fake_make_operation_mode_length),
+      f_macro_string_range_initialize(fake_make_operation_modes_length),
       f_macro_string_range_initialize(fake_make_operation_operate_length),
       f_macro_string_range_initialize(fake_make_operation_owner_length),
+      f_macro_string_range_initialize(fake_make_operation_owners_length),
       f_macro_string_range_initialize(fake_make_operation_pop_length),
       f_macro_string_range_initialize(fake_make_operation_print_length),
       f_macro_string_range_initialize(fake_make_operation_run_length),
@@ -1174,11 +1307,14 @@ extern "C" {
       fake_make_operation_type_else,
       fake_make_operation_type_fail,
       fake_make_operation_type_group,
+      fake_make_operation_type_groups,
       fake_make_operation_type_if,
       fake_make_operation_type_link,
       fake_make_operation_type_mode,
+      fake_make_operation_type_modes,
       fake_make_operation_type_operate,
       fake_make_operation_type_owner,
+      fake_make_operation_type_owners,
       fake_make_operation_type_pop,
       fake_make_operation_type_print,
       fake_make_operation_type_run,
@@ -1337,10 +1473,35 @@ extern "C" {
     }
 
     if (operation == fake_make_operation_type_group) {
-      // fake_make_assure_inside_project
-      // *status = fll_execute_arguments_add(values[i], lengths[i], &arguments);
-      //fake_build_arguments_standard_add(data, data_build, F_true, F_true, &arguments, status);
-      //fake_build_execute(data, data_build, data_build.setting.build_compiler, arguments, status);
+      gid_t id = 0;
+
+      *status = fake_make_get_id_group(data, arguments.array[0], &id);
+      if (F_status_is_error(*status)) return;
+
+      for (f_array_length i = 1; i < arguments.used; i++) {
+        *status = f_file_change_role(arguments.array[i].string, -1, id, F_false);
+        if (F_status_is_error(*status)) {
+          fake_print_error_file(data, *status, "f_file_change_role", arguments.array[i].string, "change group of", F_true, F_true);
+        }
+      } // for
+
+      return;
+    }
+
+    if (operation == fake_make_operation_type_groups) {
+      gid_t id = 0;
+
+      *status = fake_make_get_id_group(data, arguments.array[0], &id);
+      if (F_status_is_error(*status)) return;
+
+      for (f_array_length i = 1; i < arguments.used; i++) {
+        // @todo: recursive.
+        *status = f_file_change_role(arguments.array[i].string, -1, id, F_false);
+        if (F_status_is_error(*status)) {
+          fake_print_error_file(data, *status, "f_file_change_role", arguments.array[i].string, "change group of", F_true, F_true);
+        }
+      } // for
+
       return;
     }
 
@@ -1358,10 +1519,33 @@ extern "C" {
     }
 
     if (operation == fake_make_operation_type_mode) {
-      // fake_make_assure_inside_project
-      // *status = fll_execute_arguments_add(values[i], lengths[i], &arguments);
-      //fake_build_arguments_standard_add(data, data_build, F_true, F_true, &arguments, status);
-      //fake_build_execute(data, data_build, data_build.setting.build_compiler, arguments, status);
+      f_file_mode mode_rule = 0;
+      uint8_t replace = 0;
+
+      *status = fake_make_get_id_mode(data, arguments.array[0], &mode_rule, &replace);
+      if (F_status_is_error(*status)) return;
+
+      mode_t mode = 0;
+
+      for (f_array_length i = 1; i < arguments.used; i++) {
+        // @todo: get the file mode.
+
+        if (replace) {
+          // @todo when replace is specified, then determine what is to be replaced when converting to mode_t.
+        }
+
+        // @todo: check the zeroing logic, read each file's mode, and updat accordingly.
+        //*status = f_file_change_mode(arguments.array[i].string, mode);
+        //if (F_status_is_error(*status)) {
+        //  fake_print_error_file(data, *status, "f_file_change_mode", arguments.array[i].string, "change mode of", F_true, F_true);
+        //}
+      } // for
+
+      return;
+    }
+
+    if (operation == fake_make_operation_type_modes) {
+      // @todo
       return;
     }
 
@@ -1371,10 +1555,35 @@ extern "C" {
     }
 
     if (operation == fake_make_operation_type_owner) {
-      // fake_make_assure_inside_project
-      // *status = fll_execute_arguments_add(values[i], lengths[i], &arguments);
-      //fake_build_arguments_standard_add(data, data_build, F_true, F_true, &arguments, status);
-      //fake_build_execute(data, data_build, data_build.setting.build_compiler, arguments, status);
+      uid_t id = 0;
+
+      *status = fake_make_get_id_owner(data, arguments.array[0], &id);
+      if (F_status_is_error(*status)) return;
+
+      for (f_array_length i = 1; i < arguments.used; i++) {
+        *status = f_file_change_role(arguments.array[i].string, id, -1, F_false);
+        if (F_status_is_error(*status)) {
+          fake_print_error_file(data, *status, "f_file_change_role", arguments.array[i].string, "change owner of", F_true, F_true);
+        }
+      } // for
+
+      return;
+    }
+
+    if (operation == fake_make_operation_type_owners) {
+      uid_t id = 0;
+
+      *status = fake_make_get_id_owner(data, arguments.array[0], &id);
+      if (F_status_is_error(*status)) return;
+
+      for (f_array_length i = 1; i < arguments.used; i++) {
+        // @todo recursive.
+        *status = f_file_change_role(arguments.array[i].string, id, -1, F_false);
+        if (F_status_is_error(*status)) {
+          fake_print_error_file(data, *status, "f_file_change_role", arguments.array[i].string, "change owner of", F_true, F_true);
+        }
+      } // for
+
       return;
     }
 
@@ -1768,12 +1977,16 @@ extern "C" {
         if (arguments.array[0].used) {
           f_status status_file = f_file_is(arguments.array[0].string, f_file_type_regular);
 
-          if (F_status_is_error(status_file)) {
+          if (status_file == F_file_found_not) {
             printf("%c", f_string_eol[0]);
             fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: Failed to find file '");
             fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", arguments.array[0].string);
             fl_color_print_line(f_type_error, data.context.error, data.context.reset, "'.");
 
+            *status = F_status_set_error(status_file);
+          }
+          else if (F_status_is_error(status_file)) {
+            fake_print_error_file(data, *status, "f_file_is", data.file_data_build_fakefile.string, "find", F_true, F_true);
             *status = status_file;
           }
 
@@ -1910,42 +2123,27 @@ extern "C" {
         *status = F_status_set_error(F_failure);
       }
     }
-    else if (operation == fake_make_operation_type_group || operation == fake_make_operation_type_mode || operation == fake_make_operation_type_owner) {
-      if (arguments.used > 3) {
-        printf("%c", f_string_eol[0]);
-        fl_color_print_line(f_type_error, data.context.error, data.context.reset, "ERROR: Has too many arguments.");
-
-        *status = F_status_set_error(F_failure);
-      }
-      else if (arguments.used > 1) {
-        f_status status_file = f_file_is(arguments.array[1].string, f_file_type_regular);
-
-        if (F_status_is_error(status_file)) {
-          printf("%c", f_string_eol[0]);
-          fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: Failed to find file '");
-          fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", arguments.array[1].string);
-          fl_color_print_line(f_type_error, data.context.error, data.context.reset, "'.");
-
-          *status = status_file;
-        }
+    else if (operation == fake_make_operation_type_group || operation == fake_make_operation_type_groups || operation == fake_make_operation_type_mode || operation == fake_make_operation_type_modes || operation == fake_make_operation_type_owner || operation == fake_make_operation_type_owners) {
+      printf("DEBUG: arguments.used = %llu\n");
+      if (arguments.used > 1) {
+        f_status status_file = F_none;
 
-        if (!status_file) {
-          printf("%c", f_string_eol[0]);
-          fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: The file '");
-          fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", arguments.array[1].string);
-          fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' must be a regular file.");
+        for (f_array_length i = 1; i < arguments.used; i++) {
+          status_file = f_file_is(arguments.array[i].string, f_file_type_regular);
 
-          *status = F_status_set_error(F_failure);
-        }
+          printf("DEBUG: at %llu, looking at '%s', %llu\n", i, arguments.array[i].string, F_status_set_fine(status_file));
 
-        if (arguments.used == 3) {
-          if (fl_string_dynamic_compare_string(fake_make_operation_argument_recursive, arguments.array[2], fake_make_operation_argument_recursive_length) == F_equal_to_not) {
+          if (status_file == F_file_found_not) {
             printf("%c", f_string_eol[0]);
-            fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: Third argument must be either '");
-            fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", fake_make_operation_argument_recursive);
-            fl_color_print_line(f_type_error, data.context.error, data.context.reset, "' or not provided at all.");
+            fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: Failed to find file '");
+            fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", arguments.array[i].string);
+            fl_color_print_line(f_type_error, data.context.error, data.context.reset, "'.");
 
-            *status = F_status_set_error(F_failure);
+            *status = status_file;
+          }
+          else if (F_status_is_error(status_file)) {
+            fake_print_error_file(data, *status, "f_file_is", arguments.array[i].string, "find", F_true, F_true);
+            *status = status_file;
           }
         }
       }
@@ -2045,7 +2243,7 @@ extern "C" {
         if (arguments.array[0].used) {
           f_status status_file = f_file_is(arguments.array[0].string, f_file_type_directory);
 
-          if (F_status_is_error(status_file)) {
+          if (status_file == F_file_found_not) {
             printf("%c", f_string_eol[0]);
             fl_color_print(f_type_error, data.context.error, data.context.reset, "ERROR: Failed to find file '");
             fl_color_print(f_type_error, data.context.notable, data.context.reset, "%s", arguments.array[0].string);
@@ -2053,6 +2251,10 @@ extern "C" {
 
             *status = status_file;
           }
+          else if (F_status_is_error(status_file)) {
+            fake_print_error_file(data, *status, "f_file_is", data.file_data_build_fakefile.string, "find", F_true, F_true);
+            *status = status_file;
+          }
 
           if (!status_file) {
             printf("%c", f_string_eol[0]);
index f9ea2c9ae57f4ff1fda22e5467d72cbfb11da813..683de3aba9e782d83268a7235b21b8ecb9c94596 100644 (file)
@@ -66,11 +66,14 @@ extern "C" {
   #define fake_make_operation_else     "else"
   #define fake_make_operation_fail     "fail"
   #define fake_make_operation_group    "group"
+  #define fake_make_operation_groups   "groups"
   #define fake_make_operation_if       "if"
   #define fake_make_operation_link     "link"
   #define fake_make_operation_mode     "mode"
+  #define fake_make_operation_modes    "modes"
   #define fake_make_operation_operate  "operate"
   #define fake_make_operation_owner    "owner"
+  #define fake_make_operation_owners   "owners"
   #define fake_make_operation_pop      "pop"
   #define fake_make_operation_print    "print"
   #define fake_make_operation_run      "run"
@@ -90,11 +93,14 @@ extern "C" {
   #define fake_make_operation_else_length     4
   #define fake_make_operation_fail_length     4
   #define fake_make_operation_group_length    5
+  #define fake_make_operation_groups_length   6
   #define fake_make_operation_if_length       2
   #define fake_make_operation_link_length     4
   #define fake_make_operation_mode_length     4
+  #define fake_make_operation_modes_length    5
   #define fake_make_operation_operate_length  7
   #define fake_make_operation_owner_length    5
+  #define fake_make_operation_owners_length   6
   #define fake_make_operation_pop_length      3
   #define fake_make_operation_print_length    5
   #define fake_make_operation_run_length      3
@@ -115,11 +121,14 @@ extern "C" {
     fake_make_operation_type_else,
     fake_make_operation_type_fail,
     fake_make_operation_type_group,
+    fake_make_operation_type_groups,
     fake_make_operation_type_if,
     fake_make_operation_type_link,
     fake_make_operation_type_mode,
+    fake_make_operation_type_modes,
     fake_make_operation_type_operate,
     fake_make_operation_type_owner,
+    fake_make_operation_type_owners,
     fake_make_operation_type_pop,
     fake_make_operation_type_print,
     fake_make_operation_type_run,
@@ -130,7 +139,7 @@ extern "C" {
     fake_make_operation_type_touch,
   };
 
-  #define fake_make_operation_total 23
+  #define fake_make_operation_total 26
 
   #define fake_make_operation_argument_file      "file"
   #define fake_make_operation_argument_directory "directory"
@@ -325,6 +334,70 @@ extern "C" {
 #endif // _di_fake_make_assure_inside_project_
 
 /**
+ * Get the group id from either a string representing the number or a string representing the name.
+ *
+ * @param data
+ *   The program data.
+ * @param buffer
+ *   The string containing the name or number.
+ * @param id
+ *   The detected group id.
+ *
+ * @return
+ *   F_none on success.
+ *   F_exist_not if there is no group by the given name.
+ *
+ *   Status codes (with error bit) are returned on any problem.
+ */
+#ifndef _di_fake_make_get_id_group_
+  f_return_status fake_make_get_id_group(const fake_data data, const f_string_static buffer, gid_t *id) f_gcc_attribute_visibility_internal;
+#endif // _di_fake_make_get_id_group_
+
+/**
+ * Get the mode id from either a string representing the number or a string representing the mode.
+ *
+ * @param data
+ *   The program data.
+ * @param buffer
+ *   The string containing the name or number.
+ * @param mode
+ *   The determined mode.
+ *   This uses bitwise data.
+ * @param replace
+ *   The determined modes that are to be replaced, such as: f_file_mode_replace_owner.
+ *   This uses bitwise data.
+ *
+ * @return
+ *   F_none on success.
+ *   F_exist_not if there is no mode by the given name.
+ *
+ *   Status codes (with error bit) are returned on any problem.
+ */
+#ifndef _di_fake_make_get_id_mode_
+  f_return_status fake_make_get_id_mode(const fake_data data, const f_string_static buffer, f_file_mode *mode, uint8_t *replace) f_gcc_attribute_visibility_internal;
+#endif // _di_fake_make_get_id_mode_
+
+/**
+ * Get the user id from either a string representing the number or a string representing the name.
+ *
+ * @param data
+ *   The program data.
+ * @param buffer
+ *   The string containing the name or number.
+ * @param id
+ *   The detected user id.
+ *
+ * @return
+ *   F_none on success.
+ *   F_exist_not if there is no user by the given name.
+ *
+ *   Status codes (with error bit) are returned on any problem.
+ */
+#ifndef _di_fake_make_get_id_owner_
+  f_return_status fake_make_get_id_owner(const fake_data data, const f_string_static buffer, uid_t *id) f_gcc_attribute_visibility_internal;
+#endif // _di_fake_make_get_id_owner_
+
+/**
  * Find the fake file, load it, validate it, and process it.
  *
  * This will process any additional files as necessary, such as the build settings file.
index 2a05f0899a948e7c32c3b42a26caa5ad52ab964f..48fe7ef1fbbe796a1448ad2730485d9802373be7 100644 (file)
@@ -5,6 +5,7 @@ f_status
 f_memory
 f_string
 f_utf
+f_account
 f_color
 f_console
 f_conversion
index 2ae12881c8800f5a32b29202956980779c305790..1128fed55e77558c11bdb5d09084a25927aae475 100644 (file)
@@ -9,4 +9,5 @@ settings:
 
 main:
 
-  build
+  #build
+  owner a b
index 726c2bbc9f2ed2cdc2c3f258dcf05a48d9c4a052..2d1ce3751eb7578f39b7c120a88adfef633f44a7 100644 (file)
@@ -19,7 +19,7 @@ build_compiler gcc
 build_language c
 build_linker ar
 build_libraries -lc
-build_libraries-individual -lfll_program -lfll_path -lfll_execute -lfl_environment -lfll_fss -lfl_utf -lfl_string -lfl_status -lfl_iki -lfl_fss -lfl_directory -lfl_conversion -lfl_console -lfl_color -lf_print -lf_path -lf_iki -lf_file -lf_fss -lf_environment -lf_directory -lf_conversion -lf_console -lf_utf -lf_memory
+build_libraries-individual -lfll_program -lfll_path -lfll_execute -lfl_environment -lfll_fss -lfl_utf -lfl_string -lfl_status -lfl_iki -lfl_fss -lfl_directory -lfl_conversion -lfl_console -lfl_color -lf_account -lf_print -lf_path -lf_iki -lf_file -lf_fss -lf_environment -lf_directory -lf_conversion -lf_console -lf_utf -lf_memory
 build_libraries-level -lfll_2 -lfll_1 -lfll_0
 build_libraries-monolithic -lfll
 build_sources_library fake.c private-fake.c private-clean.c private-build.c private-make.c private-print.c private-skeleton.c
index 17b1138f25d1d7b3ac9d09e180d7e1b703ea77f2..78cf8dcc97dbff701263889663fc204a7e0d9de9 100644 (file)
@@ -124,9 +124,8 @@ Fakefile Documentation:
       The first Content represents the group to assign.
       The second Content represents the file to assign the group to.
 
-      An optional third Content may be specified.
-      This third Content, if specified, may only be "recursive".
-      When specified, this will recursively apply the role to all files within the given file, if that file is a directory file path.
+    - groups\:
+      Identical to group operation, except this will recursively apply the mode to all files within the given file, if that file is a directory file path.
 
     - if\:
       Performs a programmatic "if" condition.
@@ -147,9 +146,8 @@ Fakefile Documentation:
       The first Content represents the mode to assign.
       The second Content represents the file to assign the mode to.
 
-      An optional third Content may be specified.
-      This third Content, if specified, may only be "recursive".
-      When specified, this will recursively apply the mode to all files within the given file, if that file is a directory file path.
+    - modes\:
+      Identical to mode operation, except this will recursively apply the mode to all files within the given file, if that file is a directory file path.
 
     - operate\:
       Begin execution of another Section.
@@ -164,9 +162,8 @@ Fakefile Documentation:
       The first Content represents the role to assign.
       The second Content represents the file to assign the role to.
 
-      An optional third Content may be specified.
-      This third Content, if specified, may only be "recursive".
-      When specified, this will recursively apply the role to all files within the given file, if that file is a directory file path.
+    - owners\:
+      Identical to owner operation, except this will recursively apply the mode to all files within the given file, if that file is a directory file path.
 
     - pop\:
       Pop a directory path of the path stack, thereby changing to the previous directory on the stack.
index b8bf101fff2e5844a7a469bd5e9fa73c408ccbad..d1c95e04b0ba1eaa6b1ae07d65134c740c05ca17 100644 (file)
@@ -50,12 +50,15 @@ Fakefile Specification:
   - delete: Two or three Content. First Content is either "file" or "directory" (case-sensitive), second Content is path to file, and third Content is "recursive" (case-sensitive) or is not provided.
   - else: Zero Content.
   - fail: One Content. First Content must be one of "exit", "warn", or "ignore" (case-sensitive).
-  - group: Two or three Content. First Content is group name or number, second Content is path to file, and third Content is "recursive" (case-sensitive) or is not provided.
+  - group: Two or more Content. First Content is group name or number, remaining Content are paths to files.
+  - groups: Two or more Content. First Content is group name or number, remaining Content are paths to files.
   - if: One or more Content. First Content is the condition, remaining Content are specific to the condition.
   - link: Two Content. First Content is the link target file and second Content is the pointer file (the link).
-  - mode: Two or three Content. First Content is group name or number, second Content is path to file, and third Content is "recursive" (case-sensitive) or is not provided.
+  - mode: Two or more Content. First Content is group name or number, remaining Content are paths to files.
+  - modes: Two or more Content. First Content is group name or number, remaining Content are paths to files.
   - operate: One Content. First Content is the name of a valid Section Object, except for the reserved Section Objects.
-  - owner: Two or Three Content. First Content is group name or number, second Content is path to file, and third Content is "recursive" (case-sensitive) or is not provided.
+  - owner: Two or more Content. First Content is group name or number, remaining Content are paths to files.
+  - owners: Two or more Content. First Content is group name or number, remaining Content are paths to files.
   - pop: Zero Content.
   - print: Zero or more Content.
   - run: One or more Content. First Content is the name of the program (or script) and all remaining Content are passed as arguments to the named program (or script).