diff --git a/meson.build b/meson.build index d08e815..c0826aa 100644 --- a/meson.build +++ b/meson.build @@ -1,10 +1,11 @@ -project('layeredfs', 'c', 'cpp', version: '3.9', +project('layeredfs', 'c', 'cpp', version: '3.10', default_options: [ 'cpp_std=c++20', 'buildtype=release', 'strip=true', 'werror=true', - ] + ], + meson_version: '>=1.2.0', ) add_project_link_arguments('-static', language: 'cpp') @@ -204,3 +205,21 @@ test('unit tests', executable('tests_bin', ), workdir: meson.current_source_dir(), ) + +test('commandline parsing test', executable('test_commandline', + sources: 'src/test_commandline.cpp', + link_with: [layeredfs_lib, texbin_lib, avs_standalone_lib], + dependencies: [layeredfs_cfg_dep, gtest_main_dep, gmock_dep], + build_by_default: false, + ), + workdir: meson.current_source_dir(), + args: [ + '--layered-disable', + '--layered-verbose', + '--layered-devmode', + '--layered-allowlist=allowed,these folders', + '--layered-blocklist=blocked,these folders', + '--layered-logfile=some logfile.log', + '--layered-data-mods-folder=./some modfolder', + ] +) diff --git a/src/config.cpp b/src/config.cpp index 364edfc..11941a7 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -67,39 +67,55 @@ void load_config(void) { config.logfile = NULL; #endif - int i; + int argc; + auto argv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (!argv) { + log_warning("Couldn't fetch commandline args!"); + return; + } + + char* arg; // so close to just pulling in a third party argparsing lib... - for (i = 0; i < __argc; i++) { - if (strcmp(__argv[i], VERBOSE_FLAG) == 0) { + for (int i = 0; i < argc; i++) { + if (!wstr_narrow(argv[i], &arg)) + continue; + + if (strcmp(arg, VERBOSE_FLAG) == 0) { config.verbose_logs = true; + free(arg); } - else if (strcmp(__argv[i], DEVMODE_FLAG) == 0) { + else if (strcmp(arg, DEVMODE_FLAG) == 0) { config.developer_mode = true; + free(arg); } - else if (strcmp(__argv[i], DISABLE_FLAG) == 0) { + else if (strcmp(arg, DISABLE_FLAG) == 0) { config.disable = true; + free(arg); } - else if (strncmp(__argv[i], ALLOWLIST_FLAG, strlen(ALLOWLIST_FLAG)) == 0) { - allowlist = parse_list(ALLOWLIST_FLAG, __argv[i], config.allowlist); + else if (strncmp(arg, ALLOWLIST_FLAG, strlen(ALLOWLIST_FLAG)) == 0) { + allowlist = parse_list(ALLOWLIST_FLAG, arg, config.allowlist); } - else if (strncmp(__argv[i], BLOCKLIST_FLAG, strlen(BLOCKLIST_FLAG)) == 0) { - blocklist = parse_list(BLOCKLIST_FLAG, __argv[i], config.blocklist); + else if (strncmp(arg, BLOCKLIST_FLAG, strlen(BLOCKLIST_FLAG)) == 0) { + blocklist = parse_list(BLOCKLIST_FLAG, arg, config.blocklist); } - else if (strncmp(__argv[i], LOGFILE_FLAG, strlen(LOGFILE_FLAG)) == 0) { - const char *path = &__argv[i][strlen(LOGFILE_FLAG)]; + else if (strncmp(arg, LOGFILE_FLAG, strlen(LOGFILE_FLAG)) == 0) { + const char *path = &arg[strlen(LOGFILE_FLAG)]; // correct format: --layered-logfile=whatever.log if(path[0] == '=' && path[1]) { config.logfile = &path[1]; } } - else if (strncmp(__argv[i], MOD_FOLDER_FLAG, strlen(MOD_FOLDER_FLAG)) == 0) { - std::string_view path = &__argv[i][strlen(MOD_FOLDER_FLAG)]; + else if (strncmp(arg, MOD_FOLDER_FLAG, strlen(MOD_FOLDER_FLAG)) == 0) { + std::string_view path = &arg[strlen(MOD_FOLDER_FLAG)]; // correct format: --layered-data-mods-folder=./my_mods if(path.starts_with("=./")) { config.mod_folder = path.substr(1); } } + else { + free(arg); + } } } diff --git a/src/test_commandline.cpp b/src/test_commandline.cpp new file mode 100644 index 0000000..b873618 --- /dev/null +++ b/src/test_commandline.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +#include "config.hpp" +#include "avs_standalone.hpp" + +using ::testing::ElementsAre; + +TEST(Cmdline, ConfigParsingWorks) { + load_config(); + + EXPECT_EQ(config.verbose_logs, 1); + EXPECT_EQ(config.developer_mode, 1); + EXPECT_EQ(config.disable, 1); + EXPECT_STREQ(config.logfile, "some logfile.log"); + EXPECT_THAT(config.allowlist, ElementsAre("allowed", "these folders")); + EXPECT_THAT(config.blocklist, ElementsAre("blocked", "these folders")); + EXPECT_EQ(config.mod_folder, "./some modfolder"); +} diff --git a/src/utils.cpp b/src/utils.cpp index cf5f229..5441435 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -99,6 +99,33 @@ wchar_t *str_widen(const char *src) return result; } +bool wstr_narrow(const wchar_t *src, char **dest) +{ + int nbytes; + + nbytes = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL); + + if (nbytes <= 0) { + goto size_fail; + } + + *dest = (char*)malloc(nbytes); + + if (WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, nbytes, NULL, NULL) != + nbytes) { + goto conv_fail; + } + + return true; + +conv_fail: + free(*dest); + *dest = NULL; + +size_fail: + return false; +} + bool file_exists(const char* name) { // file_exists is only used by the modfile machinery, so use the easy // method, not avs_fs_open or avs_fs_lstat diff --git a/src/utils.hpp b/src/utils.hpp index 91cfe41..67dded0 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -21,6 +21,7 @@ bool string_replace_first(std::string &str, const char* from, const char* to); // Like string.find(), but case insensitive std::size_t string_find_icase(const std::string & strHaystack, const std::string & strNeedle, std::size_t off = 0); wchar_t *str_widen(const char *src); +bool wstr_narrow(const wchar_t *src, char **dest); void str_toupper_inline(std::string &str); bool file_exists(const char* name); bool folder_exists(const char* name); diff --git a/xp_dlls/shell32.dll b/xp_dlls/shell32.dll new file mode 100644 index 0000000..277aff1 Binary files /dev/null and b/xp_dlls/shell32.dll differ