From 61377c582e0f2675baa5f5e6e30a35d1a4164b33 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 1 May 2017 16:08:43 +0300 Subject: Add hxx extension for headers and lib prefix for library dir --- libbutl/pager.cxx | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 libbutl/pager.cxx (limited to 'libbutl/pager.cxx') diff --git a/libbutl/pager.cxx b/libbutl/pager.cxx new file mode 100644 index 0000000..3910443 --- /dev/null +++ b/libbutl/pager.cxx @@ -0,0 +1,207 @@ +// file : libbutl/pager.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#ifndef _WIN32 +# include // STDOUT_FILENO +# include // ioctl() + +# include +# include // this_thread::sleep_for() +#else +# include +#endif + +#include // strchr() +#include // move() + +#include // operator<<(ostream, exception), + // throw_generic_error() +#include // fdclose() + +using namespace std; + +namespace butl +{ + pager:: + pager (const string& name, + bool verbose, + const string* pager, + const vector* pager_options) + // Create successfully exited process. Will "wait" for it if fallback to + // non-interactive execution path. + // + : p_ (process_exit (0)) + { + // If we are using the default pager, try to get the terminal width + // so that we can center the output. + // + if (pager == nullptr) + { + 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 + // This works properly on PowerShell, while for the regular console + // (cmd.exe) it always returns 80 columns. + // + CONSOLE_SCREEN_BUFFER_INFO w; + if (GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &w)) + col = static_cast (w.srWindow.Right - w.srWindow.Left + 1); +#endif + if (col > 80) + indent_.assign ((col - 80) / 2, ' '); + } + + vector args; + string prompt; + + if (pager != nullptr) + { + if (pager->empty ()) + return; // No pager should be used. + + args.push_back (pager->c_str ()); + } + else + { + // By default try less (again, no pun intended). + // + 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. + // + if (pager_options != nullptr) + for (const string& o: *pager_options) + args.push_back (o.c_str ()); + + args.push_back (nullptr); + + if (verbose) + { + for (const char* const* p (args.data ()); *p != nullptr; ++p) + { + if (p != args.data ()) + cerr << ' '; + + // Quote if empty or contains spaces. + // + bool q (**p == '\0' || strchr (*p, ' ') != nullptr); + + if (q) + cerr << '"'; + + cerr << *p; + + if (q) + cerr << '"'; + } + cerr << endl; + } + + // 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 (and no, a select()-based + // approach doesn't work; the pipe is buffered and therefore is always + // ready for writing). + // + // MINGW GCC 4.9 doesn't implement this_thread so use Win32 Sleep(). + // +#ifndef _WIN32 + this_thread::sleep_for (chrono::milliseconds (50)); +#else + Sleep (50); +#endif + + bool r; + if (p_.try_wait (r)) + { + p_.out_fd.reset (); + + if (pager != nullptr) + throw_generic_error (ECHILD); + } + else + os_.open (move (p_.out_fd)); + } + catch (const process_error& e) + { + if (e.child) + { + cerr << args[0] << ": unable to execute: " << e << endl; + exit (1); + } + + // Ignore unless it was a user-specified pager. + // + if (pager != nullptr) + throw; // Re-throw as system_error. + } + + // Setup the indentation machinery. + // + if (!indent_.empty ()) + buf_ = stream ().rdbuf (this); + } + + bool pager:: + wait (bool ie) + { + // Teardown the indentation machinery. + // + if (buf_ != nullptr) + { + stream ().rdbuf (buf_); + buf_ = nullptr; + } + + // Prevent ofdstream::close() from throwing in the ignore errors mode. + // + if (ie) + os_.exceptions (ofdstream::goodbit); + + os_.close (); + return p_.wait (ie); + } + + pager::int_type pager:: + 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); + } + + int pager:: + sync () + { + return buf_->pubsync (); + } +} -- cgit v1.1