1*6b3a42afSjmmv // Copyright 2011 Google Inc.
2*6b3a42afSjmmv // All rights reserved.
3*6b3a42afSjmmv //
4*6b3a42afSjmmv // Redistribution and use in source and binary forms, with or without
5*6b3a42afSjmmv // modification, are permitted provided that the following conditions are
6*6b3a42afSjmmv // met:
7*6b3a42afSjmmv //
8*6b3a42afSjmmv // * Redistributions of source code must retain the above copyright
9*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer.
10*6b3a42afSjmmv // * Redistributions in binary form must reproduce the above copyright
11*6b3a42afSjmmv // notice, this list of conditions and the following disclaimer in the
12*6b3a42afSjmmv // documentation and/or other materials provided with the distribution.
13*6b3a42afSjmmv // * Neither the name of Google Inc. nor the names of its contributors
14*6b3a42afSjmmv // may be used to endorse or promote products derived from this software
15*6b3a42afSjmmv // without specific prior written permission.
16*6b3a42afSjmmv //
17*6b3a42afSjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6b3a42afSjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6b3a42afSjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6b3a42afSjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6b3a42afSjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6b3a42afSjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6b3a42afSjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6b3a42afSjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6b3a42afSjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6b3a42afSjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6b3a42afSjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6b3a42afSjmmv
29*6b3a42afSjmmv #include "utils/cmdline/ui.hpp"
30*6b3a42afSjmmv
31*6b3a42afSjmmv extern "C" {
32*6b3a42afSjmmv #include <sys/ioctl.h>
33*6b3a42afSjmmv
34*6b3a42afSjmmv #include <unistd.h>
35*6b3a42afSjmmv }
36*6b3a42afSjmmv
37*6b3a42afSjmmv #include <iostream>
38*6b3a42afSjmmv
39*6b3a42afSjmmv #include "utils/cmdline/globals.hpp"
40*6b3a42afSjmmv #include "utils/env.hpp"
41*6b3a42afSjmmv #include "utils/format/macros.hpp"
42*6b3a42afSjmmv #include "utils/fs/path.hpp"
43*6b3a42afSjmmv #include "utils/logging/macros.hpp"
44*6b3a42afSjmmv #include "utils/optional.ipp"
45*6b3a42afSjmmv #include "utils/text/operations.ipp"
46*6b3a42afSjmmv #include "utils/sanity.hpp"
47*6b3a42afSjmmv #include "utils/text/table.hpp"
48*6b3a42afSjmmv
49*6b3a42afSjmmv namespace cmdline = utils::cmdline;
50*6b3a42afSjmmv namespace text = utils::text;
51*6b3a42afSjmmv
52*6b3a42afSjmmv using utils::none;
53*6b3a42afSjmmv using utils::optional;
54*6b3a42afSjmmv
55*6b3a42afSjmmv
56*6b3a42afSjmmv /// Destructor for the class.
~ui(void)57*6b3a42afSjmmv cmdline::ui::~ui(void)
58*6b3a42afSjmmv {
59*6b3a42afSjmmv }
60*6b3a42afSjmmv
61*6b3a42afSjmmv
62*6b3a42afSjmmv /// Writes a line to stderr.
63*6b3a42afSjmmv ///
64*6b3a42afSjmmv /// The written line is printed as is, without being wrapped to fit within the
65*6b3a42afSjmmv /// screen width.
66*6b3a42afSjmmv ///
67*6b3a42afSjmmv /// \param message The line to print, without the trailing newline character.
68*6b3a42afSjmmv /// \param newline Whether to append a newline to the message or not.
69*6b3a42afSjmmv void
err(const std::string & message,const bool newline)70*6b3a42afSjmmv cmdline::ui::err(const std::string& message, const bool newline)
71*6b3a42afSjmmv {
72*6b3a42afSjmmv PRE(message.empty() || message[message.length() - 1] != '\n');
73*6b3a42afSjmmv LI(F("stderr: %s") % message);
74*6b3a42afSjmmv if (newline)
75*6b3a42afSjmmv std::cerr << message << "\n";
76*6b3a42afSjmmv else {
77*6b3a42afSjmmv std::cerr << message;
78*6b3a42afSjmmv std::cerr.flush();
79*6b3a42afSjmmv }
80*6b3a42afSjmmv }
81*6b3a42afSjmmv
82*6b3a42afSjmmv
83*6b3a42afSjmmv /// Writes a line to stdout.
84*6b3a42afSjmmv ///
85*6b3a42afSjmmv /// The written line is printed as is, without being wrapped to fit within the
86*6b3a42afSjmmv /// screen width.
87*6b3a42afSjmmv ///
88*6b3a42afSjmmv /// \param message The line to print, without the trailing newline character.
89*6b3a42afSjmmv /// \param newline Whether to append a newline to the message or not.
90*6b3a42afSjmmv void
out(const std::string & message,const bool newline)91*6b3a42afSjmmv cmdline::ui::out(const std::string& message, const bool newline)
92*6b3a42afSjmmv {
93*6b3a42afSjmmv PRE(message.empty() || message[message.length() - 1] != '\n');
94*6b3a42afSjmmv LI(F("stdout: %s") % message);
95*6b3a42afSjmmv if (newline)
96*6b3a42afSjmmv std::cout << message << "\n";
97*6b3a42afSjmmv else {
98*6b3a42afSjmmv std::cout << message;
99*6b3a42afSjmmv std::cout.flush();
100*6b3a42afSjmmv }
101*6b3a42afSjmmv }
102*6b3a42afSjmmv
103*6b3a42afSjmmv
104*6b3a42afSjmmv /// Queries the width of the screen.
105*6b3a42afSjmmv ///
106*6b3a42afSjmmv /// This information comes first from the COLUMNS environment variable. If not
107*6b3a42afSjmmv /// present or invalid, and if the stdout of the current process is connected to
108*6b3a42afSjmmv /// a terminal the width is deduced from the terminal itself. Ultimately, if
109*6b3a42afSjmmv /// all fails, none is returned. This function shall not raise any errors.
110*6b3a42afSjmmv ///
111*6b3a42afSjmmv /// Be aware that the results of this query are cached during execution.
112*6b3a42afSjmmv /// Subsequent calls to this function will always return the same value even if
113*6b3a42afSjmmv /// the terminal size has actually changed.
114*6b3a42afSjmmv ///
115*6b3a42afSjmmv /// \todo Install a signal handler for SIGWINCH so that we can readjust our
116*6b3a42afSjmmv /// knowledge of the terminal width when the user resizes the window.
117*6b3a42afSjmmv ///
118*6b3a42afSjmmv /// \return The width of the screen if it was possible to determine it, or none
119*6b3a42afSjmmv /// otherwise.
120*6b3a42afSjmmv optional< std::size_t >
screen_width(void) const121*6b3a42afSjmmv cmdline::ui::screen_width(void) const
122*6b3a42afSjmmv {
123*6b3a42afSjmmv static bool done = false;
124*6b3a42afSjmmv static optional< std::size_t > width = none;
125*6b3a42afSjmmv
126*6b3a42afSjmmv if (!done) {
127*6b3a42afSjmmv const optional< std::string > columns = utils::getenv("COLUMNS");
128*6b3a42afSjmmv if (columns) {
129*6b3a42afSjmmv if (columns.get().length() > 0) {
130*6b3a42afSjmmv try {
131*6b3a42afSjmmv width = utils::make_optional(
132*6b3a42afSjmmv utils::text::to_type< std::size_t >(columns.get()));
133*6b3a42afSjmmv } catch (const utils::text::value_error& e) {
134*6b3a42afSjmmv LD(F("Ignoring invalid value in COLUMNS variable: %s") %
135*6b3a42afSjmmv e.what());
136*6b3a42afSjmmv }
137*6b3a42afSjmmv }
138*6b3a42afSjmmv }
139*6b3a42afSjmmv if (!width) {
140*6b3a42afSjmmv struct ::winsize ws;
141*6b3a42afSjmmv if (::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
142*6b3a42afSjmmv width = optional< std::size_t >(ws.ws_col);
143*6b3a42afSjmmv }
144*6b3a42afSjmmv
145*6b3a42afSjmmv if (width && width.get() >= 80)
146*6b3a42afSjmmv width.get() -= 5;
147*6b3a42afSjmmv
148*6b3a42afSjmmv done = true;
149*6b3a42afSjmmv }
150*6b3a42afSjmmv
151*6b3a42afSjmmv return width;
152*6b3a42afSjmmv }
153*6b3a42afSjmmv
154*6b3a42afSjmmv
155*6b3a42afSjmmv /// Writes a line to stdout.
156*6b3a42afSjmmv ///
157*6b3a42afSjmmv /// The line is wrapped to fit on screen.
158*6b3a42afSjmmv ///
159*6b3a42afSjmmv /// \param message The line to print, without the trailing newline character.
160*6b3a42afSjmmv void
out_wrap(const std::string & message)161*6b3a42afSjmmv cmdline::ui::out_wrap(const std::string& message)
162*6b3a42afSjmmv {
163*6b3a42afSjmmv const optional< std::size_t > max_width = screen_width();
164*6b3a42afSjmmv if (max_width) {
165*6b3a42afSjmmv const std::vector< std::string > lines = text::refill(
166*6b3a42afSjmmv message, max_width.get());
167*6b3a42afSjmmv for (std::vector< std::string >::const_iterator iter = lines.begin();
168*6b3a42afSjmmv iter != lines.end(); iter++)
169*6b3a42afSjmmv out(*iter);
170*6b3a42afSjmmv } else
171*6b3a42afSjmmv out(message);
172*6b3a42afSjmmv }
173*6b3a42afSjmmv
174*6b3a42afSjmmv
175*6b3a42afSjmmv /// Writes a line to stdout with a leading tag.
176*6b3a42afSjmmv ///
177*6b3a42afSjmmv /// If the line does not fit on the current screen width, the line is broken
178*6b3a42afSjmmv /// into pieces and the tag is repeated on every line.
179*6b3a42afSjmmv ///
180*6b3a42afSjmmv /// \param tag The leading line tag.
181*6b3a42afSjmmv /// \param message The message to be printed, without the trailing newline
182*6b3a42afSjmmv /// character.
183*6b3a42afSjmmv /// \param repeat If true, print the tag on every line; otherwise, indent the
184*6b3a42afSjmmv /// text of all lines to match the width of the tag on the first line.
185*6b3a42afSjmmv void
out_tag_wrap(const std::string & tag,const std::string & message,const bool repeat)186*6b3a42afSjmmv cmdline::ui::out_tag_wrap(const std::string& tag, const std::string& message,
187*6b3a42afSjmmv const bool repeat)
188*6b3a42afSjmmv {
189*6b3a42afSjmmv const optional< std::size_t > max_width = screen_width();
190*6b3a42afSjmmv if (max_width && max_width.get() > tag.length()) {
191*6b3a42afSjmmv const std::vector< std::string > lines = text::refill(
192*6b3a42afSjmmv message, max_width.get() - tag.length());
193*6b3a42afSjmmv for (std::vector< std::string >::const_iterator iter = lines.begin();
194*6b3a42afSjmmv iter != lines.end(); iter++) {
195*6b3a42afSjmmv if (repeat || iter == lines.begin())
196*6b3a42afSjmmv out(F("%s%s") % tag % *iter);
197*6b3a42afSjmmv else
198*6b3a42afSjmmv out(F("%s%s") % std::string(tag.length(), ' ') % *iter);
199*6b3a42afSjmmv }
200*6b3a42afSjmmv } else {
201*6b3a42afSjmmv out(F("%s%s") % tag % message);
202*6b3a42afSjmmv }
203*6b3a42afSjmmv }
204*6b3a42afSjmmv
205*6b3a42afSjmmv
206*6b3a42afSjmmv /// Writes a table to stdout.
207*6b3a42afSjmmv ///
208*6b3a42afSjmmv /// \param table The table to write.
209*6b3a42afSjmmv /// \param formatter The table formatter to use to convert the table to a
210*6b3a42afSjmmv /// console representation.
211*6b3a42afSjmmv /// \param prefix Text to prepend to all the lines of the output table.
212*6b3a42afSjmmv void
out_table(const text::table & table,text::table_formatter formatter,const std::string & prefix)213*6b3a42afSjmmv cmdline::ui::out_table(const text::table& table,
214*6b3a42afSjmmv text::table_formatter formatter,
215*6b3a42afSjmmv const std::string& prefix)
216*6b3a42afSjmmv {
217*6b3a42afSjmmv if (table.empty())
218*6b3a42afSjmmv return;
219*6b3a42afSjmmv
220*6b3a42afSjmmv const optional< std::size_t > max_width = screen_width();
221*6b3a42afSjmmv if (max_width)
222*6b3a42afSjmmv formatter.set_table_width(max_width.get() - prefix.length());
223*6b3a42afSjmmv
224*6b3a42afSjmmv const std::vector< std::string > lines = formatter.format(table);
225*6b3a42afSjmmv for (std::vector< std::string >::const_iterator iter = lines.begin();
226*6b3a42afSjmmv iter != lines.end(); ++iter)
227*6b3a42afSjmmv out(prefix + *iter);
228*6b3a42afSjmmv }
229*6b3a42afSjmmv
230*6b3a42afSjmmv
231*6b3a42afSjmmv /// Formats and prints an error message.
232*6b3a42afSjmmv ///
233*6b3a42afSjmmv /// \param ui_ The user interface object used to print the message.
234*6b3a42afSjmmv /// \param message The message to print. Must not end with a dot nor with a
235*6b3a42afSjmmv /// newline character.
236*6b3a42afSjmmv void
print_error(ui * ui_,const std::string & message)237*6b3a42afSjmmv cmdline::print_error(ui* ui_, const std::string& message)
238*6b3a42afSjmmv {
239*6b3a42afSjmmv PRE(!message.empty() && message[message.length() - 1] != '.');
240*6b3a42afSjmmv LE(message);
241*6b3a42afSjmmv ui_->err(F("%s: E: %s.") % cmdline::progname() % message);
242*6b3a42afSjmmv }
243*6b3a42afSjmmv
244*6b3a42afSjmmv
245*6b3a42afSjmmv /// Formats and prints an informational message.
246*6b3a42afSjmmv ///
247*6b3a42afSjmmv /// \param ui_ The user interface object used to print the message.
248*6b3a42afSjmmv /// \param message The message to print. Must not end with a dot nor with a
249*6b3a42afSjmmv /// newline character.
250*6b3a42afSjmmv void
print_info(ui * ui_,const std::string & message)251*6b3a42afSjmmv cmdline::print_info(ui* ui_, const std::string& message)
252*6b3a42afSjmmv {
253*6b3a42afSjmmv PRE(!message.empty() && message[message.length() - 1] != '.');
254*6b3a42afSjmmv LI(message);
255*6b3a42afSjmmv ui_->err(F("%s: I: %s.") % cmdline::progname() % message);
256*6b3a42afSjmmv }
257*6b3a42afSjmmv
258*6b3a42afSjmmv
259*6b3a42afSjmmv /// Formats and prints a warning message.
260*6b3a42afSjmmv ///
261*6b3a42afSjmmv /// \param ui_ The user interface object used to print the message.
262*6b3a42afSjmmv /// \param message The message to print. Must not end with a dot nor with a
263*6b3a42afSjmmv /// newline character.
264*6b3a42afSjmmv void
print_warning(ui * ui_,const std::string & message)265*6b3a42afSjmmv cmdline::print_warning(ui* ui_, const std::string& message)
266*6b3a42afSjmmv {
267*6b3a42afSjmmv PRE(!message.empty() && message[message.length() - 1] != '.');
268*6b3a42afSjmmv LW(message);
269*6b3a42afSjmmv ui_->err(F("%s: W: %s.") % cmdline::progname() % message);
270*6b3a42afSjmmv }
271