xref: /netbsd-src/external/bsd/atf/dist/atf-c++/detail/application.cpp (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
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 
30 #if defined(HAVE_CONFIG_H)
31 #include "bconfig.h"
32 #endif
33 
34 extern "C" {
35 #include <unistd.h>
36 }
37 
38 #include <cstdarg>
39 #include <cstdio>
40 #include <cstdlib>
41 #include <cstring>
42 #include <iostream>
43 
44 #include "application.hpp"
45 #include "sanity.hpp"
46 #include "ui.hpp"
47 
48 #if !defined(HAVE_VSNPRINTF_IN_STD)
49 namespace std {
50 using ::vsnprintf;
51 }
52 #endif // !defined(HAVE_VSNPRINTF_IN_STD)
53 
54 namespace impl = atf::application;
55 #define IMPL_NAME "atf::application"
56 
57 // ------------------------------------------------------------------------
58 // The "usage_error" class.
59 // ------------------------------------------------------------------------
60 
61 impl::usage_error::usage_error(const char *fmt, ...)
62     throw() :
63     std::runtime_error("usage_error; message unformatted")
64 {
65     va_list ap;
66 
67     va_start(ap, fmt);
68     std::vsnprintf(m_text, sizeof(m_text), fmt, ap);
69     va_end(ap);
70 }
71 
72 impl::usage_error::~usage_error(void)
73     throw()
74 {
75 }
76 
77 const char*
78 impl::usage_error::what(void)
79     const throw()
80 {
81     return m_text;
82 }
83 
84 // ------------------------------------------------------------------------
85 // The "application" class.
86 // ------------------------------------------------------------------------
87 
88 impl::option::option(char ch,
89                      const std::string& a,
90                      const std::string& desc) :
91     m_character(ch),
92     m_argument(a),
93     m_description(desc)
94 {
95 }
96 
97 bool
98 impl::option::operator<(const impl::option& o)
99     const
100 {
101     return m_character < o.m_character;
102 }
103 
104 impl::app::app(const std::string& description,
105                const std::string& manpage,
106                const std::string& global_manpage,
107                const bool use_ui) :
108     m_hflag(false),
109     m_argc(-1),
110     m_argv(NULL),
111     m_prog_name(NULL),
112     m_description(description),
113     m_manpage(manpage),
114     m_global_manpage(global_manpage),
115     m_use_ui(use_ui)
116 {
117 }
118 
119 impl::app::~app(void)
120 {
121 }
122 
123 bool
124 impl::app::inited(void)
125 {
126     return m_argc != -1;
127 }
128 
129 impl::app::options_set
130 impl::app::options(void)
131 {
132     options_set opts = specific_options();
133     if (m_use_ui) {
134         opts.insert(option('h', "", "Shows this help message"));
135     }
136     return opts;
137 }
138 
139 std::string
140 impl::app::specific_args(void)
141     const
142 {
143     return "";
144 }
145 
146 impl::app::options_set
147 impl::app::specific_options(void)
148     const
149 {
150     return options_set();
151 }
152 
153 void
154 impl::app::process_option(int ch, const char* arg)
155 {
156 }
157 
158 void
159 impl::app::process_options(void)
160 {
161     PRE(inited());
162 
163     std::string optstr;
164 #if defined(HAVE_GNU_GETOPT)
165     optstr += '+'; // Turn on POSIX behavior.
166 #endif
167     optstr += ':';
168     {
169         options_set opts = options();
170         for (options_set::const_iterator iter = opts.begin();
171              iter != opts.end(); iter++) {
172             const option& opt = (*iter);
173 
174             optstr += opt.m_character;
175             if (!opt.m_argument.empty())
176                 optstr += ':';
177         }
178     }
179 
180     int ch;
181     ::opterr = 0;
182     while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
183         switch (ch) {
184             case 'h':
185                 INV(m_use_ui);
186                 m_hflag = true;
187                 break;
188 
189             case ':':
190                 throw usage_error("Option -%c requires an argument.",
191                                   ::optopt);
192 
193             case '?':
194                 throw usage_error("Unknown option -%c.", ::optopt);
195 
196             default:
197                 process_option(ch, ::optarg);
198         }
199     }
200     m_argc -= ::optind;
201     m_argv += ::optind;
202 
203     // Clear getopt state just in case the test wants to use it.
204     optind = 1;
205 #if defined(HAVE_OPTRESET)
206     optreset = 1;
207 #endif
208 }
209 
210 void
211 impl::app::usage(std::ostream& os)
212 {
213     PRE(inited());
214 
215     std::string args = specific_args();
216     if (!args.empty())
217         args = " " + args;
218     os << ui::format_text_with_tag(std::string(m_prog_name) + " [options]" +
219                                    args, "Usage: ", false) << "\n\n"
220        << ui::format_text(m_description) << "\n\n";
221 
222     options_set opts = options();
223     INV(!opts.empty());
224     os << "Available options:\n";
225     size_t coldesc = 0;
226     for (options_set::const_iterator iter = opts.begin();
227          iter != opts.end(); iter++) {
228         const option& opt = (*iter);
229 
230         if (opt.m_argument.length() + 1 > coldesc)
231             coldesc = opt.m_argument.length() + 1;
232     }
233     for (options_set::const_iterator iter = opts.begin();
234          iter != opts.end(); iter++) {
235         const option& opt = (*iter);
236 
237         std::string tag = std::string("    -") + opt.m_character;
238         if (opt.m_argument.empty())
239             tag += "    ";
240         else
241             tag += " " + opt.m_argument + "    ";
242         os << ui::format_text_with_tag(opt.m_description, tag, false,
243                                        coldesc + 10) << "\n";
244     }
245     os << "\n";
246 
247     std::string gmp;
248     if (!m_global_manpage.empty())
249         gmp = " and " + m_global_manpage;
250     os << ui::format_text("For more details please see " + m_manpage +
251                           gmp + ".")
252        << "\n";
253 }
254 
255 int
256 impl::app::run(int argc, char* const* argv)
257 {
258     PRE(argc > 0);
259     PRE(argv != NULL);
260 
261     m_argc = argc;
262     m_argv = argv;
263 
264     m_argv0 = m_argv[0];
265 
266     m_prog_name = std::strrchr(m_argv[0], '/');
267     if (m_prog_name == NULL)
268         m_prog_name = m_argv[0];
269     else
270         m_prog_name++;
271 
272     // Libtool workaround: if running from within the source tree (binaries
273     // that are not installed yet), skip the "lt-" prefix added to files in
274     // the ".libs" directory to show the real (not temporary) name.
275     if (std::strncmp(m_prog_name, "lt-", 3) == 0)
276         m_prog_name += 3;
277 
278     const std::string bug =
279         std::string("This is probably a bug in ") + m_prog_name +
280         " or one of the libraries it uses.  Please report this problem to "
281         PACKAGE_BUGREPORT " and provide as many details as possible "
282         "describing how you got to this condition.";
283 
284     int errcode;
285     try {
286         int oldargc = m_argc;
287 
288         process_options();
289 
290         if (m_hflag) {
291             INV(m_use_ui);
292             if (oldargc != 2)
293                 throw usage_error("-h must be given alone.");
294 
295             usage(std::cout);
296             errcode = EXIT_SUCCESS;
297         } else
298             errcode = main();
299     } catch (const usage_error& e) {
300         if (m_use_ui) {
301             std::cerr << ui::format_error(m_prog_name, e.what()) << "\n"
302                       << ui::format_info(m_prog_name, std::string("Type `") +
303                                          m_prog_name + " -h' for more details.")
304                       << "\n";
305         } else {
306             std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
307             std::cerr << m_prog_name << ": See " << m_manpage << " for usage "
308                 "details.\n";
309         }
310         errcode = EXIT_FAILURE;
311     } catch (const std::runtime_error& e) {
312         if (m_use_ui) {
313             std::cerr << ui::format_error(m_prog_name, std::string(e.what()))
314                       << "\n";
315         } else {
316             std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
317         }
318         errcode = EXIT_FAILURE;
319     } catch (const std::exception& e) {
320         if (m_use_ui) {
321             std::cerr << ui::format_error(m_prog_name, std::string("Caught "
322                 "unexpected error: ") + e.what() + "\n" + bug) << "\n";
323         } else {
324             std::cerr << m_prog_name << ": ERROR: Caught unexpected error: "
325                       << e.what() << "\n";
326         }
327         errcode = EXIT_FAILURE;
328     } catch (...) {
329         if (m_use_ui) {
330             std::cerr << ui::format_error(m_prog_name, std::string("Caught "
331                 "unknown error\n") + bug) << "\n";
332         } else {
333             std::cerr << m_prog_name << ": ERROR: Caught unknown error\n";
334         }
335         errcode = EXIT_FAILURE;
336     }
337     return errcode;
338 }
339