diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4066b700..81670d29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,52 @@ on: - main jobs: + Windows: + runs-on: windows-2022 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup python 3.10 + uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - name: Install packages + run: + choco install pkgconfiglite ninja + + - name: Install python modules + run: pip3 install meson + + - name: Setup MSVC compiler + uses: bus1/cabuild/action/msdevshell@v1 + with: + architecture: x64 + + - name: Install dependencies + uses: kiwix/kiwix-build/actions/dl_deps_archive@main + with: + target_platform: win-x86_64-static + + - name: Compile + shell: cmd + run: | + set PKG_CONFIG_PATH=%cd%\BUILD_win-amd64\INSTALL\lib\pkgconfig + set CPPFLAGS=-I%cd%\BUILD_win-amd64\INSTALL\include + meson.exe setup . build -Dstatic-linkage=true --buildtype=debug + cd build + ninja.exe + + - name: Test + shell: cmd + run: | + cd build + meson.exe test --verbose + env: + WAIT_TIME_FACTOR_TEST: 10 + Linux: strategy: fail-fast: false diff --git a/debian/control b/debian/control index 91f5b990..5601315c 100644 --- a/debian/control +++ b/debian/control @@ -5,6 +5,8 @@ Maintainer: Kiwix team Build-Depends: debhelper-compat (= 13), libzim-dev (>= 9.0.0), libzim-dev (<< 10.0.0), libkiwix-dev (>= 13.0.0), libkiwix-dev (<< 14.0.0), + cmake, + libdocopt-dev, meson, pkg-config, Standards-Version: 4.5.0 diff --git a/meson.build b/meson.build index 31180183..c089617e 100644 --- a/meson.build +++ b/meson.build @@ -7,6 +7,10 @@ compiler = meson.get_compiler('cpp') add_global_arguments('-DKIWIX_TOOLS_VERSION="@0@"'.format(meson.project_version()), language : 'cpp') +if host_machine.system() == 'windows' + add_project_arguments('-DNOMINMAX', language: 'cpp') +endif + static_linkage = get_option('static-linkage') if static_linkage # Static build is not supported on MacOS @@ -20,8 +24,9 @@ libzim_dep = dependency('libzim', version:'>=9.0.0', static:static_linkage) libzim_dep = dependency('libzim', version:'<10.0.0', static:static_linkage) kiwixlib_dep = dependency('kiwix', version:'>=13.0.0', static:static_linkage) kiwixlib_dep = dependency('kiwix', version:'<14.0.0', static:static_linkage) +docopt_dep = dependency('docopt', static:static_linkage) -all_deps = [thread_dep, kiwixlib_dep, libzim_dep] +all_deps = [thread_dep, kiwixlib_dep, libzim_dep, docopt_dep] if static_linkage librt = compiler.find_library('rt', required:false) diff --git a/src/manager/kiwix-manage.cpp b/src/manager/kiwix-manage.cpp index 8d3f1722..8364afa9 100644 --- a/src/manager/kiwix-manage.cpp +++ b/src/manager/kiwix-manage.cpp @@ -17,7 +17,7 @@ * MA 02110-1301, USA. */ -#include +#include #include #include #include @@ -51,57 +51,51 @@ void show(const kiwix::Library& library, const std::string& bookId) std::cout << std::endl; } +// Older version of docopt doesn't declare Options. Let's declare it ourself. +using Options = std::map; + + /* Print correct console usage options */ -void usage() -{ - std::cout << "Usage:" << std::endl - << "\tkiwix-manage LIBRARY_PATH add ZIM_PATH [OPTIONS]" << std::endl - << "\tkiwix-manage LIBRARY_PATH remove ZIM_ID [ZIM_ID]..." << std::endl - << "\tkiwix-manage LIBRARY_PATH show [ZIM_ID]..." << std::endl - << std::endl - - << "Purpose:" << std::endl - << "\tManipulates the Kiwix library XML file" - << std::endl << std::endl - - << "Arguments:" << std::endl - << "\tLIBRARY_PATH\tis the XML library file path." - << std::endl << std::endl - << "\tACTION\t\tis the pre-defined string to specify the action to run on the XML library file." - << std::endl << std::endl - << "\t\t\tMust be one of the following values:" << std::endl - << "\t\t\t* add: add a ZIM file to the library" << std::endl - << "\t\t\t* remove: remove a ZIM file from the library" << std::endl - << "\t\t\t* show: show the content of the library" - << std::endl << std::endl - << "\tZIM_ID\t\tZIM file unique ID" - << std::endl << std::endl - << "\tOPTIONS\t\tCustom options for \"add\" action:" << std::endl - << "\t\t\t--zimPathToSave=CUSTOM_ZIM_PATH to replace the current ZIM file path" << std::endl - << "\t\t\t--url=HTTP_ZIM_URL to create an \"url\" attribute for the online version of the ZIM file" << std::endl - << std::endl - << "\t\t\tOther options:" << std::endl - << "\t\t\t-v, --version to print the software version" << std::endl - << std::endl - - << "Examples:" << std::endl - << "\tAdd ZIM files to library: kiwix-manage my_library.xml add first.zim second.zim" << std::endl - << "\tRemove ZIM files from library: kiwix-manage my_library.xml remove e5c2c003-b49e-2756-5176-5d9c86393dd9" << std::endl - << "\tShow all library ZIM files: kiwix-manage my_library.xml show" << std::endl - << std::endl - - << "Documentation:" << std::endl - << "\tSource code\thttps://github.com/kiwix/kiwix-tools" << std::endl - << "\tMore info\thttps://wiki.kiwix.org/wiki/Kiwix-manage" << std::endl - << std::endl; -} +static const char USAGE[] = +R"(Manipulates the Kiwix library XML file + +Usage: + kiwix-manage LIBRARYPATH add [--zimPathToSave=] [--url=] ZIMPATH + kiwix-manage LIBRARYPATH remove|delete ZIMID ... + kiwix-manage LIBRARYPATH show [ZIMID ...] + kiwix-manage -v | --version + kiwix-manage -h | --help + +Arguments: + LIBRARYPATH The XML library file path. + ZIMID ZIM file unique ID. + ZIMPATH A path to a zim to add. + +Options: + Custom options for "add" action: + --zimPathToSave= Replace the current ZIM file path + --url= Create an "url" attribute for the online version of the ZIM file + + Other options: + -h --help Print this help + -v --version Print the software version + +Examples: + Add ZIM files to library: kiwix-manage my_library.xml add first.zim second.zim + Remove ZIM files from library: kiwix-manage my_library.xml remove e5c2c003-b49e-2756-5176-5d9c86393dd9 + Show all library ZIM files: kiwix-manage my_library.xml show + +Documentation: + Source code https://github.com/kiwix/kiwix-tools + More info https://wiki.kiwix.org/wiki/Kiwix-manage +)"; int handle_show(const kiwix::Library& library, const std::string& libraryPath, - int argc, char* argv[]) + const Options& options) { - if (argc > 3 ) { - for(auto i=3; i= argc) { - std::cerr << "Path to zim file to add is missing in the command line" << std::endl; - return (-1); + std::string zimPath = options.at("ZIMPATH").asString(); + if (options.at("--zimPathToSave").isString()) { + zimPathToSave = options.at("--zimPathToSave").asString(); + } else { + zimPathToSave = zimPath; } - - if (has_option && argc-optind > 1) { - std::cerr << "You cannot give option and several zim files to add" << std::endl; - return (-1); + if (options.at("--url").isString()) { + url = options.at("--url").asString(); } - kiwix::Manager manager(library); - - for(auto i=optind; i 2) { - libraryPath = argv[1]; - string actionString = argv[2]; - - if (actionString == "add") - action = ADD; - else if (actionString == "show") - action = SHOW; - else if (actionString == "remove" || actionString == "delete") - action = REMOVE; + if (args["--help"].asBool()) { + std::cout << USAGE << std::endl; + return 0; } - /* Print usage)) if necessary */ - if (libraryPath == "" || action == NONE) { - usage(); - return -1; + if (args["--version"].asBool()) { + version(); + return 0; } + std::string libraryPath = args.at("LIBRARYPATH").asString(); + + if (args.at("add").asBool()) + action = ADD; + else if (args.at("show").asBool()) + action = SHOW; + else if (args.at("remove").asBool() || args.at("delete").asBool()) + action = REMOVE; + /* Try to read the file */ libraryPath = kiwix::isRelativePath(libraryPath) ? kiwix::computeAbsolutePath(kiwix::getCurrentDirectory(), libraryPath) @@ -273,13 +205,13 @@ int main(int argc, char** argv) int exitCode = 0; switch (action) { case SHOW: - exitCode = handle_show(*library, libraryPath, argc, argv); + exitCode = handle_show(*library, libraryPath, args); break; case ADD: - exitCode = handle_add(library, libraryPath, argc, argv); + exitCode = handle_add(library, libraryPath, args); break; case REMOVE: - exitCode = handle_remove(*library, libraryPath, argc, argv); + exitCode = handle_remove(*library, libraryPath, args); break; case NONE: break; diff --git a/src/searcher/kiwix-search.cpp b/src/searcher/kiwix-search.cpp index 0dcacfbe..4df604a8 100644 --- a/src/searcher/kiwix-search.cpp +++ b/src/searcher/kiwix-search.cpp @@ -17,7 +17,7 @@ * MA 02110-1301, USA. */ -#include +#include #include #include @@ -28,88 +28,69 @@ using namespace std; -void usage() -{ - cout << "Usage: kiwix-search [OPTIONS] ZIM PATTERN" << endl << endl - << " kiwix-search allows one to find articles based on the a fulltext search pattern." << endl << endl - << " ZIM is the full path of the ZIM file." << endl - << " PATTERN is/are word(s) - or part of - to search in the ZIM." << endl << endl - << " -s, --suggestion\tSuggest article titles based on the few letters of the PATTERN instead of making a fulltext search. Work a bit like a completion solution." << endl - << " -v, --verbose\t\tGive details about the search process" << endl - << " -V, --version\t\tPrint software version" << endl; - exit(1); -} + +// Older version of docopt doesn't declare Options. Let's declare it ourself. +using Options = std::map; + +static const char USAGE[] = +R"(Find articles based on a fulltext search pattern. + +Usage: + kiwix-search [options] ZIM PATTERN + kiwix-search -h | --help + kiwix-search -V | --version + +Arguments: + ZIM The full path of the ZIM file + PATTERN Word(s) - or part of - to search in the ZIM. + +Options: + -s --suggestion Suggest article titles based on the few letters of the PATTERN instead of making a fulltext search. Work a bit like a completion solution + -v --verbose Give details about the search process + -V --version Print software version + -h --help Print this help +)"; + int main(int argc, char** argv) { - /* Init the variables */ - // const char *indexPath = - // "/home/itamar/.www.kiwix.org/kiwix/43k0i1j4.default/6d2e587b-d586-dc6a-dc6a-e4ef035a1495d15c.index"; - // const char *indexPath = "/home/itamar/testindex"; - const char* zimPath = NULL; - const char* search = NULL; - bool verboseFlag = false; - bool suggestionFlag = false; - int option_index = 0; - int c = 0; - - /* Argument parsing */ - while (42) { - static struct option long_options[] - = {{"verbose", no_argument, 0, 'v'}, - {"suggestion", no_argument, 0, 's'}, - {"version", no_argument, 0, 'V'}, - {0, 0, 0, 0}}; - - if (c != -1) { - c = getopt_long(argc, argv, "Vvsb:", long_options, &option_index); - - switch (c) { - case 'v': - verboseFlag = true; - break; - case 'V': - version(); - return 0; - case 's': - suggestionFlag = true; - break; - } - } else { - if (optind < argc) { - if (zimPath == NULL) { - zimPath = argv[optind++]; - } else if (search == NULL) { - search = argv[optind++]; - } else { - cout << zimPath << endl; - usage(); - } - } else { - break; - } - } + Options args; + try { + args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false); + } catch (docopt::DocoptArgumentError const & error ) { + std::cerr << error.what() << std::endl; + std::cerr << USAGE << std::endl; + return -1; + } + + if (args.at("--help").asBool()) { + std::cout << USAGE << std::endl; + return 0; } - /* Check if we have enough arguments */ - if (zimPath == NULL || search == NULL) { - usage(); + if (args.at("--version").asBool()) { + version(); + return 0; } + auto zimPath = args.at("ZIM").asString(); + auto pattern = args.at("PATTERN").asString(); + auto verboseFlag = args.at("--verbose").asBool(); + /* Try to prepare the indexing */ try { zim::Archive archive(zimPath); - if (suggestionFlag) { + if (args.at("--suggestion").asBool()) { zim::SuggestionSearcher searcher(archive); searcher.setVerbose(verboseFlag); - for (const auto& r : searcher.suggest(search).getResults(0, 10) ) { + for (const auto& r:searcher.suggest(pattern).getResults(0, 10)) { cout << r.getTitle() << endl; } } else { zim::Searcher searcher(archive); searcher.setVerbose(verboseFlag); - const zim::Query query(search); + const zim::Query query(pattern); for (const auto& r : searcher.search(query).getResults(0, 10) ) { cout << r.getTitle() << endl; } diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index 5d8e3ae0..3db57ca8 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -18,7 +18,7 @@ * MA 02110-1301, USA. */ -#include +#include #include #include #include @@ -41,50 +41,48 @@ #include "../version.h" #define DEFAULT_THREADS 4 - -void usage() -{ - std::cout << "Usage:" << std::endl - << "\tkiwix-serve [OPTIONS] ZIM_PATH+" << std::endl - << "\tkiwix-serve --library [OPTIONS] LIBRARY_PATH" << std::endl - << std::endl - - << "Purpose:" << std::endl - << "\tDeliver ZIM file(s) articles via HTTP" - << std::endl << std::endl - - << "Mandatory arguments:" << std::endl - << "\tLIBRARY_PATH\t\tXML library file path listing ZIM file to serve. To be used only with the --library argument." - << std::endl - << "\tZIM_PATH\t\tZIM file path(s)" - << std::endl << std::endl - - << "Optional arguments:" << std::endl << std::endl - << "\t-h, --help\t\tPrint this help" << std::endl << std::endl - << "\t-a, --attachToProcess\tExit if given process id is not running anymore" << std::endl - << "\t-d, --daemon\t\tDetach the HTTP server daemon from the main process" << std::endl - << "\t-i, --address\t\tListen only on the specified IP address. Specify 'ipv4', 'ipv6' or 'all' to listen on all IPv4, IPv6 or both types of addresses, respectively (default: all)." << std::endl - << "\t-M, --monitorLibrary\tMonitor the XML library file and reload it automatically" << std::endl - << "\t-m, --nolibrarybutton\tDon't print the builtin home button in the builtin top bar overlay" << std::endl - << "\t-n, --nosearchbar\tDon't print the builtin bar overlay on the top of each served page" << std::endl - << "\t-b, --blockexternal\tPrevent users from directly accessing external links" << std::endl - << "\t-p, --port\t\tTCP port on which to listen to HTTP requests (default: 80)" << std::endl - << "\t-r, --urlRootLocation\tURL prefix on which the content should be made available (default: /)" << std::endl - << "\t-s, --searchLimit\tMaximun number of zim in a fulltext multizim search (default: No limit)" << std::endl - << "\t-t, --threads\t\tNumber of threads to run in parallel (default: " << DEFAULT_THREADS << ")" << std::endl - << "\t-v, --verbose\t\tPrint debug log to STDOUT" << std::endl - << "\t-V, --version\t\tPrint software version" << std::endl - << "\t-z, --nodatealiases\tCreate URL aliases for each content by removing the date" << std::endl - << "\t-c, --customIndex\tAdd path to custom index.html for welcome page" << std::endl - << "\t-L, --ipConnectionLimit\tMax number of (concurrent) connections per IP (default: infinite, recommended: >= 6)" << std::endl - << "\t-k, --skipInvalid\tStartup even when ZIM files are invalid (those will be skipped)" << std::endl - << std::endl - - << "Documentation:" << std::endl - << "\tSource code\t\thttps://github.com/kiwix/kiwix-tools" << std::endl - << "\tMore info\t\thttps://wiki.kiwix.org/wiki/Kiwix-serve" << std::endl - << std::endl; -} +#define LITERAL_AS_STR(A) #A +#define AS_STR(A) LITERAL_AS_STR(A) + + +static const char USAGE[] = +R"(Deliver ZIM file(s) articles via HTTP + +Usage: + kiwix-serve [options] ZIMPATH ... + kiwix-serve [options] (-l | --library) LIBRARYPATH + kiwix-serve -h | --help + kiwix-serve -V | --version + +Mandatory arguments: + LIBRARYPATH XML library file path listing ZIM file to serve. To be used only with the --library argument." + ZIMPATH ZIM file path(s) + +Options: + -h --help Print this help + -a= --attachToProcess= Exit if given process id is not running anymore [default: 0] + -d --daemon Detach the HTTP server daemon from the main process + -i=
--address=
Listen only on the specified IP address. Specify 'ipv4', 'ipv6' or 'all' to listen on all IPv4, IPv6 or both types of addresses, respectively [default: all] + -M --monitorLibrary Monitor the XML library file and reload it automatically + -m --nolibrarybutton Don't print the builtin home button in the builtin top bar overlay + -n --nosearchbar Don't print the builtin bar overlay on the top of each served page + -b --blockexternal Prevent users from directly accessing external links + -p= --port= Port on which to listen to HTTP requests [default: 80] + -r= --urlRootLocation= URL prefix on which the content should be made available [default: /] + -s= --searchLimit= Maximun number of zim in a fulltext multizim search [default: 0] + -t= --threads= Number of threads to run in parallel [default: )" AS_STR(DEFAULT_THREADS) R"(] + -v --verbose Print debug log to STDOUT + -V --version Print software version + -z --nodatealiases Create URL aliases for each content by removing the date + -c= --customIndex= Add path to custom index.html for welcome page + -L= --ipConnectionLimit= Max number of (concurrent) connections per IP [default: 0] (recommended: >= 6) + -k --skipInvalid Startup even when ZIM files are invalid (those will be skipped) + +Documentation: + Source code https://github.com/kiwix/kiwix-tools + More info https://wiki.kiwix.org/wiki/Kiwix-serve + https://kiwix-tools.readthedocs.io/en/latest/kiwix-serve.html +)"; std::string loadCustomTemplate (std::string customIndexPath) { customIndexPath = kiwix::isRelativePath(customIndexPath) ? @@ -114,10 +112,9 @@ inline std::string normalizeRootUrl(std::string rootUrl) return rootUrl.empty() ? rootUrl : "/" + rootUrl; } +#ifndef _WIN32 volatile sig_atomic_t waiting = false; volatile sig_atomic_t libraryMustBeReloaded = false; - -#ifndef _WIN32 void handle_sigterm(int signum) { if ( waiting == false ) { @@ -147,6 +144,9 @@ void setup_sighandlers() set_signal_handler(SIGINT, &handle_sigterm); set_signal_handler(SIGHUP, &handle_sighup); } +#else +bool waiting = false; +bool libraryMustBeReloaded = false; #endif uint64_t fileModificationTime(const std::string& path) @@ -191,6 +191,28 @@ bool reloadLibrary(kiwix::Manager& mgr, const std::vector& paths) } } +// docopt::value::isLong() is counting repeated values. +// It doesn't check if the string can be parsed as long. +// (Contrarly to `asLong` which will try to convert string to long) +// See https://github.com/docopt/docopt.cpp/issues/62 +// `isLong` is a small helper to get if the value can be parsed as long. +inline bool isLong(const docopt::value& v) { + try { + v.asLong(); + return true; + } catch (...) { + return false; + } +} + +#define FLAG(NAME, VAR) if (arg.first == NAME) { VAR = arg.second.asBool(); continue; } +#define STRING(NAME, VAR) if (arg.first == NAME && arg.second.isString() ) { VAR = arg.second.asString(); continue; } +#define STRING_LIST(NAME, VAR, ERRORSTR) if (arg.first == NAME) { if (arg.second.isStringList()) { VAR = arg.second.asStringList(); continue; } else { errorString = ERRORSTR; break; } } +#define INT(NAME, VAR, ERRORSTR) if (arg.first == NAME ) { if (isLong(arg.second)) { VAR = arg.second.asLong(); continue; } else { errorString = ERRORSTR; break; } } + +// Older version of docopt doesn't declare Options. Let's declare it ourself. +using Options = std::map; + int main(int argc, char** argv) { #ifndef _WIN32 @@ -207,139 +229,74 @@ int main(int argc, char** argv) std::string customIndexPath=""; std::string indexTemplateString=""; int serverPort = 80; - int daemonFlag [[gnu::unused]] = false; - int libraryFlag = false; + bool daemonFlag [[gnu::unused]] = false; + bool helpFlag = false; bool noLibraryButtonFlag = false; bool noSearchBarFlag = false; bool noDateAliasesFlag = false; bool blockExternalLinks = false; bool isVerboseFlag = false; bool monitorLibrary = false; + bool versionFlag = false; unsigned int PPID = 0; int ipConnectionLimit = 0; int searchLimit = 0; bool skipInvalid = false; - static struct option long_options[] - = {{"daemon", no_argument, 0, 'd'}, - {"help", no_argument, 0, 'h'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"library", no_argument, 0, 'l'}, - {"nolibrarybutton", no_argument, 0, 'm'}, - {"nodatealiases", no_argument, 0, 'z'}, - {"nosearchbar", no_argument, 0, 'n'}, - {"blockexternallinks", no_argument, 0, 'b'}, - {"attachToProcess", required_argument, 0, 'a'}, - {"port", required_argument, 0, 'p'}, - {"address", required_argument, 0, 'i'}, - {"threads", required_argument, 0, 't'}, - {"urlRootLocation", required_argument, 0, 'r'}, - {"customIndex", required_argument, 0, 'c'}, - {"monitorLibrary", no_argument, 0, 'M'}, - {"ipConnectionLimit", required_argument, 0, 'L'}, - {"searchLimit", required_argument, 0, 's'}, - {"skipInvalid", no_argument, 0, 'k'}, - {0, 0, 0, 0}}; - - std::set usedOptions; - /* Argument parsing */ - while (true) { - int option_index = 0; - int c - = getopt_long(argc, argv, "hzmnbdvVla:p:f:t:r:i:c:ML:s:", long_options, &option_index); - - if (c != -1) { - auto insertRes = usedOptions.insert(c); - if (!insertRes.second) { - std::cerr << "Multiple values of same option are not allowed." << std::endl; - exit(1); - } - switch (c) { - case 'h': - usage(); - return 0; - case 'd': - daemonFlag = true; - break; - case 'v': - isVerboseFlag = true; - break; - case 'V': - version(); - return 0; - case 'l': - libraryFlag = true; - break; - case 'n': - noSearchBarFlag = true; - break; - case 'b': - blockExternalLinks = true; - break; - case 'z': - noDateAliasesFlag = true; - break; - case 'm': - noLibraryButtonFlag = true; - break; - case 'p': - serverPort = atoi(optarg); - break; - case 'a': - PPID = atoi(optarg); - break; - case 'i': - address = std::string(optarg); - break; - case 't': - nb_threads = atoi(optarg); - break; - case 'r': - rootLocation = std::string(optarg); - break; - case 'c': - customIndexPath = std::string(optarg); - break; - case 'M': - monitorLibrary = true; - break; - case 'L': - ipConnectionLimit = atoi(optarg); - break; - case 's': - searchLimit = atoi(optarg); - break; - case 'k': - skipInvalid = true; - break; - case '?': - usage(); - return 2; - } - } else { - if (optind < argc) { - if (libraryFlag) { - libraryPath = argv[optind++]; - } else { - while (optind < argc) - zimPathes.push_back(std::string(argv[optind++])); - } - } - break; - } - } + std::string errorString; - /* Print usage)) if necessary */ - if (zimPathes.empty() && libraryPath.empty()) { - usage(); - exit(1); + Options args; + try { + args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false); + } catch (docopt::DocoptArgumentError const & error) { + std::cerr << error.what() << std::endl; + std::cerr << USAGE << std::endl; + return -1; } + for (auto const& arg: args) { + FLAG("--help", helpFlag) + FLAG("--daemon", daemonFlag) + FLAG("--verbose", isVerboseFlag) + FLAG("--nosearchbar", noSearchBarFlag) + FLAG("--blockexternal", blockExternalLinks) + FLAG("--nodatealiases", noDateAliasesFlag) + FLAG("--nolibrarybutton",noLibraryButtonFlag) + FLAG("--monitorLibrary", monitorLibrary) + FLAG("--skipInvalid", skipInvalid) + FLAG("--version", versionFlag) + STRING("LIBRARYPATH", libraryPath) + INT("--port", serverPort, "Port must be an integer") + INT("--attachToProcess", PPID, "Process to attach must be an integer") + STRING("--address", address) + INT("--threads", nb_threads, "Number of threads must be an integer") + STRING("--urlRootLocation", rootLocation) + STRING("--customIndex", customIndexPath) + INT("--ipConnectionLimit", ipConnectionLimit, "IP connection limit must be an integer") + INT("--searchLimit", searchLimit, "Search limit must be an integer") + STRING_LIST("ZIMPATH", zimPathes, "ZIMPATH must be a string list") + } + + if (!errorString.empty()) { + std::cerr << errorString << std::endl; + std::cerr << USAGE << std::endl; + return -1; + } + + if (helpFlag) { + std::cout << USAGE << std::endl; + return 0; + } + + if (versionFlag) { + version(); + return 0; + } + /* Setup the library manager and get the list of books */ kiwix::Manager manager(library); std::vector libraryPaths; - if (libraryFlag) { + if (!libraryPath.empty()) { libraryPaths = kiwix::split(libraryPath, ";"); if ( !reloadLibrary(manager, libraryPaths) ) { exit(1); @@ -468,7 +425,9 @@ int main(int argc, char** argv) if ( monitorLibrary ) { curLibraryFileTimestamp = newestFileTimestamp(libraryPaths); - libraryMustBeReloaded += curLibraryFileTimestamp > libraryFileTimestamp; + if ( !libraryMustBeReloaded ) { + libraryMustBeReloaded = curLibraryFileTimestamp > libraryFileTimestamp; + } } if ( libraryMustBeReloaded && !libraryPaths.empty() ) {