Home | History | Annotate | Line # | Download | only in detail
application.cpp revision 1.1.1.2
      1 //
      2 // Automated Testing Framework (atf)
      3 //
      4 // Copyright (c) 2007, 2008, 2010, 2011 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     const int old_opterr = ::opterr;
    182     ::opterr = 0;
    183     while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
    184         switch (ch) {
    185             case 'h':
    186                 INV(m_use_ui);
    187                 m_hflag = true;
    188                 break;
    189 
    190             case ':':
    191                 throw usage_error("Option -%c requires an argument.",
    192                                   ::optopt);
    193 
    194             case '?':
    195                 throw usage_error("Unknown option -%c.", ::optopt);
    196 
    197             default:
    198                 process_option(ch, ::optarg);
    199         }
    200     }
    201     m_argc -= ::optind;
    202     m_argv += ::optind;
    203 
    204     // Clear getopt state just in case the test wants to use it.
    205     opterr = old_opterr;
    206     optind = 1;
    207 #if defined(HAVE_OPTRESET)
    208     optreset = 1;
    209 #endif
    210 }
    211 
    212 void
    213 impl::app::usage(std::ostream& os)
    214 {
    215     PRE(inited());
    216 
    217     std::string args = specific_args();
    218     if (!args.empty())
    219         args = " " + args;
    220     os << ui::format_text_with_tag(std::string(m_prog_name) + " [options]" +
    221                                    args, "Usage: ", false) << "\n\n"
    222        << ui::format_text(m_description) << "\n\n";
    223 
    224     options_set opts = options();
    225     INV(!opts.empty());
    226     os << "Available options:\n";
    227     size_t coldesc = 0;
    228     for (options_set::const_iterator iter = opts.begin();
    229          iter != opts.end(); iter++) {
    230         const option& opt = (*iter);
    231 
    232         if (opt.m_argument.length() + 1 > coldesc)
    233             coldesc = opt.m_argument.length() + 1;
    234     }
    235     for (options_set::const_iterator iter = opts.begin();
    236          iter != opts.end(); iter++) {
    237         const option& opt = (*iter);
    238 
    239         std::string tag = std::string("    -") + opt.m_character;
    240         if (opt.m_argument.empty())
    241             tag += "    ";
    242         else
    243             tag += " " + opt.m_argument + "    ";
    244         os << ui::format_text_with_tag(opt.m_description, tag, false,
    245                                        coldesc + 10) << "\n";
    246     }
    247     os << "\n";
    248 
    249     std::string gmp;
    250     if (!m_global_manpage.empty())
    251         gmp = " and " + m_global_manpage;
    252     os << ui::format_text("For more details please see " + m_manpage +
    253                           gmp + ".")
    254        << "\n";
    255 }
    256 
    257 int
    258 impl::app::run(int argc, char* const* argv)
    259 {
    260     PRE(argc > 0);
    261     PRE(argv != NULL);
    262 
    263     m_argc = argc;
    264     m_argv = argv;
    265 
    266     m_argv0 = m_argv[0];
    267 
    268     m_prog_name = std::strrchr(m_argv[0], '/');
    269     if (m_prog_name == NULL)
    270         m_prog_name = m_argv[0];
    271     else
    272         m_prog_name++;
    273 
    274     // Libtool workaround: if running from within the source tree (binaries
    275     // that are not installed yet), skip the "lt-" prefix added to files in
    276     // the ".libs" directory to show the real (not temporary) name.
    277     if (std::strncmp(m_prog_name, "lt-", 3) == 0)
    278         m_prog_name += 3;
    279 
    280     const std::string bug =
    281         std::string("This is probably a bug in ") + m_prog_name +
    282         " or one of the libraries it uses.  Please report this problem to "
    283         PACKAGE_BUGREPORT " and provide as many details as possible "
    284         "describing how you got to this condition.";
    285 
    286     int errcode;
    287     try {
    288         int oldargc = m_argc;
    289 
    290         process_options();
    291 
    292         if (m_hflag) {
    293             INV(m_use_ui);
    294             if (oldargc != 2)
    295                 throw usage_error("-h must be given alone.");
    296 
    297             usage(std::cout);
    298             errcode = EXIT_SUCCESS;
    299         } else
    300             errcode = main();
    301     } catch (const usage_error& e) {
    302         if (m_use_ui) {
    303             std::cerr << ui::format_error(m_prog_name, e.what()) << "\n"
    304                       << ui::format_info(m_prog_name, std::string("Type `") +
    305                                          m_prog_name + " -h' for more details.")
    306                       << "\n";
    307         } else {
    308             std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
    309             std::cerr << m_prog_name << ": See " << m_manpage << " for usage "
    310                 "details.\n";
    311         }
    312         errcode = EXIT_FAILURE;
    313     } catch (const std::runtime_error& e) {
    314         if (m_use_ui) {
    315             std::cerr << ui::format_error(m_prog_name, std::string(e.what()))
    316                       << "\n";
    317         } else {
    318             std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
    319         }
    320         errcode = EXIT_FAILURE;
    321     } catch (const std::exception& e) {
    322         if (m_use_ui) {
    323             std::cerr << ui::format_error(m_prog_name, std::string("Caught "
    324                 "unexpected error: ") + e.what() + "\n" + bug) << "\n";
    325         } else {
    326             std::cerr << m_prog_name << ": ERROR: Caught unexpected error: "
    327                       << e.what() << "\n";
    328         }
    329         errcode = EXIT_FAILURE;
    330     } catch (...) {
    331         if (m_use_ui) {
    332             std::cerr << ui::format_error(m_prog_name, std::string("Caught "
    333                 "unknown error\n") + bug) << "\n";
    334         } else {
    335             std::cerr << m_prog_name << ": ERROR: Caught unknown error\n";
    336         }
    337         errcode = EXIT_FAILURE;
    338     }
    339     return errcode;
    340 }
    341