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