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