mirror of
https://github.com/kiwix/kiwix-tools.git
synced 2026-01-08 14:08:32 -05:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2308b02504 | ||
|
|
81d9a6cc02 | ||
|
|
0232f0a95f | ||
|
|
309d4d01f1 | ||
|
|
abcbe0c9df | ||
|
|
d08638429e | ||
|
|
5348495c2b | ||
|
|
9b91ff1529 | ||
|
|
6f599589f9 | ||
|
|
6272c6d11e | ||
|
|
0bcb3957e5 | ||
|
|
16f2734699 | ||
|
|
45f0b0601e | ||
|
|
d61eafe6a2 | ||
|
|
ae21ba42da | ||
|
|
830a9e5e29 | ||
|
|
b22c10f6ff | ||
|
|
863fe0f973 | ||
|
|
769182731c | ||
|
|
b679e5d159 | ||
|
|
a6af7707e4 | ||
|
|
3ebd070d20 | ||
|
|
6ed59398e4 | ||
|
|
80dcf95b7f | ||
|
|
90f4562408 | ||
|
|
869b08cf71 | ||
|
|
ae1640736c | ||
|
|
40d32cfc32 | ||
|
|
463015da45 | ||
|
|
0ba30b5525 | ||
|
|
e22cfea9d9 | ||
|
|
fcc8877cb0 | ||
|
|
5fdea94c05 | ||
|
|
597223196d | ||
|
|
824eddaf94 | ||
|
|
7413682060 | ||
|
|
0125188bc0 | ||
|
|
acad8a85ab | ||
|
|
2e4cae72ef | ||
|
|
5c2a483929 | ||
|
|
0d38d071a8 | ||
|
|
0091ffa1ca | ||
|
|
43b00419dd | ||
|
|
f2c78ca03b | ||
|
|
73f357a627 | ||
|
|
0fd4abeeae | ||
|
|
998bbc6b0e | ||
|
|
e12687b1ec | ||
|
|
e6ff088ffa | ||
|
|
87c6eb837d | ||
|
|
88ceebab8a | ||
|
|
dac84999bc | ||
|
|
5b8d72de5d | ||
|
|
e65354a486 | ||
|
|
55b42f27b6 | ||
|
|
24c0b7220e | ||
|
|
b1dd681c59 | ||
|
|
c6b72e9362 | ||
|
|
362db1094d | ||
|
|
7512611146 | ||
|
|
3599e8476d | ||
|
|
5e222b3f0b | ||
|
|
ce3c3bd153 | ||
|
|
da22757a14 | ||
|
|
f0a8643603 | ||
|
|
84faf9c09e | ||
|
|
f912519647 | ||
|
|
e609c1c826 | ||
|
|
fdcea9889f | ||
|
|
4ec05bf98e | ||
|
|
2bab9a210e | ||
|
|
28008efb28 | ||
|
|
f6baad903f | ||
|
|
984741de03 | ||
|
|
b557d0a1dc | ||
|
|
98483170c9 | ||
|
|
c94c1bae6c | ||
|
|
b45426640e | ||
|
|
504affb481 | ||
|
|
6a4b7508f0 | ||
|
|
cebe079c11 | ||
|
|
945c4d61e6 | ||
|
|
504a7a1ab7 | ||
|
|
76b617817a | ||
|
|
9eab766ff9 | ||
|
|
4b3d6202e7 | ||
|
|
e18ffe19dc | ||
|
|
353d20bf83 | ||
|
|
5f69a41259 | ||
|
|
2cc1ca4017 | ||
|
|
dc1067b8c4 | ||
|
|
5e7166cb07 | ||
|
|
24d81f9507 |
100
.github/workflows/ci.yml
vendored
100
.github/workflows/ci.yml
vendored
@@ -1,60 +1,106 @@
|
||||
name: CI
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 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=release
|
||||
cd build
|
||||
ninja.exe
|
||||
|
||||
- name: Test
|
||||
shell: cmd
|
||||
run: |
|
||||
cd build
|
||||
meson.exe test --verbose
|
||||
env:
|
||||
WAIT_TIME_FACTOR_TEST: 10
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- native_static
|
||||
- native_dyn
|
||||
- win32_static
|
||||
- win32_dyn
|
||||
- linux-x86_64-static
|
||||
- linux-x86_64-dyn
|
||||
include:
|
||||
- target: native_static
|
||||
image_variant: focal
|
||||
- target: linux-x86_64-static
|
||||
image_variant: jammy
|
||||
lib_postfix: '/x86_64-linux-gnu'
|
||||
- target: native_dyn
|
||||
image_variant: focal
|
||||
arch_name: linux-x86_64
|
||||
- target: linux-x86_64-dyn
|
||||
image_variant: jammy
|
||||
lib_postfix: '/x86_64-linux-gnu'
|
||||
- target: win32_static
|
||||
image_variant: f35
|
||||
lib_postfix: '64'
|
||||
- target: win32_dyn
|
||||
image_variant: f35
|
||||
lib_postfix: '64'
|
||||
arch_name: linux-x86_64
|
||||
|
||||
env:
|
||||
HOME: /home/runner
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:36"
|
||||
image: "ghcr.io/kiwix/kiwix-build_ci_${{matrix.image_variant}}:2025-06-07"
|
||||
|
||||
steps:
|
||||
- name: Extract branch name
|
||||
shell: bash
|
||||
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
||||
id: extract_branch
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
uses: kiwix/kiwix-build/actions/dl_deps_archive@main
|
||||
with:
|
||||
target_platform: ${{ matrix.target }}
|
||||
|
||||
- name: Compile
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{matrix.target}}" =~ .*_static ]]; then
|
||||
if [[ "${{matrix.target}}" =~ .*-static ]]; then
|
||||
MESON_OPTION="-Dstatic-linkage=true"
|
||||
else
|
||||
MESON_OPTION=""
|
||||
fi
|
||||
if [[ ! "${{matrix.target}}" =~ native_.* ]]; then
|
||||
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.target}}/meson_cross_file.txt"
|
||||
if [ -e "$HOME/BUILD_${{matrix.arch_name}}/meson_cross_file.txt" ]; then
|
||||
MESON_OPTION="$MESON_OPTION --cross-file $HOME/BUILD_${{matrix.arch_name}}/meson_cross_file.txt"
|
||||
fi
|
||||
meson . build ${MESON_OPTION}
|
||||
cd build
|
||||
ninja
|
||||
env:
|
||||
PKG_CONFIG_PATH: "${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/lib/pkgconfig:${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
|
||||
CPPFLAGS: "-I${{env.HOME}}/BUILD_${{matrix.target}}/INSTALL/include"
|
||||
PKG_CONFIG_PATH: "${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib/pkgconfig:${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/lib${{matrix.lib_postfix}}/pkgconfig"
|
||||
CPPFLAGS: "-I${{env.HOME}}/BUILD_${{matrix.arch_name}}/INSTALL/include"
|
||||
|
||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
name: Deploy kiwix-tools Docker Image
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3.4.0
|
||||
- uses: actions/checkout@v4
|
||||
- name: build and publish kiwix-tools
|
||||
uses: openzim/docker-publish-action@v10
|
||||
with:
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-and-push-kiwix-tools
|
||||
steps:
|
||||
- uses: actions/checkout@v3.4.0
|
||||
- uses: actions/checkout@v4
|
||||
- name: build and publish kiwix-serve
|
||||
uses: openzim/docker-publish-action@v10
|
||||
with:
|
||||
|
||||
77
.github/workflows/package.yml
vendored
77
.github/workflows/package.yml
vendored
@@ -1,17 +1,28 @@
|
||||
name: Packages
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build-deb:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
distro:
|
||||
# - debian-unstable
|
||||
# - debian-trixie
|
||||
# - debian-bookworm
|
||||
# - debian-bullseye
|
||||
- ubuntu-noble
|
||||
- ubuntu-jammy
|
||||
- ubuntu-focal
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Determine which PPA we should upload to
|
||||
- name: PPA
|
||||
@@ -19,19 +30,55 @@ jobs:
|
||||
run: |
|
||||
if [[ $REF == refs/tags* ]]
|
||||
then
|
||||
echo "::set-output name=ppa::kiwixteam/release"
|
||||
echo "ppa=kiwixteam/release" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "::set-output name=ppa::kiwixteam/dev"
|
||||
echo "ppa=kiwixteam/dev" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
REF: ${{ github.ref }}
|
||||
|
||||
- uses: legoktm/gh-action-auto-dch@master
|
||||
- uses: legoktm/gh-action-auto-dch@main
|
||||
with:
|
||||
fullname: Kiwix builder
|
||||
email: release+launchpad@kiwix.org
|
||||
distro: ${{ matrix.distro }}
|
||||
|
||||
# - uses: legoktm/gh-action-build-deb@debian-unstable
|
||||
# if: matrix.distro == 'debian-unstable'
|
||||
# name: Build package for debian-unstable
|
||||
# id: build-debian-unstable
|
||||
# with:
|
||||
# args: --no-sign
|
||||
#
|
||||
# - uses: legoktm/gh-action-build-deb@b47978ba8498dc8b8153cc3b5f99a5fc1afa5de1 # pin@debian-trixie
|
||||
# if: matrix.distro == 'debian-trixie'
|
||||
# name: Build package for debian-trixie
|
||||
# id: build-debian-trixie
|
||||
# with:
|
||||
# args: --no-sign
|
||||
#
|
||||
# - uses: legoktm/gh-action-build-deb@1f4e86a6bb34aaad388167eaf5eb85d553935336 # pin@debian-bookworm
|
||||
# if: matrix.distro == 'debian-bookworm'
|
||||
# name: Build package for debian-bookworm
|
||||
# id: build-debian-bookworm
|
||||
# with:
|
||||
# args: --no-sign
|
||||
#
|
||||
# - uses: legoktm/gh-action-build-deb@084b4263209252ec80a75d2c78a586192c17f18d # pin@debian-bullseye
|
||||
# if: matrix.distro == 'debian-bullseye'
|
||||
# name: Build package for debian-bullseye
|
||||
# id: build-debian-bullseye
|
||||
# with:
|
||||
# args: --no-sign
|
||||
|
||||
- uses: legoktm/gh-action-build-deb@9114a536498b65c40b932209b9833aa942bf108d # pin@ubuntu-noble
|
||||
if: matrix.distro == 'ubuntu-noble'
|
||||
name: Build package for ubuntu-noble
|
||||
id: build-ubuntu-noble
|
||||
with:
|
||||
args: --no-sign
|
||||
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||
|
||||
- uses: legoktm/gh-action-build-deb@ubuntu-jammy
|
||||
if: matrix.distro == 'ubuntu-jammy'
|
||||
name: Build package for ubuntu-jammy
|
||||
@@ -40,20 +87,12 @@ jobs:
|
||||
args: --no-sign
|
||||
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||
|
||||
- uses: legoktm/gh-action-build-deb@ubuntu-focal
|
||||
if: matrix.distro == 'ubuntu-focal'
|
||||
name: Build package for ubuntu-focal
|
||||
id: build-ubuntu-focal
|
||||
with:
|
||||
args: --no-sign
|
||||
ppa: ${{ steps.ppa.outputs.ppa }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Packages for ${{ matrix.distro }}
|
||||
path: output
|
||||
|
||||
- uses: legoktm/gh-action-dput@master
|
||||
- uses: legoktm/gh-action-dput@main
|
||||
name: Upload dev package
|
||||
# Only upload on pushes to git default branch
|
||||
if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' && startswith(matrix.distro, 'ubuntu-')
|
||||
@@ -62,9 +101,9 @@ jobs:
|
||||
repository: ppa:kiwixteam/dev
|
||||
packages: output/*_source.changes
|
||||
|
||||
- uses: legoktm/gh-action-dput@master
|
||||
- uses: legoktm/gh-action-dput@main
|
||||
name: Upload release package
|
||||
# Only upload on pushes to master or tag
|
||||
# Only upload on pushes to main or tag
|
||||
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') && startswith(matrix.distro, 'ubuntu-')
|
||||
with:
|
||||
gpg_key: ${{ secrets.LAUNCHPAD_GPG }}
|
||||
|
||||
1
.well-known/funding-manifest-urls
Normal file
1
.well-known/funding-manifest-urls
Normal file
@@ -0,0 +1 @@
|
||||
https://kiwix.org/funding.json
|
||||
29
Changelog
29
Changelog
@@ -1,3 +1,32 @@
|
||||
kiwix-tools 3.8.1
|
||||
=================
|
||||
|
||||
* Kiwix server
|
||||
- Hide port number in URL when server is running on port 80 (@vighnesh-sawant #763)
|
||||
- Better deal with container /data dir permissions (@kelson42 #787)
|
||||
|
||||
* Other
|
||||
- Fix kiwix-manage docopt integration (@kelson42 #783)
|
||||
|
||||
kiwix-tools 3.8.0
|
||||
=================
|
||||
|
||||
* Kiwix server
|
||||
- Improve message when server is running on standard port (@vighnesh-sawant #763)
|
||||
- Update container base image to latest Alpline version (@yashgoyal0110 #771)
|
||||
- Container image to use unprivileged user (@Sedetisu #755)
|
||||
- Empty urlRootLocation doesn't disable book preview links anymore (@veloman-yunkan #1224)
|
||||
- Add support of IPv6 (@sgourdas #673 #704)
|
||||
- Popups are allowed to escape the browser sandbox (@veloman-yunkan #1208)
|
||||
|
||||
* Other
|
||||
- Stop publishing on Ubuntu 20.04 PPA (@kelson42 #748)
|
||||
- Use Docopt for command line argument parsing (@mgautierfr #695)
|
||||
|
||||
* Compilation & Packaging
|
||||
- Based on libkiwix 14.1.0 (@kelson42 #773)
|
||||
- Improve CI/CD (@kelson42 #681 #698 #702 #705 #720 #722, @mgautierfr #695)
|
||||
|
||||
kiwix-tools 3.7.0
|
||||
=================
|
||||
|
||||
|
||||
@@ -9,10 +9,9 @@ command line tools:
|
||||
|
||||
[](https://download.kiwix.org/release/kiwix-tools/)
|
||||
[](https://github.com/kiwix/kiwix-tools/wiki/Repology)
|
||||
[](https://ghcr.io/kiwix/kiwix-tools)
|
||||
[)](https://ghcr.io/kiwix/kiwix-tools)
|
||||
[](https://apps.sandstorm.io/app/5uh349d0kky2zp5whrh2znahn27gwha876xze3864n0fu9e5220h)
|
||||
[](https://github.com/kiwix/kiwix-tools/actions?query=branch%3Amain)
|
||||
[](https://ghcr.io/kiwix/kiwix-tools)
|
||||
[)](https://ghcr.io/kiwix/kiwix-tools)
|
||||
[](https://github.com/kiwix/kiwix-tools/actions/workflows/ci.yml?query=branch%3Amain)
|
||||
[](https://kiwix-tools.readthedocs.org/en/latest/?badge=latest)
|
||||
[](https://www.codefactor.io/repository/github/kiwix/kiwix-tools)
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
|
||||
10
debian/control
vendored
10
debian/control
vendored
@@ -3,11 +3,13 @@ Section: utils
|
||||
Priority: optional
|
||||
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),
|
||||
libzim-dev (>= 9.0), libzim-dev (<< 10.0),
|
||||
libkiwix-dev (>= 14.0), libkiwix-dev (<< 15.0),
|
||||
cmake,
|
||||
libdocopt-dev,
|
||||
meson,
|
||||
pkg-config,
|
||||
Standards-Version: 4.5.0
|
||||
pkgconf,
|
||||
Standards-Version: 4.6.2
|
||||
Homepage: https://github.com/kiwix/kiwix-tools
|
||||
Rules-Requires-Root: no
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
FROM alpine:3.18
|
||||
LABEL org.opencontainers.image.source https://github.com/openzim/kiwix-tools
|
||||
FROM alpine:3.22
|
||||
LABEL org.opencontainers.image.source=https://github.com/openzim/kiwix-tools
|
||||
|
||||
# TARGETPLATFORM is injected by docker build
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
@@ -2,13 +2,18 @@ ARG VERSION=latest
|
||||
|
||||
# kiwix-tools is multi-arch
|
||||
FROM ghcr.io/kiwix/kiwix-tools:$VERSION
|
||||
LABEL org.opencontainers.image.source https://github.com/openzim/kiwix-tools
|
||||
LABEL org.opencontainers.image.source=https://github.com/openzim/kiwix-tools
|
||||
|
||||
# expose kiwix-serve default port and workdir
|
||||
EXPOSE 8080
|
||||
VOLUME /data
|
||||
WORKDIR /data
|
||||
|
||||
# running as a named unprivileged user
|
||||
RUN addgroup -S -g 1001 user && adduser -S -u 1001 user -G user
|
||||
RUN chown user:user /data
|
||||
USER user
|
||||
|
||||
COPY ./start.sh /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["/usr/bin/dumb-init", "--", "/usr/local/bin/start.sh"]
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
# Download if necessary a file
|
||||
if [ ! -z "$DOWNLOAD" ]
|
||||
then
|
||||
# Check if /data is writable
|
||||
if [ ! -w /data ]
|
||||
then
|
||||
echo "'/data' directory is not writable by '$(id -n -u):$(id -n -g)' ($(id -u):$(id -g)). ZIM file(s) can not be written."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dwonload ZIM file
|
||||
ZIM=`basename $DOWNLOAD`
|
||||
wget $DOWNLOAD -O "$ZIM"
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ file).
|
||||
Clients can also remotely search inside those ZIM files that contain a full-text
|
||||
search database.
|
||||
|
||||
``kiwix-serve`` supports Web browsers `Firefox >= 70, Chrome >= 80, Edge >= 80, ChromeAndroid >= 80, Safari >= 14, iOS >= 14 <https://browsersl.ist/#q=Firefox+%3E%3D+70%2C+Chrome+%3E%3D+80%2C+Edge+%3E%3D+80%2C+ChromeAndroid+%3E%3D+80%2C+Safari+%3E%3D+14%2C+iOS+%3E%3D+14>`_.
|
||||
|
||||
Usage
|
||||
=====
|
||||
@@ -48,10 +49,32 @@ Options
|
||||
that the command line argument is rather a :ref:`library XML file
|
||||
<cli-arg-library-file-path>`.
|
||||
|
||||
.. option:: --catalogOnly
|
||||
|
||||
In this mode ``kiwix-serve`` only serves the welcome (library) page and the
|
||||
OPDS catalog. ZIM files referred by the :ref:`library XML file
|
||||
<cli-arg-library-file-path>` need not be accessible.
|
||||
|
||||
This option may be combined with the :option:`--contentServerURL` option.
|
||||
|
||||
.. option:: --contentServerURL=URL
|
||||
|
||||
In :option:`--catalogOnly` mode book content is not served by this instance
|
||||
of `kiwix-serve`. If a separate instance of `kiwix-serve` is running for the
|
||||
same library without that option and thus serves book content, then the root
|
||||
URL of that server can be passed to this instance so that books can still be
|
||||
previewed.
|
||||
|
||||
This option must be combined with the :option:`--catalogOnly` option.
|
||||
|
||||
.. option:: -i ADDR, --address=ADDR
|
||||
|
||||
Listen only on this IP address. By default the server listens on all
|
||||
available IP addresses.
|
||||
available IP addresses. Alternatively, you can use special values to define which types of connections to accept:
|
||||
|
||||
- all : Listen for connections on all IP addresses (IPv4 and IPv6).
|
||||
- ipv4 : Listen for connections on all IPv4 addresses.
|
||||
- ipv6 : Listen for connections on all IPv6 addresses.
|
||||
|
||||
|
||||
.. option:: -p PORT, --port=PORT
|
||||
@@ -654,10 +677,10 @@ A multi-ZIM search request must comply with the following constraints:
|
||||
``pageLength`` (optional, default: 25): maximum number of search results in
|
||||
the response. Capped at 140.
|
||||
|
||||
``start`` (optional, default: 1): this parameter enables pagination of
|
||||
``start`` (optional, default: 0): this parameter enables pagination of
|
||||
results. The response will include up to ``pageLength`` results starting
|
||||
with entry # ``start`` from the full list of search results (the first
|
||||
result is assumed to have index 1).
|
||||
result is assumed to have index 0).
|
||||
|
||||
Other parameters:
|
||||
|
||||
|
||||
15
meson.build
15
meson.build
@@ -1,5 +1,5 @@
|
||||
project('kiwix-tools', 'cpp',
|
||||
version : '3.7.0',
|
||||
version : '3.8.1',
|
||||
license : 'GPL',
|
||||
default_options: ['c_std=c11', 'cpp_std=c++17', 'werror=true'])
|
||||
|
||||
@@ -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
|
||||
@@ -16,12 +20,11 @@ if static_linkage
|
||||
endif
|
||||
|
||||
thread_dep = dependency('threads')
|
||||
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)
|
||||
libzim_dep = dependency('libzim', version:['>=9.0.0', '<10.0.0'], static:static_linkage)
|
||||
libkiwix_dep = dependency('libkiwix', version:['>=14.1.0', '<15.0.0'], static:static_linkage)
|
||||
libdocopt_dep = dependency('docopt', static:static_linkage)
|
||||
|
||||
all_deps = [thread_dep, kiwixlib_dep, libzim_dep]
|
||||
all_deps = [thread_dep, libkiwix_dep, libzim_dep, libdocopt_dep]
|
||||
|
||||
if static_linkage
|
||||
librt = compiler.find_library('rt', required:false)
|
||||
|
||||
@@ -28,7 +28,13 @@ By default, kiwix-serve expects a list of ZIM files as command line arguments. P
|
||||
|
||||
.TP
|
||||
\fB-i ADDR, --address=ADDR\fR
|
||||
Listen only on this IP address. By default, the server listens on all available IP addresses.
|
||||
Listen only on this IP address. By default, the server listens on all available IP addresses. Alternatively, you can use special values to define which types of connections to accept:
|
||||
|
||||
all : Listen for connections on all IP addresses (IPv4 and IPv6).
|
||||
.br
|
||||
ipv4 : Listen for connections on all IPv4 addresses.
|
||||
.br
|
||||
ipv6 : Listen for connections on all IPv6 addresses.
|
||||
|
||||
.TP
|
||||
\fB-p PORT, --port=PORT\fR
|
||||
|
||||
@@ -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,158 +51,105 @@ 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 (delete|remove) 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];
|
||||
show(library, bookId);
|
||||
}
|
||||
} else {
|
||||
if (options.at("ZIMID").asStringList().empty()) {
|
||||
auto booksIds = library.getBooksIds();
|
||||
for(auto& bookId: booksIds) {
|
||||
show(library, bookId);
|
||||
}
|
||||
} else {
|
||||
auto bookIds = options.at("ZIMID").asStringList();
|
||||
for(auto& bookId: bookIds) {
|
||||
show(library, bookId);
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
auto zimPaths = options.at("ZIMPATH").asStringList();
|
||||
for (auto& zimPath: zimPaths) {
|
||||
if (options.at("--zimPathToSave").isString()) {
|
||||
zimPathToSave = options.at("--zimPathToSave").asString();
|
||||
} else {
|
||||
std::cerr << "Invalid zim file path" << std::endl;
|
||||
resultCode = 1;
|
||||
zimPathToSave = zimPath;
|
||||
}
|
||||
if (options.at("--url").isString()) {
|
||||
url = options.at("--url").asString();
|
||||
}
|
||||
|
||||
if (manager.addBookFromPathAndGetId(zimPath, zimPathToSave, url, false).empty()) {
|
||||
std::cerr << "Cannot add ZIM " << zimPath << " to the library." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return(resultCode);
|
||||
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 +161,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 +208,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,99 +17,97 @@
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <docopt/docopt.h>
|
||||
|
||||
#include <zim/search.h>
|
||||
#include <zim/suggestion.h>
|
||||
|
||||
#include <kiwix/spelling_correction.h>
|
||||
#include <xapian.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "../version.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
void usage()
|
||||
|
||||
// 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
|
||||
--spelling Suggest article titles based on the spelling corrected PATTERN instead of making a fulltext search.
|
||||
-v --verbose Give details about the search process
|
||||
-V --version Print software version
|
||||
-h --help Print this help
|
||||
)";
|
||||
|
||||
std::filesystem::path getKiwixCachedDataDirPath()
|
||||
{
|
||||
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);
|
||||
std::filesystem::path home(getenv("HOME"));
|
||||
std::filesystem::path cacheDirPath = home / ".cache" / "kiwix";
|
||||
std::filesystem::create_directories(cacheDirPath);
|
||||
return cacheDirPath;
|
||||
}
|
||||
|
||||
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 if (args.at("--spelling").asBool()) {
|
||||
kiwix::SpellingsDB spellingsDB(archive, getKiwixCachedDataDirPath());
|
||||
for (const auto& r:spellingsDB.getSpellingCorrections(pattern, 1)) {
|
||||
cout << r << 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;
|
||||
}
|
||||
@@ -117,6 +115,9 @@ int main(int argc, char** argv)
|
||||
} catch ( const std::runtime_error& err) {
|
||||
cerr << err.what() << endl;
|
||||
exit(1);
|
||||
} catch ( const Xapian::Error& err) {
|
||||
cerr << err.get_msg() << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
|
||||
@@ -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,50 @@
|
||||
#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 this ip address, all available ones otherwise" << 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]
|
||||
--catalogOnly Serve only the library catalog
|
||||
--contentServerURL=<url> Root URL of the server serving ZIM content for this library
|
||||
-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) ?
|
||||
@@ -104,20 +104,9 @@ std::string loadCustomTemplate (std::string customIndexPath) {
|
||||
return indexTemplateString;
|
||||
}
|
||||
|
||||
inline std::string normalizeRootUrl(std::string rootUrl)
|
||||
{
|
||||
while ( !rootUrl.empty() && rootUrl.back() == '/' )
|
||||
rootUrl.pop_back();
|
||||
|
||||
while ( !rootUrl.empty() && rootUrl.front() == '/' )
|
||||
rootUrl = rootUrl.substr(1);
|
||||
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 +136,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 +183,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 +221,78 @@ int main(int argc, char** argv)
|
||||
std::string customIndexPath="";
|
||||
std::string indexTemplateString="";
|
||||
int serverPort = 80;
|
||||
int daemonFlag [[gnu::unused]] = false;
|
||||
int libraryFlag = false;
|
||||
bool catalogOnlyFlag = false;
|
||||
std::string contentServerURL;
|
||||
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("--catalogOnly", catalogOnlyFlag)
|
||||
STRING("--contentServerURL", contentServerURL)
|
||||
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);
|
||||
@@ -367,6 +320,19 @@ int main(int argc, char** argv)
|
||||
auto libraryFileTimestamp = newestFileTimestamp(libraryPaths);
|
||||
auto curLibraryFileTimestamp = libraryFileTimestamp;
|
||||
|
||||
kiwix::IpMode ipMode = kiwix::IpMode::AUTO;
|
||||
|
||||
if (address == "all") {
|
||||
address.clear();
|
||||
ipMode = kiwix::IpMode::ALL;
|
||||
} else if (address == "ipv4") {
|
||||
address.clear();
|
||||
ipMode = kiwix::IpMode::IPV4;
|
||||
} else if (address == "ipv6") {
|
||||
address.clear();
|
||||
ipMode = kiwix::IpMode::IPV6;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Fork if necessary */
|
||||
if (daemonFlag) {
|
||||
@@ -408,14 +374,20 @@ int main(int argc, char** argv)
|
||||
server.setIndexTemplateString(indexTemplateString);
|
||||
server.setIpConnectionLimit(ipConnectionLimit);
|
||||
server.setMultiZimSearchLimit(searchLimit);
|
||||
server.setIpMode(ipMode);
|
||||
server.setCatalogOnlyMode(catalogOnlyFlag);
|
||||
while ( !contentServerURL.empty() && contentServerURL.back() == '/' )
|
||||
contentServerURL.pop_back();
|
||||
server.setContentServerUrl(contentServerURL);
|
||||
|
||||
if (! server.start()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
std::string url = "http://" + server.getAddress() + ":" + std::to_string(server.getPort()) + normalizeRootUrl(rootLocation);
|
||||
std::cout << "The Kiwix server is running and can be accessed in the local network at: "
|
||||
<< url << std::endl;
|
||||
|
||||
std::cout << "The Kiwix server is running and can be accessed in the local network at: " << std::endl;
|
||||
for (const auto& url : server.getServerAccessUrls()) {
|
||||
std::cout << " - " << url << std::endl;
|
||||
}
|
||||
|
||||
/* Run endless (until PPID dies) */
|
||||
waiting = true;
|
||||
@@ -451,7 +423,9 @@ int main(int argc, char** argv)
|
||||
|
||||
if ( monitorLibrary ) {
|
||||
curLibraryFileTimestamp = newestFileTimestamp(libraryPaths);
|
||||
libraryMustBeReloaded += curLibraryFileTimestamp > libraryFileTimestamp;
|
||||
if ( !libraryMustBeReloaded ) {
|
||||
libraryMustBeReloaded = curLibraryFileTimestamp > libraryFileTimestamp;
|
||||
}
|
||||
}
|
||||
|
||||
if ( libraryMustBeReloaded && !libraryPaths.empty() ) {
|
||||
|
||||
Reference in New Issue
Block a user