aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-01-30 15:43:38 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-01-30 15:43:38 +0200
commitd26d886aabc1d5b5aa0aa47a4eaef0c02e5a0031 (patch)
treed5f0ea8759064adb2d490c1579c95a9268df206f
parentb06a77e13ed963d8cbe87828ae5890a41fc47c68 (diff)
Move pager to libbutl
-rw-r--r--bpkg/help.cxx208
1 files changed, 19 insertions, 189 deletions
diff --git a/bpkg/help.cxx b/bpkg/help.cxx
index 9b0fcea..587a7c7 100644
--- a/bpkg/help.cxx
+++ b/bpkg/help.cxx
@@ -4,23 +4,9 @@
#include <bpkg/help>
-#ifndef _WIN32
-# include <unistd.h> // close(), STDOUT_FILENO
-# include <sys/ioctl.h> // ioctl()
-#else
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# include <windows.h> // GetConsoleScreenBufferInfo(), GetStdHandle()
-# include <io.h> // _close()
-#endif
+#include <system_error>
-#include <chrono>
-#include <thread> // this_thread::sleep_for()
-#include <iostream>
-
-#include <butl/process>
-#include <butl/fdstream>
+#include <butl/pager>
#include <bpkg/types>
#include <bpkg/utility>
@@ -33,174 +19,6 @@ using namespace butl;
namespace bpkg
{
- struct pager: protected streambuf
- {
- pager (const common_options& co, const string& name)
- {
- bool up (co.pager_specified ()); // User's pager.
-
- // If we are using the default pager, try to get the terminal width
- // so that we can center the output.
- //
- if (!up)
- {
- size_t col (0);
-
-#ifndef _WIN32
-# ifdef TIOCGWINSZ
- struct winsize w;
- if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &w) == 0)
- col = static_cast<size_t> (w.ws_col);
-# endif
-#else
-#error TODO: needs testing
- CONSOLE_SCREEN_BUFFER_INFO w;
- if (GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &w))
- col = static_cast<size_t> (w.srWindow.Right - w.srWindow.Left + 1);
-#endif
- if (col > 80)
- indent_.assign ((col - 80) / 2, ' ');
- }
-
- cstrings args;
- string prompt;
-
- if (up)
- {
- if (co.pager ().empty ())
- return; // No pager should be used.
-
- args.push_back (co.pager ().c_str ());
- }
- else
- {
- // By default try less.
- //
- prompt = "-Ps" + name + " (press q to quit, h for help)";
-
- args.push_back ("less");
- args.push_back ("-R"); // Handle ANSI color.
- args.push_back (prompt.c_str ());
- }
-
- // Add extra pager options.
- //
- for (const string& o: co.pager_option ())
- args.push_back (o.c_str ());
-
- args.push_back (nullptr);
-
- if (verb >= 2)
- print_process (args);
-
- // Ignore errors and go without a pager unless the pager was specified
- // by the user.
- //
- try
- {
- p_ = process (args.data (), -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
- if (up)
- fail << "pager " << args[0] << " exited unexpectedly";
- }
- else
- os_.open (p_.out_fd);
- }
- catch (const process_error& e)
- {
- // Ignore unless it was a user-specified pager.
- //
- if (up)
- error << "unable to execute " << args[0] << ": " << e.what ();
-
- if (e.child ())
- exit (1);
-
- if (up)
- throw failed ();
- }
-
- // Setup the indentation machinery.
- //
- if (!indent_.empty ())
- buf_ = stream ().rdbuf (this);
- }
-
- ostream&
- stream ()
- {
- return os_.is_open () ? os_ : cout;
- }
-
- bool
- wait ()
- {
- // Teardown the indentation machinery.
- //
- if (buf_ != nullptr)
- {
- stream ().rdbuf (buf_);
- buf_ = nullptr;
- }
-
- os_.close ();
- return p_.wait ();
- }
-
- ~pager () {wait ();}
-
- // streambuf output interface.
- //
- protected:
- using int_type = streambuf::int_type;
- using traits_type = streambuf::traits_type;
-
- virtual int_type
- overflow (int_type c)
- {
- if (prev_ == '\n' && c != '\n') // Don't indent blanks.
- {
- auto n (static_cast<streamsize> (indent_.size ()));
-
- if (buf_->sputn (indent_.c_str (), n) != n)
- return traits_type::eof ();
- }
-
- prev_ = c;
- return buf_->sputc (c);
- }
-
- virtual int
- sync ()
- {
- return buf_->pubsync ();
- }
-
- private:
- process p_;
- ofdstream os_;
-
- string indent_;
- int_type prev_ = '\n'; // Previous character.
- streambuf* buf_ = nullptr;
- };
-
int
help (const help_options& o, const string& t, usage_function* usage)
{
@@ -215,11 +33,23 @@ namespace bpkg
info << "run 'bpkg help' for more information";
}
- pager p (o, "bpkg " + (t.empty () ? "help" : t));
- usage (p.stream (), cli::usage_para::none);
+ try
+ {
+ pager p ("bpkg " + (t.empty () ? "help" : t),
+ verb >= 2,
+ o.pager_specified () ? &o.pager () : nullptr,
+ &o.pager_option ());
+
+ usage (p.stream (), cli::usage_para::none);
- // If the pager failed, assume it has issued some diagnostics.
- //
- return p.wait () ? 0 : 1;
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+ catch (const system_error& e)
+ {
+ error << "pager failed: " << e.what ();
+ throw failed ();
+ }
}
}