From b59f42d67fbd38c90523ddb023ff2606b9816b5a Mon Sep 17 00:00:00 2001 From: Nikhil Tanwar <2002nikhiltanwar@gmail.com> Date: Sat, 12 Sep 2026 02:38:09 +0530 Subject: [PATCH] Recursively scan directories for ZIM files in kiwix-serve - Support directory paths as positional PATH arguments in kiwix-serve - Recursively add all ZIM files found within specified directories - Unify USAGE, man page, and RST documentation to use PATH ... - Handle filesystem errors gracefully when directories are inaccessible Co-authored-by: Lakshya <162354839+lakshyajn@users.noreply.github.com> --- docs/kiwix-serve.rst | 7 ++++-- src/man/kiwix-serve.1 | 15 +++++++++--- src/server/kiwix-serve.cpp | 48 ++++++++++++++++++++++++++++---------- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/docs/kiwix-serve.rst b/docs/kiwix-serve.rst index 69b299a..d33449e 100644 --- a/docs/kiwix-serve.rst +++ b/docs/kiwix-serve.rst @@ -25,7 +25,8 @@ Usage .. code-block:: sh kiwix-serve --library [OPTIONS] LIBRARY_FILE_PATH - kiwix-serve [OPTIONS] ZIM_FILE_PATH ... + kiwix-serve [OPTIONS] PATH ... + Arguments @@ -37,7 +38,9 @@ Arguments to serve. To be used only with the :option:`--library` option. Multiple library files can be provided as a semicolon (``;``) separated list. -``ZIM_FILE_PATH``: ZIM file path (multiple arguments are allowed). +``PATH``: a ZIM file path or a directory path. When a directory is given, all +ZIM files under that directory are used. Multiple arguments are allowed. + Options ------- diff --git a/src/man/kiwix-serve.1 b/src/man/kiwix-serve.1 index 214b508..0b10b3e 100644 --- a/src/man/kiwix-serve.1 +++ b/src/man/kiwix-serve.1 @@ -7,7 +7,8 @@ kiwix-serve \- Kiwix HTTP Server .B kiwix-serve --library [OPTIONS] LIBRARY_FILE_PATH .br -.B kiwix-serve [OPTIONS] ZIM_FILE_PATH ... +.B kiwix-serve [OPTIONS] PATH ... + .SH DESCRIPTION The \fBkiwix-serve\fR command is used to run a stand-alone HTTP server for serving ZIM contents over the network. @@ -18,8 +19,10 @@ The \fBkiwix-serve\fR command is used to run a stand-alone HTTP server for servi Path of a library file (XML or OPDS) listing ZIM files to serve. To be used only with the --library option. Multiple library files can be provided as a semicolon (;) separated list. .TP -\fBZIM_FILE_PATH ...\fR -ZIM file path(s). Multiple arguments are allowed. +\fBPATH ...\fR +A ZIM file path or a directory path. When a directory path is provided, all ZIM +files under that directory are used. Multiple arguments are allowed. + .SH OPTIONS .TP @@ -123,6 +126,12 @@ Serve multiple ZIM files: .B kiwix-serve zim1.zim zim2.zim zim3.zim .fi +Serve ZIM files within a directory or directories: +.sp +.nf +.B kiwix-serve zimDir1 zimDir2 +.fi + Serve ZIM files from a library: .sp .nf diff --git a/src/server/kiwix-serve.cpp b/src/server/kiwix-serve.cpp index e1d441d..10b6d95 100644 --- a/src/server/kiwix-serve.cpp +++ b/src/server/kiwix-serve.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef _WIN32 # include @@ -44,19 +45,20 @@ #define LITERAL_AS_STR(A) #A #define AS_STR(A) LITERAL_AS_STR(A) +namespace fs = std::filesystem; static const char USAGE[] = R"(Deliver ZIM file(s) articles via HTTP Usage: - kiwix-serve [options] ZIMPATH ... + kiwix-serve [options] PATH ... kiwix-serve [options] (-l | --library) LIBRARYPATH kiwix-serve -h | --help kiwix-serve -V | --version Mandatory arguments: LIBRARYPATH Library file path (XML or OPDS) listing ZIM file to serve. To be used only with the --library argument." - ZIMPATH ZIM file path(s) + PATH A ZIM file path or a directory path (in which case all ZIM files under that directory are used). Options: -h --help Print this help @@ -184,16 +186,38 @@ bool reloadLibrary(kiwix::Manager& mgr, const std::vector& paths) } void addPathsInManager(kiwix::Manager& manager, const std::vector& paths, - bool skipInvalid) + bool skipInvalid, bool isVerboseFlag) { for (const auto& path : paths) { - if (!manager.addBookFromPath(path, path, "", false)) { - if (skipInvalid) { - std::cerr << "Skipping invalid '" << path << "' ...continuing" << std::endl; - } else { - std::cerr << "Unable to add the ZIM file '" << path - << "' to the internal library." << std::endl; - exit(1); + std::error_code ec; + const bool isDir = fs::is_directory(path, ec); + + if (isDir) { + // It's a directory - try to add all ZIM files inside it + try { + manager.addBooksFromDirectory(path, isVerboseFlag); + } catch (const fs::filesystem_error& e) { + if (skipInvalid) { + std::cerr << "Skipping directory '" << path << "': " + << e.what() << "." << std::endl; + } else { + std::cerr << "Unable to scan directory '" << path << "': " + << e.what() << "." << std::endl; + exit(1); + } + } + } else { + // Not a directory: either a regular ZIM file, a nonexistent path, or + // a path that could not be stat'd (e.g. permission denied on parent dir). + // Delegate to addBookFromPath which will report a suitable error itself. + if (!manager.addBookFromPath(path, path, "", false)) { + if (skipInvalid) { + std::cerr << "Skipping invalid path '" << path << "'." << std::endl; + } else { + std::cerr << "Unable to add '" << path + << "' to the internal library." << std::endl; + exit(1); + } } } } @@ -286,7 +310,7 @@ int main(int argc, char** argv) 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", paths, "ZIMPATH must be a string list") + STRING_LIST("PATH", paths, "PATH must be a string list") } if (!errorString.empty()) { @@ -320,7 +344,7 @@ int main(int argc, char** argv) << "' is empty (or has only remote books)." << std::endl; } } else { - addPathsInManager(manager, paths, skipInvalid); + addPathsInManager(manager, paths, skipInvalid, isVerboseFlag); } auto libraryFileTimestamp = newestFileTimestamp(libraryPaths); auto curLibraryFileTimestamp = libraryFileTimestamp;