From 507c566c43d000ea1a882600f5bc0176b3c208b0 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Wed, 29 Jul 2020 22:26:14 -0500 Subject: [PATCH] Progress: featureless make and related 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. --- build/level_0/settings | 4 +- build/monolithic/settings | 4 +- build/scripts/bootstrap-example.sh | 4 +- level_0/f_account/c/account.c | 159 ++++++++++++ level_0/f_account/c/account.h | 99 ++++++++ level_0/f_account/data/build/defines | 2 + level_0/f_account/data/build/dependencies | 6 + level_0/f_account/data/build/settings | 52 ++++ level_0/f_file/c/file.c | 398 +++++++++++++++++++++++++++--- level_0/f_file/c/file.h | 270 ++++++++++++++------ level_0/f_pipe/c/pipe.h | 2 +- level_3/fake/c/fake.h | 1 + level_3/fake/c/private-make.c | 292 ++++++++++++++++++---- level_3/fake/c/private-make.h | 75 +++++- level_3/fake/data/build/dependencies | 1 + level_3/fake/data/build/fakefile | 3 +- level_3/fake/data/build/settings | 2 +- level_3/fake/documents/fakefile.txt | 15 +- level_3/fake/specifications/fakefile.txt | 9 +- 19 files changed, 1211 insertions(+), 187 deletions(-) create mode 100644 level_0/f_account/c/account.c create mode 100644 level_0/f_account/c/account.h create mode 100644 level_0/f_account/data/build/defines create mode 100644 level_0/f_account/data/build/dependencies create mode 100644 level_0/f_account/data/build/settings diff --git a/build/level_0/settings b/build/level_0/settings index c9c7e11..6f9dd38 100644 --- a/build/level_0/settings +++ b/build/level_0/settings @@ -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 diff --git a/build/monolithic/settings b/build/monolithic/settings index bce523d..88b8e73 100644 --- a/build/monolithic/settings +++ b/build/monolithic/settings @@ -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 diff --git a/build/scripts/bootstrap-example.sh b/build/scripts/bootstrap-example.sh index 7b66ff1..0b32a1a 100644 --- a/build/scripts/bootstrap-example.sh +++ b/build/scripts/bootstrap-example.sh @@ -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 index 0000000..b0974b7 --- /dev/null +++ b/level_0/f_account/c/account.c @@ -0,0 +1,159 @@ +#include + +#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 index 0000000..a06397a --- /dev/null +++ b/level_0/f_account/c/account.h @@ -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 +#include +#include +#include + +// fll-0 includes +#include +#include +#include +#include + +#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 index 0000000..c665317 --- /dev/null +++ b/level_0/f_account/data/build/defines @@ -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 index 0000000..d798245 --- /dev/null +++ b/level_0/f_account/data/build/dependencies @@ -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 index 0000000..82ded25 --- /dev/null +++ b/level_0/f_account/data/build/settings @@ -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 diff --git a/level_0/f_file/c/file.c b/level_0/f_file/c/file.c index d110ec1..03efa95 100644 --- a/level_0/f_file/c/file.c +++ b/level_0/f_file/c/file.c @@ -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) { diff --git a/level_0/f_file/c/file.h b/level_0/f_file/c/file.h index 080b1bc..c0a8479 100644 --- a/level_0/f_file/c/file.h +++ b/level_0/f_file/c/file.h @@ -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. diff --git a/level_0/f_pipe/c/pipe.h b/level_0/f_pipe/c/pipe.h index 7677579..f472790 100644 --- a/level_0/f_pipe/c/pipe.h +++ b/level_0/f_pipe/c/pipe.h @@ -17,8 +17,8 @@ #include // fll-0 includes -#include #include +#include #ifdef __cplusplus extern "C" { diff --git a/level_3/fake/c/fake.h b/level_3/fake/c/fake.h index ec9ce24..0d57122 100644 --- a/level_3/fake/c/fake.h +++ b/level_3/fake/c/fake.h @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include diff --git a/level_3/fake/c/private-make.c b/level_3/fake/c/private-make.c index 50c65ad..598fe1c 100644 --- a/level_3/fake/c/private-make.c +++ b/level_3/fake/c/private-make.c @@ -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]); diff --git a/level_3/fake/c/private-make.h b/level_3/fake/c/private-make.h index f9ea2c9..683de3a 100644 --- a/level_3/fake/c/private-make.h +++ b/level_3/fake/c/private-make.h @@ -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. diff --git a/level_3/fake/data/build/dependencies b/level_3/fake/data/build/dependencies index 2a05f08..48fe7ef 100644 --- a/level_3/fake/data/build/dependencies +++ b/level_3/fake/data/build/dependencies @@ -5,6 +5,7 @@ f_status f_memory f_string f_utf +f_account f_color f_console f_conversion diff --git a/level_3/fake/data/build/fakefile b/level_3/fake/data/build/fakefile index 2ae1288..1128fed 100644 --- a/level_3/fake/data/build/fakefile +++ b/level_3/fake/data/build/fakefile @@ -9,4 +9,5 @@ settings: main: - build + #build + owner a b diff --git a/level_3/fake/data/build/settings b/level_3/fake/data/build/settings index 726c2bb..2d1ce37 100644 --- a/level_3/fake/data/build/settings +++ b/level_3/fake/data/build/settings @@ -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 diff --git a/level_3/fake/documents/fakefile.txt b/level_3/fake/documents/fakefile.txt index 17b1138..78cf8dc 100644 --- a/level_3/fake/documents/fakefile.txt +++ b/level_3/fake/documents/fakefile.txt @@ -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. diff --git a/level_3/fake/specifications/fakefile.txt b/level_3/fake/specifications/fakefile.txt index b8bf101..d1c95e0 100644 --- a/level_3/fake/specifications/fakefile.txt +++ b/level_3/fake/specifications/fakefile.txt @@ -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). -- 1.8.3.1