diff options
-rw-r--r-- | bpkg/help.cxx | 208 |
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 (); + } } } |