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
34extern "C" {
35#include <unistd.h>
36}
37
38#include <cstdarg>
39#include <cstdio>
40#include <cstdlib>
41#include <cstring>
42#include <iostream>
43
44extern "C" {
45#include "atf-c/defs.h"
46}
47
48#include "application.hpp"
49#include "sanity.hpp"
50
51#if !defined(HAVE_VSNPRINTF_IN_STD)
52namespace std {
53using ::vsnprintf;
54}
55#endif // !defined(HAVE_VSNPRINTF_IN_STD)
56
57namespace impl = atf::application;
58#define IMPL_NAME "atf::application"
59
60// ------------------------------------------------------------------------
61// The "usage_error" class.
62// ------------------------------------------------------------------------
63
64impl::usage_error::usage_error(const char *fmt, ...)
65    throw() :
66    std::runtime_error("usage_error; message unformatted")
67{
68    va_list ap;
69
70    va_start(ap, fmt);
71    std::vsnprintf(m_text, sizeof(m_text), fmt, ap);
72    va_end(ap);
73}
74
75impl::usage_error::~usage_error(void)
76    throw()
77{
78}
79
80const char*
81impl::usage_error::what(void)
82    const throw()
83{
84    return m_text;
85}
86
87// ------------------------------------------------------------------------
88// The "application" class.
89// ------------------------------------------------------------------------
90
91impl::option::option(char ch,
92                     const std::string& a,
93                     const std::string& desc) :
94    m_character(ch),
95    m_argument(a),
96    m_description(desc)
97{
98}
99
100bool
101impl::option::operator<(const impl::option& o)
102    const
103{
104    return m_character < o.m_character;
105}
106
107impl::app::app(const std::string& description,
108               const std::string& manpage) :
109    m_argc(-1),
110    m_argv(NULL),
111    m_prog_name(NULL),
112    m_description(description),
113    m_manpage(manpage)
114{
115}
116
117impl::app::~app(void)
118{
119}
120
121bool
122impl::app::inited(void)
123{
124    return m_argc != -1;
125}
126
127impl::app::options_set
128impl::app::options(void)
129{
130    return specific_options();
131}
132
133std::string
134impl::app::specific_args(void)
135    const
136{
137    return "";
138}
139
140impl::app::options_set
141impl::app::specific_options(void)
142    const
143{
144    return options_set();
145}
146
147void
148impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED,
149                          const char* arg ATF_DEFS_ATTRIBUTE_UNUSED)
150{
151}
152
153void
154impl::app::process_options(void)
155{
156    PRE(inited());
157
158    std::string optstr;
159#if defined(HAVE_GNU_GETOPT)
160    optstr += '+'; // Turn on POSIX behavior.
161#endif
162    optstr += ':';
163    {
164        options_set opts = options();
165        for (options_set::const_iterator iter = opts.begin();
166             iter != opts.end(); iter++) {
167            const option& opt = (*iter);
168
169            optstr += opt.m_character;
170            if (!opt.m_argument.empty())
171                optstr += ':';
172        }
173    }
174
175    int ch;
176    const int old_opterr = ::opterr;
177    ::opterr = 0;
178    while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
179        switch (ch) {
180            case ':':
181                throw usage_error("Option -%c requires an argument.",
182                                  ::optopt);
183
184            case '?':
185                throw usage_error("Unknown option -%c.", ::optopt);
186
187            default:
188                process_option(ch, ::optarg);
189        }
190    }
191    m_argc -= ::optind;
192    m_argv += ::optind;
193
194    // Clear getopt state just in case the test wants to use it.
195    opterr = old_opterr;
196    optind = 1;
197#if defined(HAVE_OPTRESET)
198    optreset = 1;
199#endif
200}
201
202int
203impl::app::run(int argc, char* const* argv)
204{
205    PRE(argc > 0);
206    PRE(argv != NULL);
207
208    m_argc = argc;
209    m_argv = argv;
210
211    m_argv0 = m_argv[0];
212
213    m_prog_name = std::strrchr(m_argv[0], '/');
214    if (m_prog_name == NULL)
215        m_prog_name = m_argv[0];
216    else
217        m_prog_name++;
218
219    // Libtool workaround: if running from within the source tree (binaries
220    // that are not installed yet), skip the "lt-" prefix added to files in
221    // the ".libs" directory to show the real (not temporary) name.
222    if (std::strncmp(m_prog_name, "lt-", 3) == 0)
223        m_prog_name += 3;
224
225    const std::string bug =
226        std::string("This is probably a bug in ") + m_prog_name +
227        " or one of the libraries it uses.  Please report this problem to "
228        PACKAGE_BUGREPORT " and provide as many details as possible "
229        "describing how you got to this condition.";
230
231    int errcode;
232    try {
233        process_options();
234        errcode = main();
235    } catch (const usage_error& e) {
236        std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
237        std::cerr << m_prog_name << ": See " << m_manpage << " for usage "
238            "details.\n";
239        errcode = EXIT_FAILURE;
240    } catch (const std::runtime_error& e) {
241        std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
242        errcode = EXIT_FAILURE;
243    } catch (const std::exception& e) {
244        std::cerr << m_prog_name << ": ERROR: Caught unexpected error: "
245                  << e.what() << "\n";
246        errcode = EXIT_FAILURE;
247    } catch (...) {
248        std::cerr << m_prog_name << ": ERROR: Caught unknown error\n";
249        errcode = EXIT_FAILURE;
250    }
251    return errcode;
252}
253