mirror of
https://github.com/kiwix/kiwix-tools.git
synced 2025-09-20 10:14:01 -04:00
commit
98483170c9
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
2
debian/control
vendored
2
debian/control
vendored
@ -5,6 +5,8 @@ Maintainer: Kiwix team <kiwix@kiwix.org>
|
||||
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
|
||||
|
@ -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)
|
||||
|
@ -17,7 +17,7 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <docopt/docopt.h>
|
||||
#include <kiwix/manager.h>
|
||||
#include <kiwix/tools.h>
|
||||
#include <cstdlib>
|
||||
@ -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<std::string, docopt::value>;
|
||||
|
||||
|
||||
/* 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
|
||||
static const char USAGE[] =
|
||||
R"(Manipulates the Kiwix library XML file
|
||||
|
||||
<< "Purpose:" << std::endl
|
||||
<< "\tManipulates the Kiwix library XML file"
|
||||
<< std::endl << std::endl
|
||||
Usage:
|
||||
kiwix-manage LIBRARYPATH add [--zimPathToSave=<custom_zim_path>] [--url=<http_zim_url>] ZIMPATH
|
||||
kiwix-manage LIBRARYPATH remove|delete ZIMID ...
|
||||
kiwix-manage LIBRARYPATH show [ZIMID ...]
|
||||
kiwix-manage -v | --version
|
||||
kiwix-manage -h | --help
|
||||
|
||||
<< "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
|
||||
Arguments:
|
||||
LIBRARYPATH The XML library file path.
|
||||
ZIMID ZIM file unique ID.
|
||||
ZIMPATH A path to a zim to add.
|
||||
|
||||
<< "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
|
||||
Options:
|
||||
Custom options for "add" action:
|
||||
--zimPathToSave=<custom_zim_path> Replace the current ZIM file path
|
||||
--url=<http_zim_url> Create an "url" attribute for the online version of the ZIM file
|
||||
|
||||
<< "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;
|
||||
}
|
||||
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; i++) {
|
||||
std::string bookId = argv[i];
|
||||
if (options.at("ZIMID").isStringList()) {
|
||||
auto bookIds = options.at("ZIMID").asStringList();
|
||||
for(auto& bookId: bookIds) {
|
||||
show(library, bookId);
|
||||
}
|
||||
} else {
|
||||
@ -114,95 +108,45 @@ int handle_show(const kiwix::Library& library, const std::string& libraryPath,
|
||||
}
|
||||
|
||||
int handle_add(kiwix::LibraryPtr library, const std::string& libraryPath,
|
||||
int argc, char* argv[])
|
||||
const Options& options)
|
||||
{
|
||||
string zimPath;
|
||||
string zimPathToSave = ".";
|
||||
string zimPathToSave;
|
||||
string url;
|
||||
int option_index = 0;
|
||||
int c = 0;
|
||||
int resultCode = 0;
|
||||
|
||||
if (argc <= 3) {
|
||||
std::cerr << "Path to zim file to add is missing in the command line" << std::endl;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Options parsing */
|
||||
optind = 3;
|
||||
static struct option long_options[] = {
|
||||
{"url", required_argument, 0, 'u'},
|
||||
{"zimPathToSave", required_argument, 0, 'z'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
bool has_option = false;
|
||||
while (true) {
|
||||
c = getopt_long(argc, argv, "cz:u:", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
has_option = true;
|
||||
switch (c) {
|
||||
case 'u':
|
||||
url = optarg;
|
||||
break;
|
||||
case 'z':
|
||||
zimPathToSave = optarg;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
std::cerr << "Path to zim file to add is missing in the command line" << std::endl;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (has_option && argc-optind > 1) {
|
||||
std::cerr << "You cannot give option and several zim files to add" << std::endl;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kiwix::Manager manager(library);
|
||||
|
||||
for(auto i=optind; i<argc; i++) {
|
||||
std::string zimPath = argv[i];
|
||||
if (!zimPath.empty()) {
|
||||
auto _zimPathToSave = zimPathToSave == "." ? zimPath : zimPathToSave;
|
||||
if (manager.addBookFromPathAndGetId(zimPath, _zimPathToSave, url, false).empty()) {
|
||||
std::cerr << "Cannot add zim " << zimPath << " to the library." << std::endl;
|
||||
resultCode = 1;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Invalid zim file path" << std::endl;
|
||||
resultCode = 1;
|
||||
}
|
||||
std::string zimPath = options.at("ZIMPATH").asString();
|
||||
if (options.at("--zimPathToSave").isString()) {
|
||||
zimPathToSave = options.at("--zimPathToSave").asString();
|
||||
} else {
|
||||
zimPathToSave = zimPath;
|
||||
}
|
||||
if (options.at("--url").isString()) {
|
||||
url = options.at("--url").asString();
|
||||
}
|
||||
|
||||
return(resultCode);
|
||||
if (manager.addBookFromPathAndGetId(zimPath, zimPathToSave, url, false).empty()) {
|
||||
std::cerr << "Cannot add zim " << zimPath << " to the library." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handle_remove(kiwix::Library& library, const std::string& libraryPath,
|
||||
int argc, char* argv[])
|
||||
const Options& options)
|
||||
{
|
||||
std::string bookId;
|
||||
const unsigned int totalBookCount = library.getBookCount(true, true);
|
||||
int exitCode = 0;
|
||||
|
||||
if (argc <= 3) {
|
||||
std::cerr << "BookId to remove missing in the command line" << std::endl;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (!totalBookCount) {
|
||||
std::cerr << "Library is empty, no book to delete."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 3; i<argc; i++) {
|
||||
bookId = argv[i];
|
||||
|
||||
auto bookIds = options.at("ZIMID").asStringList();
|
||||
for (auto& bookId: bookIds) {
|
||||
if (!library.removeBookById(bookId)) {
|
||||
std::cerr << "Invalid book id '" << bookId << "'." << std::endl;
|
||||
exitCode = 1;
|
||||
@ -214,49 +158,37 @@ int handle_remove(kiwix::Library& library, const std::string& libraryPath,
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
string libraryPath = "";
|
||||
supportedAction action = NONE;
|
||||
auto library = kiwix::Library::create();
|
||||
|
||||
/* General argument parsing */
|
||||
static struct option long_options[] = {
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int option_index = 0;
|
||||
int c;
|
||||
while (true && argc == 2) {
|
||||
c = getopt_long(argc, argv, "v", long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'v':
|
||||
version();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Action related argument parsing */
|
||||
if (argc > 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;
|
||||
}
|
||||
|
||||
/* Print usage)) if necessary */
|
||||
if (libraryPath == "" || action == NONE) {
|
||||
usage();
|
||||
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["--help"].asBool()) {
|
||||
std::cout << USAGE << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -17,7 +17,7 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <docopt/docopt.h>
|
||||
|
||||
#include <zim/search.h>
|
||||
#include <zim/suggestion.h>
|
||||
@ -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<std::string, docopt::value>;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Check if we have enough arguments */
|
||||
if (zimPath == NULL || search == NULL) {
|
||||
usage();
|
||||
if (args.at("--help").asBool()) {
|
||||
std::cout << USAGE << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <docopt/docopt.h>
|
||||
#include <kiwix/manager.h>
|
||||
#include <kiwix/server.h>
|
||||
#include <kiwix/name_mapper.h>
|
||||
@ -41,50 +41,48 @@
|
||||
#include "../version.h"
|
||||
|
||||
#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
|
||||
<< "\tDeliver ZIM file(s) articles via HTTP"
|
||||
<< std::endl << std::endl
|
||||
static const char USAGE[] =
|
||||
R"(Deliver ZIM file(s) articles via HTTP
|
||||
|
||||
<< "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
|
||||
Usage:
|
||||
kiwix-serve [options] ZIMPATH ...
|
||||
kiwix-serve [options] (-l | --library) LIBRARYPATH
|
||||
kiwix-serve -h | --help
|
||||
kiwix-serve -V | --version
|
||||
|
||||
<< "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
|
||||
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)
|
||||
|
||||
<< "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;
|
||||
}
|
||||
Options:
|
||||
-h --help Print this help
|
||||
-a=<pid> --attachToProcess=<pid> Exit if given process id is not running anymore [default: 0]
|
||||
-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
|
||||
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<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)
|
||||
{
|
||||
#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::string errorString;
|
||||
|
||||
std::set<int> 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* Print usage)) if necessary */
|
||||
if (zimPathes.empty() && libraryPath.empty()) {
|
||||
usage();
|
||||
exit(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<std::string> 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() ) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user