From 961deff41dad248b3234724267d85a31e19dd058 Mon Sep 17 00:00:00 2001 From: tastytea Date: Thu, 24 Jun 2021 18:06:11 +0200 Subject: [PATCH] Add --status and --status-interval. --status prints a status message to stderr at regular intervals. --status-interval sets the interval for status messages. Closes: https://schlomp.space/tastytea/epubgrep/issues/10 --- man/epubgrep.1.adoc | 12 ++++++++++++ src/main.cpp | 27 ++++++++++++++++++++++++++- src/options.cpp | 19 ++++++++++++++----- src/options.hpp | 2 ++ 4 files changed, 54 insertions(+), 6 deletions(-) diff --git a/man/epubgrep.1.adoc b/man/epubgrep.1.adoc index 6857bf0..0f6327e 100644 --- a/man/epubgrep.1.adoc +++ b/man/epubgrep.1.adoc @@ -38,6 +38,12 @@ epubgrep -PiC2 '(Apple|Orange)s?' file.epub epubgrep -PC0 --raw --no-filename=all '"http[^"]+"' file.epub | tr -d '"' -------------------------------------------------------------------------------- +.Save the search results to an HTML file and output a status message every 20 seconds +[source,shell] +-------------------------------------------------------------------------------- +epubgrep -C2 --status --status-interval=20 --html 'Apples' file.epub > result.html +-------------------------------------------------------------------------------- + == OPTIONS *-h*, *--help*:: @@ -109,6 +115,12 @@ in an array named `matches`. I will try not to break the API. 😊 Output HTML instead of plain text. HTML will only be output at the end of the program. +*--status*:: +Output status message every *--status-interval* seconds to standard error. + +*--status-interval* _NUMBER_:: +Set status message interval to _NUMBER_ seconds. + == USAGE [source,shellsession] diff --git a/src/main.cpp b/src/main.cpp index 3f806d8..e292229 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ #include #include // For compatibility with fmt 4. +#include #include #include #include @@ -141,6 +142,7 @@ int main(int argc, char *argv[]) vector> matches_all; std::mutex mutex_matches_all; vector> futurepool; + std::atomic books_searched{0}; auto search_file{ [&opts, &matches_all, &mutex_matches_all, @@ -188,7 +190,7 @@ int main(int argc, char *argv[]) }}; auto futures_cleanup{ - [&futurepool, &return_code](const bool wait = false) + [&futurepool, &return_code, &books_searched](const bool wait = false) { using namespace std::chrono_literals; @@ -208,6 +210,7 @@ int main(int argc, char *argv[]) } } futurepool.erase(it); + ++books_searched; } }}; @@ -219,6 +222,26 @@ int main(int argc, char *argv[]) }()}; DEBUGLOG << "max_threads = " << max_threads; + const auto print_status{ + [&opts, &books_searched, &input_files](std::future cancel) + { + if (!opts.status) + { + return; + } + while (cancel.wait_for(std::chrono::seconds(opts.status_interval)) + != std::future_status::ready) + { + std::cerr + << format(translate("{0:d} of {1:d} books searched.").str(), + books_searched, input_files.size()) + << '\n'; + } + std::cerr << translate("All books searched.") << '\n'; + }}; + std::promise promise_status; + std::thread thread_status{print_status, promise_status.get_future()}; + for (const auto &filepath : input_files) { while (futurepool.size() >= max_threads) @@ -244,6 +267,8 @@ int main(int argc, char *argv[]) } DEBUGLOG << "Waiting for remaining threads to finish"; futures_cleanup(true); + promise_status.set_value(true); + thread_status.join(); if (return_code == EXIT_FATAL) { return EXIT_FATAL; diff --git a/src/options.cpp b/src/options.cpp index f15e551..829262d 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -88,15 +88,22 @@ options parse_options(int argc, char *argv[]) .str().data()) ("dereference-recursive,R", translate("Read all files under each directory, recursively, " - "following symlinks.") .str().data()) + "following symlinks.").str().data()) ("ignore-archive-errors", - translate("Ignore errors about wrong file formats.") .str().data()) + translate("Ignore errors about wrong file formats.").str().data()) ("debug", - translate("Enable debug output.") .str().data()) + translate("Enable debug output.").str().data()) ("json", - translate("Output JSON instead of plain text.") .str().data()) + translate("Output JSON instead of plain text.").str().data()) ("html", - translate("Output HTML instead of plain text.") .str().data()) + translate("Output HTML instead of plain text.").str().data()) + ("status", + translate("Output status message every STATUS-INTERVAL seconds") + .str().data()) + ("status-interval", po::value() + ->value_name(translate("NUMBER"))->default_value(30), + translate("Set status message interval to NUMBER seconds.") + .str().data()) ; po::options_description options_hidden("Hidden options"); @@ -238,6 +245,8 @@ options parse_again(const po::variables_map &vm) opts.debug = vm.count("debug") > 0; opts.json = vm.count("json") > 0; opts.html = vm.count("html") > 0; + opts.status = vm.count("status") > 0; + opts.status_interval = vm["status-interval"].as(); if (vm.count("regexp") > 0) { diff --git a/src/options.hpp b/src/options.hpp index 8929cbf..c83a5ae 100644 --- a/src/options.hpp +++ b/src/options.hpp @@ -59,6 +59,8 @@ struct options bool debug{false}; bool json{false}; bool html{false}; + bool status{false}; + uint64_t status_interval{0}; //! For the debug output. friend std::ostream &operator<<(std::ostream &out, const options &opts);