From 3069cc89aa4f9b88ab4ef8b99a84fcf8f4b6e911 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 26 Nov 2015 12:48:39 +0200 Subject: Teach pager to center the output on wide terminals --- bpkg/help.cxx | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 5 deletions(-) (limited to 'bpkg/help.cxx') diff --git a/bpkg/help.cxx b/bpkg/help.cxx index 6c16088..fd496c6 100644 --- a/bpkg/help.cxx +++ b/bpkg/help.cxx @@ -5,7 +5,8 @@ #include #ifndef _WIN32 -# include // close() +# include // close(), STDOUT_FILENO +# include // ioctl() #else # include // _close() #endif @@ -28,15 +29,34 @@ using namespace butl; namespace bpkg { - struct pager + struct pager: protected std::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 (w.ws_col); +# endif +#else +#endif + if (col > 80) + indent_.assign ((col - 80) / 2, ' '); + } + cstrings args; string prompt; - bool up (co.pager_specified ()); // User's pager. - if (up) { if (co.pager ().empty ()) @@ -48,7 +68,7 @@ namespace bpkg { // By default try less. // - prompt = "-Ps" + name + " (press q to quit, h for help)"; + prompt = "-Ps" + indent_ + name + " (press q to quit, h for help)"; args.push_back ("less"); args.push_back ("-R"); // Handle ANSI color. @@ -107,6 +127,11 @@ namespace bpkg if (up) throw failed (); } + + // Setup the indentation machinery. + // + if (!indent_.empty ()) + buf_ = stream ().rdbuf (this); } std::ostream& @@ -118,15 +143,54 @@ namespace bpkg 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 = std::streambuf::int_type; + using traits_type = std::streambuf::traits_type; + + virtual int_type + overflow (int_type c) + { + if (prev_ == '\n' && c != '\n') // Don't indent blanks. + { + auto n (static_cast (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. + std::streambuf* buf_ = nullptr; }; int -- cgit v1.1