xref: /minix3/external/bsd/atf/dist/tools/application.cpp (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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