aboutsummaryrefslogtreecommitdiff
path: root/bpkg/help.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-11-25 12:51:49 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-11-25 12:51:49 +0200
commit9ecfa10db4a4856d445910d440aa38ee5b2b3d26 (patch)
tree2cbcfbc06adce166bf0ce2376d4c7300b9fdcb4e /bpkg/help.cxx
parent87e476cf192b70c133a1bf00efa8586348326092 (diff)
Implement help pager
Diffstat (limited to 'bpkg/help.cxx')
-rw-r--r--bpkg/help.cxx106
1 files changed, 90 insertions, 16 deletions
diff --git a/bpkg/help.cxx b/bpkg/help.cxx
index df4a286..31a7cdd 100644
--- a/bpkg/help.cxx
+++ b/bpkg/help.cxx
@@ -4,8 +4,19 @@
#include <bpkg/help>
+#ifndef _WIN32
+# include <unistd.h> // close()
+#else
+# include <io.h> // _close()
+#endif
+
+#include <chrono>
+#include <thread> // this_thread::sleep_for()
#include <iostream>
+#include <butl/process>
+#include <butl/fdstream>
+
#include <bpkg/types>
#include <bpkg/utility>
#include <bpkg/diagnostics>
@@ -13,32 +24,95 @@
#include <bpkg/bpkg-options>
using namespace std;
+using namespace butl;
namespace bpkg
{
- static void
- help ()
+ struct pager
{
- print_bpkg_usage (cout);
- }
+ pager (const string& name)
+ {
+ // First try less.
+ //
+ try
+ {
+ string prompt ("-Ps" + name + " (press q to quit, h for help)");
+
+ const char* args[] = {
+ "less",
+ "-R", // ANSI color
+ prompt.c_str (),
+ nullptr
+ };
+
+ p_ = process (args, -1); // Redirect child's stdin to a pipe.
+
+ // Wait a bit and see if the pager has exited before reading
+ // anything (e.g., because exec() couldn't find the program).
+ // If you know a cleaner way to handle this, let me know (no,
+ // a select()-based approach doesn't work; the pipe is buffered
+ // and therefore is always ready for writing).
+ //
+ this_thread::sleep_for (chrono::milliseconds (50));
+
+ bool r;
+ if (p_.try_wait (r))
+ {
+#ifndef _WIN32
+ ::close (p_.out_fd);
+#else
+ _close (p_.out_fd);
+#endif
+ }
+ else
+ os_.open (p_.out_fd);
+ }
+ catch (const process_error& e)
+ {
+ if (e.child ())
+ exit (1);
+ }
+ }
+
+ std::ostream&
+ stream ()
+ {
+ return os_.is_open () ? os_ : std::cout;
+ }
+
+ bool
+ wait ()
+ {
+ os_.close ();
+ return p_.wait ();
+ }
+
+ ~pager () {wait ();}
+
+ private:
+ process p_;
+ ofdstream os_;
+ };
int
help (const help_options&, const string& t, usage_function* usage)
{
- ostream& o (cout);
-
- if (usage != nullptr) // Command.
- usage (o, cli::usage_para::none);
- else if (t.empty ()) // General help.
- help ();
- else if (t == "common-options") // Help topics.
+ if (usage == nullptr) // Not a command.
{
- print_bpkg_common_options_long_usage (cout);
+ if (t.empty ()) // General help.
+ usage = &print_bpkg_usage;
+ else if (t == "common-options") // Help topics.
+ usage = &print_bpkg_common_options_long_usage;
+ else
+ fail << "unknown bpkg command/help topic '" << t << "'" <<
+ info << "run 'bpkg help' for more information";
}
- else
- fail << "unknown bpkg command/help topic '" << t << "'" <<
- info << "run 'bpkg help' for more information";
- return 0;
+ pager p ("bpkg " + (t.empty () ? "help" : t));
+ usage (p.stream (), cli::usage_para::none);
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
}
}