1240116Smarcel// Copyright (c) 2007 The NetBSD Foundation, Inc.
2240116Smarcel// All rights reserved.
3240116Smarcel//
4240116Smarcel// Redistribution and use in source and binary forms, with or without
5240116Smarcel// modification, are permitted provided that the following conditions
6240116Smarcel// are met:
7240116Smarcel// 1. Redistributions of source code must retain the above copyright
8240116Smarcel//    notice, this list of conditions and the following disclaimer.
9240116Smarcel// 2. Redistributions in binary form must reproduce the above copyright
10240116Smarcel//    notice, this list of conditions and the following disclaimer in the
11240116Smarcel//    documentation and/or other materials provided with the distribution.
12240116Smarcel//
13240116Smarcel// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14240116Smarcel// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15240116Smarcel// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16240116Smarcel// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17240116Smarcel// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18240116Smarcel// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19240116Smarcel// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20240116Smarcel// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21240116Smarcel// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22240116Smarcel// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23240116Smarcel// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24240116Smarcel// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25240116Smarcel
26273929Sjmmv#include "atf-c++/detail/application.hpp"
27273929Sjmmv
28240116Smarcel#if defined(HAVE_CONFIG_H)
29273929Sjmmv#include "config.h"
30240116Smarcel#endif
31240116Smarcel
32240116Smarcelextern "C" {
33240116Smarcel#include <unistd.h>
34240116Smarcel}
35240116Smarcel
36240116Smarcel#include <cstdarg>
37240116Smarcel#include <cstdio>
38240116Smarcel#include <cstdlib>
39240116Smarcel#include <cstring>
40240116Smarcel#include <iostream>
41240116Smarcel
42240116Smarcelextern "C" {
43240116Smarcel#include "atf-c/defs.h"
44240116Smarcel}
45240116Smarcel
46273929Sjmmv#include "atf-c++/detail/sanity.hpp"
47240116Smarcel
48240116Smarcel#if !defined(HAVE_VSNPRINTF_IN_STD)
49240116Smarcelnamespace std {
50240116Smarcelusing ::vsnprintf;
51240116Smarcel}
52240116Smarcel#endif // !defined(HAVE_VSNPRINTF_IN_STD)
53240116Smarcel
54240116Smarcelnamespace impl = atf::application;
55240116Smarcel#define IMPL_NAME "atf::application"
56240116Smarcel
57240116Smarcel// ------------------------------------------------------------------------
58240116Smarcel// The "usage_error" class.
59240116Smarcel// ------------------------------------------------------------------------
60240116Smarcel
61240116Smarcelimpl::usage_error::usage_error(const char *fmt, ...)
62240116Smarcel    throw() :
63240116Smarcel    std::runtime_error("usage_error; message unformatted")
64240116Smarcel{
65240116Smarcel    va_list ap;
66240116Smarcel
67240116Smarcel    va_start(ap, fmt);
68240116Smarcel    std::vsnprintf(m_text, sizeof(m_text), fmt, ap);
69240116Smarcel    va_end(ap);
70240116Smarcel}
71240116Smarcel
72240116Smarcelimpl::usage_error::~usage_error(void)
73240116Smarcel    throw()
74240116Smarcel{
75240116Smarcel}
76240116Smarcel
77240116Smarcelconst char*
78240116Smarcelimpl::usage_error::what(void)
79240116Smarcel    const throw()
80240116Smarcel{
81240116Smarcel    return m_text;
82240116Smarcel}
83240116Smarcel
84240116Smarcel// ------------------------------------------------------------------------
85240116Smarcel// The "application" class.
86240116Smarcel// ------------------------------------------------------------------------
87240116Smarcel
88240116Smarcelimpl::option::option(char ch,
89240116Smarcel                     const std::string& a,
90240116Smarcel                     const std::string& desc) :
91240116Smarcel    m_character(ch),
92240116Smarcel    m_argument(a),
93240116Smarcel    m_description(desc)
94240116Smarcel{
95240116Smarcel}
96240116Smarcel
97240116Smarcelbool
98240116Smarcelimpl::option::operator<(const impl::option& o)
99240116Smarcel    const
100240116Smarcel{
101240116Smarcel    return m_character < o.m_character;
102240116Smarcel}
103240116Smarcel
104240116Smarcelimpl::app::app(const std::string& description,
105261897Sjmmv               const std::string& manpage) :
106240116Smarcel    m_argc(-1),
107240116Smarcel    m_argv(NULL),
108240116Smarcel    m_prog_name(NULL),
109240116Smarcel    m_description(description),
110261897Sjmmv    m_manpage(manpage)
111240116Smarcel{
112240116Smarcel}
113240116Smarcel
114240116Smarcelimpl::app::~app(void)
115240116Smarcel{
116240116Smarcel}
117240116Smarcel
118240116Smarcelbool
119240116Smarcelimpl::app::inited(void)
120240116Smarcel{
121240116Smarcel    return m_argc != -1;
122240116Smarcel}
123240116Smarcel
124240116Smarcelimpl::app::options_set
125240116Smarcelimpl::app::options(void)
126240116Smarcel{
127261897Sjmmv    return specific_options();
128240116Smarcel}
129240116Smarcel
130240116Smarcelstd::string
131240116Smarcelimpl::app::specific_args(void)
132240116Smarcel    const
133240116Smarcel{
134240116Smarcel    return "";
135240116Smarcel}
136240116Smarcel
137240116Smarcelimpl::app::options_set
138240116Smarcelimpl::app::specific_options(void)
139240116Smarcel    const
140240116Smarcel{
141240116Smarcel    return options_set();
142240116Smarcel}
143240116Smarcel
144240116Smarcelvoid
145240116Smarcelimpl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED,
146240116Smarcel                          const char* arg ATF_DEFS_ATTRIBUTE_UNUSED)
147240116Smarcel{
148240116Smarcel}
149240116Smarcel
150240116Smarcelvoid
151240116Smarcelimpl::app::process_options(void)
152240116Smarcel{
153240116Smarcel    PRE(inited());
154240116Smarcel
155240116Smarcel    std::string optstr;
156240116Smarcel#if defined(HAVE_GNU_GETOPT)
157240116Smarcel    optstr += '+'; // Turn on POSIX behavior.
158240116Smarcel#endif
159240116Smarcel    optstr += ':';
160240116Smarcel    {
161240116Smarcel        options_set opts = options();
162240116Smarcel        for (options_set::const_iterator iter = opts.begin();
163240116Smarcel             iter != opts.end(); iter++) {
164240116Smarcel            const option& opt = (*iter);
165240116Smarcel
166240116Smarcel            optstr += opt.m_character;
167240116Smarcel            if (!opt.m_argument.empty())
168240116Smarcel                optstr += ':';
169240116Smarcel        }
170240116Smarcel    }
171240116Smarcel
172240116Smarcel    int ch;
173240116Smarcel    const int old_opterr = ::opterr;
174240116Smarcel    ::opterr = 0;
175240116Smarcel    while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
176240116Smarcel        switch (ch) {
177240116Smarcel            case ':':
178240116Smarcel                throw usage_error("Option -%c requires an argument.",
179240116Smarcel                                  ::optopt);
180240116Smarcel
181240116Smarcel            case '?':
182240116Smarcel                throw usage_error("Unknown option -%c.", ::optopt);
183240116Smarcel
184240116Smarcel            default:
185240116Smarcel                process_option(ch, ::optarg);
186240116Smarcel        }
187240116Smarcel    }
188240116Smarcel    m_argc -= ::optind;
189240116Smarcel    m_argv += ::optind;
190240116Smarcel
191240116Smarcel    // Clear getopt state just in case the test wants to use it.
192240116Smarcel    opterr = old_opterr;
193240116Smarcel    optind = 1;
194240116Smarcel#if defined(HAVE_OPTRESET)
195240116Smarcel    optreset = 1;
196240116Smarcel#endif
197240116Smarcel}
198240116Smarcel
199240116Smarcelint
200240116Smarcelimpl::app::run(int argc, char* const* argv)
201240116Smarcel{
202240116Smarcel    PRE(argc > 0);
203240116Smarcel    PRE(argv != NULL);
204240116Smarcel
205240116Smarcel    m_argc = argc;
206240116Smarcel    m_argv = argv;
207240116Smarcel
208240116Smarcel    m_argv0 = m_argv[0];
209240116Smarcel
210240116Smarcel    m_prog_name = std::strrchr(m_argv[0], '/');
211240116Smarcel    if (m_prog_name == NULL)
212240116Smarcel        m_prog_name = m_argv[0];
213240116Smarcel    else
214240116Smarcel        m_prog_name++;
215240116Smarcel
216240116Smarcel    // Libtool workaround: if running from within the source tree (binaries
217240116Smarcel    // that are not installed yet), skip the "lt-" prefix added to files in
218240116Smarcel    // the ".libs" directory to show the real (not temporary) name.
219240116Smarcel    if (std::strncmp(m_prog_name, "lt-", 3) == 0)
220240116Smarcel        m_prog_name += 3;
221240116Smarcel
222240116Smarcel    const std::string bug =
223240116Smarcel        std::string("This is probably a bug in ") + m_prog_name +
224240116Smarcel        " or one of the libraries it uses.  Please report this problem to "
225240116Smarcel        PACKAGE_BUGREPORT " and provide as many details as possible "
226240116Smarcel        "describing how you got to this condition.";
227240116Smarcel
228240116Smarcel    int errcode;
229240116Smarcel    try {
230240116Smarcel        process_options();
231261897Sjmmv        errcode = main();
232240116Smarcel    } catch (const usage_error& e) {
233261897Sjmmv        std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
234261897Sjmmv        std::cerr << m_prog_name << ": See " << m_manpage << " for usage "
235261897Sjmmv            "details.\n";
236240116Smarcel        errcode = EXIT_FAILURE;
237240116Smarcel    } catch (const std::runtime_error& e) {
238261897Sjmmv        std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
239240116Smarcel        errcode = EXIT_FAILURE;
240240116Smarcel    } catch (const std::exception& e) {
241261897Sjmmv        std::cerr << m_prog_name << ": ERROR: Caught unexpected error: "
242261897Sjmmv                  << e.what() << "\n";
243240116Smarcel        errcode = EXIT_FAILURE;
244240116Smarcel    } catch (...) {
245261897Sjmmv        std::cerr << m_prog_name << ": ERROR: Caught unknown error\n";
246240116Smarcel        errcode = EXIT_FAILURE;
247240116Smarcel    }
248240116Smarcel    return errcode;
249240116Smarcel}
250