1//
2// Automated Testing Framework (atf)
3//
4// Copyright (c) 2007, 2008, 2010 The NetBSD Foundation, Inc.
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions
9// are met:
10// 1. Redistributions of source code must retain the above copyright
11//    notice, this list of conditions and the following disclaimer.
12// 2. Redistributions in binary form must reproduce the above copyright
13//    notice, this list of conditions and the following disclaimer in the
14//    documentation and/or other materials provided with the distribution.
15//
16// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29
30extern "C" {
31#include <sys/ioctl.h>
32
33#include <termios.h>
34}
35
36#include <sstream>
37
38#include "env.hpp"
39#include "text.hpp"
40#include "sanity.hpp"
41#include "text.hpp"
42#include "ui.hpp"
43
44namespace impl = atf::ui;
45#define IMPL_NAME "atf::ui"
46
47static
48size_t
49terminal_width(void)
50{
51    static bool done = false;
52    static size_t width = 0;
53
54    if (!done) {
55        if (atf::env::has("COLUMNS")) {
56            const std::string cols = atf::env::get("COLUMNS");
57            if (cols.length() > 0) {
58                width = atf::text::to_type< size_t >(cols);
59            }
60        } else {
61            struct winsize ws;
62            if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1)
63                width = ws.ws_col;
64        }
65
66        if (width >= 80)
67            width -= 5;
68
69        done = true;
70    }
71
72    return width;
73}
74
75static
76std::string
77format_paragraph(const std::string& text,
78                 const std::string& tag,
79                 const bool first,
80                 const bool repeat,
81                 const size_t col)
82{
83    PRE(text.find('\n') == std::string::npos);
84
85    const std::string pad(col - tag.length(), ' ');
86    const std::string fullpad(col, ' ');
87
88    std::string formatted;
89    if (first || repeat)
90        formatted = tag + pad;
91    else
92        formatted = fullpad;
93    INV(formatted.length() == col);
94    size_t curcol = col;
95
96    const size_t maxcol = terminal_width();
97
98    std::vector< std::string > words = atf::text::split(text, " ");
99    for (std::vector< std::string >::const_iterator iter = words.begin();
100         iter != words.end(); iter++) {
101        const std::string& word = *iter;
102
103        if (iter != words.begin() && maxcol > 0 &&
104            curcol + word.length() + 1 > maxcol) {
105            if (repeat)
106                formatted += '\n' + tag + pad;
107            else
108                formatted += '\n' + fullpad;
109            curcol = col;
110        } else if (iter != words.begin()) {
111            formatted += ' ';
112            curcol++;
113        }
114
115        formatted += word;
116        curcol += word.length();
117    }
118
119    return formatted;
120}
121
122std::string
123impl::format_error(const std::string& prog_name, const std::string& error)
124{
125    return format_text_with_tag("ERROR: " + error, prog_name + ": ", true);
126}
127
128std::string
129impl::format_info(const std::string& prog_name, const std::string& msg)
130{
131    return format_text_with_tag(msg, prog_name + ": ", true);
132}
133
134std::string
135impl::format_text(const std::string& text)
136{
137    return format_text_with_tag(text, "", false, 0);
138}
139
140std::string
141impl::format_text_with_tag(const std::string& text, const std::string& tag,
142                           bool repeat, size_t col)
143{
144    PRE(col == 0 || col >= tag.length());
145    if (col == 0)
146        col = tag.length();
147
148    std::string formatted;
149
150    std::vector< std::string > lines = atf::text::split(text, "\n");
151    for (std::vector< std::string >::const_iterator iter = lines.begin();
152         iter != lines.end(); iter++) {
153        const std::string& line = *iter;
154
155        formatted += format_paragraph(line, tag, iter == lines.begin(),
156                                      repeat, col);
157        if (iter + 1 != lines.end()) {
158            if (repeat)
159                formatted += "\n" + tag + "\n";
160            else
161                formatted += "\n\n";
162        }
163    }
164
165    return formatted;
166}
167
168std::string
169impl::format_warning(const std::string& prog_name, const std::string& error)
170{
171    return format_text_with_tag("WARNING: " + error, prog_name + ": ", true);
172}
173