mirror of
https://github.com/kiwix/kiwix-tools.git
synced 2025-09-08 06:38:41 -04:00
Port kiwix-serve to docopt.
This commit is contained in:
parent
9eab766ff9
commit
76b617817a
@ -20,8 +20,9 @@ libzim_dep = dependency('libzim', version:'>=9.0.0', static:static_linkage)
|
|||||||
libzim_dep = dependency('libzim', version:'<10.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:'>=13.0.0', static:static_linkage)
|
||||||
kiwixlib_dep = dependency('kiwix', version:'<14.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
|
if static_linkage
|
||||||
librt = compiler.find_library('rt', required:false)
|
librt = compiler.find_library('rt', required:false)
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
* MA 02110-1301, USA.
|
* MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <docopt/docopt.h>
|
||||||
#include <kiwix/manager.h>
|
#include <kiwix/manager.h>
|
||||||
#include <kiwix/server.h>
|
#include <kiwix/server.h>
|
||||||
#include <kiwix/name_mapper.h>
|
#include <kiwix/name_mapper.h>
|
||||||
@ -41,50 +41,47 @@
|
|||||||
#include "../version.h"
|
#include "../version.h"
|
||||||
|
|
||||||
#define DEFAULT_THREADS 4
|
#define DEFAULT_THREADS 4
|
||||||
|
#define LITERAL_AS_STR(A) #A
|
||||||
|
#define AS_STR(A) LITERAL_AS_STR(A)
|
||||||
|
|
||||||
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
|
static const char USAGE[] =
|
||||||
<< "\tDeliver ZIM file(s) articles via HTTP"
|
R"(Deliver ZIM file(s) articles via HTTP
|
||||||
<< std::endl << std::endl
|
|
||||||
|
|
||||||
<< "Mandatory arguments:" << std::endl
|
Usage:
|
||||||
<< "\tLIBRARY_PATH\t\tXML library file path listing ZIM file to serve. To be used only with the --library argument."
|
kiwix-serve [options] ZIMPATH ...
|
||||||
<< std::endl
|
kiwix-serve [options] (-l | --library) LIBRARYPATH
|
||||||
<< "\tZIM_PATH\t\tZIM file path(s)"
|
kiwix-serve -h | --help
|
||||||
<< std::endl << std::endl
|
kiwix-serve -V | --version
|
||||||
|
|
||||||
<< "Optional arguments:" << std::endl << std::endl
|
Mandatory arguments:
|
||||||
<< "\t-h, --help\t\tPrint this help" << std::endl << std::endl
|
LIBRARYPATH XML library file path listing ZIM file to serve. To be used only with the --library argument."
|
||||||
<< "\t-a, --attachToProcess\tExit if given process id is not running anymore" << std::endl
|
ZIMPATH ZIM file path(s)
|
||||||
<< "\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
|
Options:
|
||||||
<< "\tSource code\t\thttps://github.com/kiwix/kiwix-tools" << std::endl
|
-h --help Print this help
|
||||||
<< "\tMore info\t\thttps://wiki.kiwix.org/wiki/Kiwix-serve" << std::endl
|
-a=<pid> --attachToProcess=<pid> Exit if given process id is not running anymore [default: 0]
|
||||||
<< std::endl;
|
-d --daemon Detach the HTTP server daemon from the main process
|
||||||
}
|
-i=<address> --address=<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=<port> Port on which to listen to HTTP requests [default: 80]
|
||||||
|
-r=<root> --urlRootLocation=<root> URL prefix on which the content should be made available [default: /]
|
||||||
|
-s=<limit> --searchLimit=<limit> Maximun number of zim in a fulltext multizim search [default: 0]
|
||||||
|
-t=<threads> --threads=<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=<path> --customIndex=<path> Add path to custom index.html for welcome page
|
||||||
|
-L=<limit> --ipConnectionLimit=<limit> 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
|
||||||
|
)";
|
||||||
|
|
||||||
std::string loadCustomTemplate (std::string customIndexPath) {
|
std::string loadCustomTemplate (std::string customIndexPath) {
|
||||||
customIndexPath = kiwix::isRelativePath(customIndexPath) ?
|
customIndexPath = kiwix::isRelativePath(customIndexPath) ?
|
||||||
@ -191,6 +188,28 @@ bool reloadLibrary(kiwix::Manager& mgr, const std::vector<std::string>& 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<std::string, docopt::value>;
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@ -207,139 +226,74 @@ int main(int argc, char** argv)
|
|||||||
std::string customIndexPath="";
|
std::string customIndexPath="";
|
||||||
std::string indexTemplateString="";
|
std::string indexTemplateString="";
|
||||||
int serverPort = 80;
|
int serverPort = 80;
|
||||||
int daemonFlag [[gnu::unused]] = false;
|
bool daemonFlag [[gnu::unused]] = false;
|
||||||
int libraryFlag = false;
|
bool helpFlag = false;
|
||||||
bool noLibraryButtonFlag = false;
|
bool noLibraryButtonFlag = false;
|
||||||
bool noSearchBarFlag = false;
|
bool noSearchBarFlag = false;
|
||||||
bool noDateAliasesFlag = false;
|
bool noDateAliasesFlag = false;
|
||||||
bool blockExternalLinks = false;
|
bool blockExternalLinks = false;
|
||||||
bool isVerboseFlag = false;
|
bool isVerboseFlag = false;
|
||||||
bool monitorLibrary = false;
|
bool monitorLibrary = false;
|
||||||
|
bool versionFlag = false;
|
||||||
unsigned int PPID = 0;
|
unsigned int PPID = 0;
|
||||||
int ipConnectionLimit = 0;
|
int ipConnectionLimit = 0;
|
||||||
int searchLimit = 0;
|
int searchLimit = 0;
|
||||||
bool skipInvalid = false;
|
bool skipInvalid = false;
|
||||||
|
|
||||||
static struct option long_options[]
|
std::string errorString;
|
||||||
= {{"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<int> usedOptions;
|
Options args;
|
||||||
/* Argument parsing */
|
try {
|
||||||
while (true) {
|
args = docopt::docopt_parse(USAGE, {argv+1, argv+argc}, false, false);
|
||||||
int option_index = 0;
|
} catch (docopt::DocoptArgumentError const & error) {
|
||||||
int c
|
std::cerr << error.what() << std::endl;
|
||||||
= getopt_long(argc, argv, "hzmnbdvVla:p:f:t:r:i:c:ML:s:", long_options, &option_index);
|
std::cerr << USAGE << std::endl;
|
||||||
|
return -1;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print usage)) if necessary */
|
for (auto const& arg: args) {
|
||||||
if (zimPathes.empty() && libraryPath.empty()) {
|
FLAG("--help", helpFlag)
|
||||||
usage();
|
FLAG("--daemon", daemonFlag)
|
||||||
exit(1);
|
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 */
|
/* Setup the library manager and get the list of books */
|
||||||
kiwix::Manager manager(library);
|
kiwix::Manager manager(library);
|
||||||
std::vector<std::string> libraryPaths;
|
std::vector<std::string> libraryPaths;
|
||||||
if (libraryFlag) {
|
if (!libraryPath.empty()) {
|
||||||
libraryPaths = kiwix::split(libraryPath, ";");
|
libraryPaths = kiwix::split(libraryPath, ";");
|
||||||
if ( !reloadLibrary(manager, libraryPaths) ) {
|
if ( !reloadLibrary(manager, libraryPaths) ) {
|
||||||
exit(1);
|
exit(1);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user