1*0a6a1f1dSLionel Sambuc //
2*0a6a1f1dSLionel Sambuc // Automated Testing Framework (atf)
3*0a6a1f1dSLionel Sambuc //
4*0a6a1f1dSLionel Sambuc // Copyright (c) 2007 The NetBSD Foundation, Inc.
5*0a6a1f1dSLionel Sambuc // All rights reserved.
6*0a6a1f1dSLionel Sambuc //
7*0a6a1f1dSLionel Sambuc // Redistribution and use in source and binary forms, with or without
8*0a6a1f1dSLionel Sambuc // modification, are permitted provided that the following conditions
9*0a6a1f1dSLionel Sambuc // are met:
10*0a6a1f1dSLionel Sambuc // 1. Redistributions of source code must retain the above copyright
11*0a6a1f1dSLionel Sambuc // notice, this list of conditions and the following disclaimer.
12*0a6a1f1dSLionel Sambuc // 2. Redistributions in binary form must reproduce the above copyright
13*0a6a1f1dSLionel Sambuc // notice, this list of conditions and the following disclaimer in the
14*0a6a1f1dSLionel Sambuc // documentation and/or other materials provided with the distribution.
15*0a6a1f1dSLionel Sambuc //
16*0a6a1f1dSLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17*0a6a1f1dSLionel Sambuc // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18*0a6a1f1dSLionel Sambuc // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19*0a6a1f1dSLionel Sambuc // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*0a6a1f1dSLionel Sambuc // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21*0a6a1f1dSLionel Sambuc // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*0a6a1f1dSLionel Sambuc // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23*0a6a1f1dSLionel Sambuc // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*0a6a1f1dSLionel Sambuc // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25*0a6a1f1dSLionel Sambuc // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26*0a6a1f1dSLionel Sambuc // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27*0a6a1f1dSLionel Sambuc // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*0a6a1f1dSLionel Sambuc //
29*0a6a1f1dSLionel Sambuc
30*0a6a1f1dSLionel Sambuc extern "C" {
31*0a6a1f1dSLionel Sambuc #include <unistd.h>
32*0a6a1f1dSLionel Sambuc }
33*0a6a1f1dSLionel Sambuc
34*0a6a1f1dSLionel Sambuc #include <cassert>
35*0a6a1f1dSLionel Sambuc #include <cstdarg>
36*0a6a1f1dSLionel Sambuc #include <cstdio>
37*0a6a1f1dSLionel Sambuc #include <cstdlib>
38*0a6a1f1dSLionel Sambuc #include <cstring>
39*0a6a1f1dSLionel Sambuc #include <iostream>
40*0a6a1f1dSLionel Sambuc
41*0a6a1f1dSLionel Sambuc #include "application.hpp"
42*0a6a1f1dSLionel Sambuc #include "ui.hpp"
43*0a6a1f1dSLionel Sambuc
44*0a6a1f1dSLionel Sambuc namespace std {
45*0a6a1f1dSLionel Sambuc using ::vsnprintf;
46*0a6a1f1dSLionel Sambuc }
47*0a6a1f1dSLionel Sambuc
48*0a6a1f1dSLionel Sambuc namespace impl = tools::application;
49*0a6a1f1dSLionel Sambuc #define IMPL_NAME "tools::application"
50*0a6a1f1dSLionel Sambuc
51*0a6a1f1dSLionel Sambuc // ------------------------------------------------------------------------
52*0a6a1f1dSLionel Sambuc // The "usage_error" class.
53*0a6a1f1dSLionel Sambuc // ------------------------------------------------------------------------
54*0a6a1f1dSLionel Sambuc
usage_error(const char * fmt,...)55*0a6a1f1dSLionel Sambuc impl::usage_error::usage_error(const char *fmt, ...)
56*0a6a1f1dSLionel Sambuc throw() :
57*0a6a1f1dSLionel Sambuc std::runtime_error("usage_error; message unformatted")
58*0a6a1f1dSLionel Sambuc {
59*0a6a1f1dSLionel Sambuc va_list ap;
60*0a6a1f1dSLionel Sambuc
61*0a6a1f1dSLionel Sambuc va_start(ap, fmt);
62*0a6a1f1dSLionel Sambuc std::vsnprintf(m_text, sizeof(m_text), fmt, ap);
63*0a6a1f1dSLionel Sambuc va_end(ap);
64*0a6a1f1dSLionel Sambuc }
65*0a6a1f1dSLionel Sambuc
~usage_error(void)66*0a6a1f1dSLionel Sambuc impl::usage_error::~usage_error(void)
67*0a6a1f1dSLionel Sambuc throw()
68*0a6a1f1dSLionel Sambuc {
69*0a6a1f1dSLionel Sambuc }
70*0a6a1f1dSLionel Sambuc
71*0a6a1f1dSLionel Sambuc const char*
what(void) const72*0a6a1f1dSLionel Sambuc impl::usage_error::what(void)
73*0a6a1f1dSLionel Sambuc const throw()
74*0a6a1f1dSLionel Sambuc {
75*0a6a1f1dSLionel Sambuc return m_text;
76*0a6a1f1dSLionel Sambuc }
77*0a6a1f1dSLionel Sambuc
78*0a6a1f1dSLionel Sambuc // ------------------------------------------------------------------------
79*0a6a1f1dSLionel Sambuc // The "application" class.
80*0a6a1f1dSLionel Sambuc // ------------------------------------------------------------------------
81*0a6a1f1dSLionel Sambuc
option(char ch,const std::string & a,const std::string & desc)82*0a6a1f1dSLionel Sambuc impl::option::option(char ch,
83*0a6a1f1dSLionel Sambuc const std::string& a,
84*0a6a1f1dSLionel Sambuc const std::string& desc) :
85*0a6a1f1dSLionel Sambuc m_character(ch),
86*0a6a1f1dSLionel Sambuc m_argument(a),
87*0a6a1f1dSLionel Sambuc m_description(desc)
88*0a6a1f1dSLionel Sambuc {
89*0a6a1f1dSLionel Sambuc }
90*0a6a1f1dSLionel Sambuc
91*0a6a1f1dSLionel Sambuc bool
operator <(const impl::option & o) const92*0a6a1f1dSLionel Sambuc impl::option::operator<(const impl::option& o)
93*0a6a1f1dSLionel Sambuc const
94*0a6a1f1dSLionel Sambuc {
95*0a6a1f1dSLionel Sambuc return m_character < o.m_character;
96*0a6a1f1dSLionel Sambuc }
97*0a6a1f1dSLionel Sambuc
app(const std::string & description,const std::string & manpage,const std::string & global_manpage)98*0a6a1f1dSLionel Sambuc impl::app::app(const std::string& description,
99*0a6a1f1dSLionel Sambuc const std::string& manpage,
100*0a6a1f1dSLionel Sambuc const std::string& global_manpage) :
101*0a6a1f1dSLionel Sambuc m_hflag(false),
102*0a6a1f1dSLionel Sambuc m_argc(-1),
103*0a6a1f1dSLionel Sambuc m_argv(NULL),
104*0a6a1f1dSLionel Sambuc m_prog_name(NULL),
105*0a6a1f1dSLionel Sambuc m_description(description),
106*0a6a1f1dSLionel Sambuc m_manpage(manpage),
107*0a6a1f1dSLionel Sambuc m_global_manpage(global_manpage)
108*0a6a1f1dSLionel Sambuc {
109*0a6a1f1dSLionel Sambuc }
110*0a6a1f1dSLionel Sambuc
~app(void)111*0a6a1f1dSLionel Sambuc impl::app::~app(void)
112*0a6a1f1dSLionel Sambuc {
113*0a6a1f1dSLionel Sambuc }
114*0a6a1f1dSLionel Sambuc
115*0a6a1f1dSLionel Sambuc bool
inited(void)116*0a6a1f1dSLionel Sambuc impl::app::inited(void)
117*0a6a1f1dSLionel Sambuc {
118*0a6a1f1dSLionel Sambuc return m_argc != -1;
119*0a6a1f1dSLionel Sambuc }
120*0a6a1f1dSLionel Sambuc
121*0a6a1f1dSLionel Sambuc impl::app::options_set
options(void)122*0a6a1f1dSLionel Sambuc impl::app::options(void)
123*0a6a1f1dSLionel Sambuc {
124*0a6a1f1dSLionel Sambuc options_set opts = specific_options();
125*0a6a1f1dSLionel Sambuc opts.insert(option('h', "", "Shows this help message"));
126*0a6a1f1dSLionel Sambuc return opts;
127*0a6a1f1dSLionel Sambuc }
128*0a6a1f1dSLionel Sambuc
129*0a6a1f1dSLionel Sambuc std::string
specific_args(void) const130*0a6a1f1dSLionel Sambuc impl::app::specific_args(void)
131*0a6a1f1dSLionel Sambuc const
132*0a6a1f1dSLionel Sambuc {
133*0a6a1f1dSLionel Sambuc return "";
134*0a6a1f1dSLionel Sambuc }
135*0a6a1f1dSLionel Sambuc
136*0a6a1f1dSLionel Sambuc impl::app::options_set
specific_options(void) const137*0a6a1f1dSLionel Sambuc impl::app::specific_options(void)
138*0a6a1f1dSLionel Sambuc const
139*0a6a1f1dSLionel Sambuc {
140*0a6a1f1dSLionel Sambuc return options_set();
141*0a6a1f1dSLionel Sambuc }
142*0a6a1f1dSLionel Sambuc
143*0a6a1f1dSLionel Sambuc void
process_option(int ch,const char * arg)144*0a6a1f1dSLionel Sambuc impl::app::process_option(int ch __attribute__((__unused__)),
145*0a6a1f1dSLionel Sambuc const char* arg __attribute__((__unused__)))
146*0a6a1f1dSLionel Sambuc {
147*0a6a1f1dSLionel Sambuc }
148*0a6a1f1dSLionel Sambuc
149*0a6a1f1dSLionel Sambuc void
process_options(void)150*0a6a1f1dSLionel Sambuc impl::app::process_options(void)
151*0a6a1f1dSLionel Sambuc {
152*0a6a1f1dSLionel Sambuc assert(inited());
153*0a6a1f1dSLionel Sambuc
154*0a6a1f1dSLionel Sambuc std::string optstr = ":";
155*0a6a1f1dSLionel Sambuc {
156*0a6a1f1dSLionel Sambuc options_set opts = options();
157*0a6a1f1dSLionel Sambuc for (options_set::const_iterator iter = opts.begin();
158*0a6a1f1dSLionel Sambuc iter != opts.end(); iter++) {
159*0a6a1f1dSLionel Sambuc const option& opt = (*iter);
160*0a6a1f1dSLionel Sambuc
161*0a6a1f1dSLionel Sambuc optstr += opt.m_character;
162*0a6a1f1dSLionel Sambuc if (!opt.m_argument.empty())
163*0a6a1f1dSLionel Sambuc optstr += ':';
164*0a6a1f1dSLionel Sambuc }
165*0a6a1f1dSLionel Sambuc }
166*0a6a1f1dSLionel Sambuc
167*0a6a1f1dSLionel Sambuc int ch;
168*0a6a1f1dSLionel Sambuc const int old_opterr = ::opterr;
169*0a6a1f1dSLionel Sambuc ::opterr = 0;
170*0a6a1f1dSLionel Sambuc while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
171*0a6a1f1dSLionel Sambuc switch (ch) {
172*0a6a1f1dSLionel Sambuc case 'h':
173*0a6a1f1dSLionel Sambuc m_hflag = true;
174*0a6a1f1dSLionel Sambuc break;
175*0a6a1f1dSLionel Sambuc
176*0a6a1f1dSLionel Sambuc case ':':
177*0a6a1f1dSLionel Sambuc throw usage_error("Option -%c requires an argument.",
178*0a6a1f1dSLionel Sambuc ::optopt);
179*0a6a1f1dSLionel Sambuc
180*0a6a1f1dSLionel Sambuc case '?':
181*0a6a1f1dSLionel Sambuc throw usage_error("Unknown option -%c.", ::optopt);
182*0a6a1f1dSLionel Sambuc
183*0a6a1f1dSLionel Sambuc default:
184*0a6a1f1dSLionel Sambuc process_option(ch, ::optarg);
185*0a6a1f1dSLionel Sambuc }
186*0a6a1f1dSLionel Sambuc }
187*0a6a1f1dSLionel Sambuc m_argc -= ::optind;
188*0a6a1f1dSLionel Sambuc m_argv += ::optind;
189*0a6a1f1dSLionel Sambuc
190*0a6a1f1dSLionel Sambuc // Clear getopt state just in case the test wants to use it.
191*0a6a1f1dSLionel Sambuc opterr = old_opterr;
192*0a6a1f1dSLionel Sambuc optind = 1;
193*0a6a1f1dSLionel Sambuc optreset = 1;
194*0a6a1f1dSLionel Sambuc }
195*0a6a1f1dSLionel Sambuc
196*0a6a1f1dSLionel Sambuc void
usage(std::ostream & os)197*0a6a1f1dSLionel Sambuc impl::app::usage(std::ostream& os)
198*0a6a1f1dSLionel Sambuc {
199*0a6a1f1dSLionel Sambuc assert(inited());
200*0a6a1f1dSLionel Sambuc
201*0a6a1f1dSLionel Sambuc std::string args = specific_args();
202*0a6a1f1dSLionel Sambuc if (!args.empty())
203*0a6a1f1dSLionel Sambuc args = " " + args;
204*0a6a1f1dSLionel Sambuc os << ui::format_text_with_tag(std::string(m_prog_name) + " [options]" +
205*0a6a1f1dSLionel Sambuc args, "Usage: ", false) << "\n\n"
206*0a6a1f1dSLionel Sambuc << ui::format_text(m_description) << "\n\n";
207*0a6a1f1dSLionel Sambuc
208*0a6a1f1dSLionel Sambuc options_set opts = options();
209*0a6a1f1dSLionel Sambuc assert(!opts.empty());
210*0a6a1f1dSLionel Sambuc os << "Available options:\n";
211*0a6a1f1dSLionel Sambuc size_t coldesc = 0;
212*0a6a1f1dSLionel Sambuc for (options_set::const_iterator iter = opts.begin();
213*0a6a1f1dSLionel Sambuc iter != opts.end(); iter++) {
214*0a6a1f1dSLionel Sambuc const option& opt = (*iter);
215*0a6a1f1dSLionel Sambuc
216*0a6a1f1dSLionel Sambuc if (opt.m_argument.length() + 1 > coldesc)
217*0a6a1f1dSLionel Sambuc coldesc = opt.m_argument.length() + 1;
218*0a6a1f1dSLionel Sambuc }
219*0a6a1f1dSLionel Sambuc for (options_set::const_iterator iter = opts.begin();
220*0a6a1f1dSLionel Sambuc iter != opts.end(); iter++) {
221*0a6a1f1dSLionel Sambuc const option& opt = (*iter);
222*0a6a1f1dSLionel Sambuc
223*0a6a1f1dSLionel Sambuc std::string tag = std::string(" -") + opt.m_character;
224*0a6a1f1dSLionel Sambuc if (opt.m_argument.empty())
225*0a6a1f1dSLionel Sambuc tag += " ";
226*0a6a1f1dSLionel Sambuc else
227*0a6a1f1dSLionel Sambuc tag += " " + opt.m_argument + " ";
228*0a6a1f1dSLionel Sambuc os << ui::format_text_with_tag(opt.m_description, tag, false,
229*0a6a1f1dSLionel Sambuc coldesc + 10) << "\n";
230*0a6a1f1dSLionel Sambuc }
231*0a6a1f1dSLionel Sambuc os << "\n";
232*0a6a1f1dSLionel Sambuc
233*0a6a1f1dSLionel Sambuc std::string gmp;
234*0a6a1f1dSLionel Sambuc if (!m_global_manpage.empty())
235*0a6a1f1dSLionel Sambuc gmp = " and " + m_global_manpage;
236*0a6a1f1dSLionel Sambuc os << ui::format_text("For more details please see " + m_manpage +
237*0a6a1f1dSLionel Sambuc gmp + ".")
238*0a6a1f1dSLionel Sambuc << "\n";
239*0a6a1f1dSLionel Sambuc }
240*0a6a1f1dSLionel Sambuc
241*0a6a1f1dSLionel Sambuc int
run(int argc,char * const * argv)242*0a6a1f1dSLionel Sambuc impl::app::run(int argc, char* const* argv)
243*0a6a1f1dSLionel Sambuc {
244*0a6a1f1dSLionel Sambuc assert(argc > 0);
245*0a6a1f1dSLionel Sambuc assert(argv != NULL);
246*0a6a1f1dSLionel Sambuc
247*0a6a1f1dSLionel Sambuc m_argc = argc;
248*0a6a1f1dSLionel Sambuc m_argv = argv;
249*0a6a1f1dSLionel Sambuc
250*0a6a1f1dSLionel Sambuc m_argv0 = m_argv[0];
251*0a6a1f1dSLionel Sambuc
252*0a6a1f1dSLionel Sambuc m_prog_name = std::strrchr(m_argv[0], '/');
253*0a6a1f1dSLionel Sambuc if (m_prog_name == NULL)
254*0a6a1f1dSLionel Sambuc m_prog_name = m_argv[0];
255*0a6a1f1dSLionel Sambuc else
256*0a6a1f1dSLionel Sambuc m_prog_name++;
257*0a6a1f1dSLionel Sambuc
258*0a6a1f1dSLionel Sambuc // Libtool workaround: if running from within the source tree (binaries
259*0a6a1f1dSLionel Sambuc // that are not installed yet), skip the "lt-" prefix added to files in
260*0a6a1f1dSLionel Sambuc // the ".libs" directory to show the real (not temporary) name.
261*0a6a1f1dSLionel Sambuc if (std::strncmp(m_prog_name, "lt-", 3) == 0)
262*0a6a1f1dSLionel Sambuc m_prog_name += 3;
263*0a6a1f1dSLionel Sambuc
264*0a6a1f1dSLionel Sambuc const std::string bug =
265*0a6a1f1dSLionel Sambuc std::string("This is probably a bug in ") + m_prog_name +
266*0a6a1f1dSLionel Sambuc " Please use send-pr(1) to report this issue and provide as many"
267*0a6a1f1dSLionel Sambuc " details as possible describing how you got to this condition.";
268*0a6a1f1dSLionel Sambuc
269*0a6a1f1dSLionel Sambuc int errcode;
270*0a6a1f1dSLionel Sambuc try {
271*0a6a1f1dSLionel Sambuc int oldargc = m_argc;
272*0a6a1f1dSLionel Sambuc
273*0a6a1f1dSLionel Sambuc process_options();
274*0a6a1f1dSLionel Sambuc
275*0a6a1f1dSLionel Sambuc if (m_hflag) {
276*0a6a1f1dSLionel Sambuc if (oldargc != 2)
277*0a6a1f1dSLionel Sambuc throw usage_error("-h must be given alone.");
278*0a6a1f1dSLionel Sambuc
279*0a6a1f1dSLionel Sambuc usage(std::cout);
280*0a6a1f1dSLionel Sambuc errcode = EXIT_SUCCESS;
281*0a6a1f1dSLionel Sambuc } else
282*0a6a1f1dSLionel Sambuc errcode = main();
283*0a6a1f1dSLionel Sambuc } catch (const usage_error& e) {
284*0a6a1f1dSLionel Sambuc std::cerr << ui::format_error(m_prog_name, e.what()) << "\n"
285*0a6a1f1dSLionel Sambuc << ui::format_info(m_prog_name, std::string("Type `") +
286*0a6a1f1dSLionel Sambuc m_prog_name + " -h' for more details.")
287*0a6a1f1dSLionel Sambuc << "\n";
288*0a6a1f1dSLionel Sambuc errcode = EXIT_FAILURE;
289*0a6a1f1dSLionel Sambuc } catch (const std::runtime_error& e) {
290*0a6a1f1dSLionel Sambuc std::cerr << ui::format_error(m_prog_name, std::string(e.what()))
291*0a6a1f1dSLionel Sambuc << "\n";
292*0a6a1f1dSLionel Sambuc errcode = EXIT_FAILURE;
293*0a6a1f1dSLionel Sambuc } catch (const std::exception& e) {
294*0a6a1f1dSLionel Sambuc std::cerr << ui::format_error(m_prog_name, std::string("Caught "
295*0a6a1f1dSLionel Sambuc "unexpected error: ") + e.what() + "\n" + bug) << "\n";
296*0a6a1f1dSLionel Sambuc errcode = EXIT_FAILURE;
297*0a6a1f1dSLionel Sambuc } catch (...) {
298*0a6a1f1dSLionel Sambuc std::cerr << ui::format_error(m_prog_name, std::string("Caught "
299*0a6a1f1dSLionel Sambuc "unknown error\n") + bug) << "\n";
300*0a6a1f1dSLionel Sambuc errcode = EXIT_FAILURE;
301*0a6a1f1dSLionel Sambuc }
302*0a6a1f1dSLionel Sambuc return errcode;
303*0a6a1f1dSLionel Sambuc }
304