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