application.cpp revision 296373
1// Copyright (c) 2007 The NetBSD Foundation, Inc. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions 6// are met: 7// 1. Redistributions of source code must retain the above copyright 8// notice, this list of conditions and the following disclaimer. 9// 2. Redistributions in binary form must reproduce the above copyright 10// notice, this list of conditions and the following disclaimer in the 11// documentation and/or other materials provided with the distribution. 12// 13// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26#include "atf-c++/detail/application.hpp" 27 28#if defined(HAVE_CONFIG_H) 29#include "config.h" 30#endif 31 32extern "C" { 33#include <unistd.h> 34} 35 36#include <cstdarg> 37#include <cstdio> 38#include <cstdlib> 39#include <cstring> 40#include <iostream> 41 42extern "C" { 43#include "atf-c/defs.h" 44} 45 46#include "atf-c++/detail/sanity.hpp" 47 48#if !defined(HAVE_VSNPRINTF_IN_STD) 49namespace std { 50using ::vsnprintf; 51} 52#endif // !defined(HAVE_VSNPRINTF_IN_STD) 53 54namespace impl = atf::application; 55#define IMPL_NAME "atf::application" 56 57// ------------------------------------------------------------------------ 58// The "usage_error" class. 59// ------------------------------------------------------------------------ 60 61impl::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 72impl::usage_error::~usage_error(void) 73 throw() 74{ 75} 76 77const char* 78impl::usage_error::what(void) 79 const throw() 80{ 81 return m_text; 82} 83 84// ------------------------------------------------------------------------ 85// The "application" class. 86// ------------------------------------------------------------------------ 87 88impl::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 97bool 98impl::option::operator<(const impl::option& o) 99 const 100{ 101 return m_character < o.m_character; 102} 103 104impl::app::app(const std::string& description, 105 const std::string& manpage) : 106 m_argc(-1), 107 m_argv(NULL), 108 m_prog_name(NULL), 109 m_description(description), 110 m_manpage(manpage) 111{ 112} 113 114impl::app::~app(void) 115{ 116} 117 118bool 119impl::app::inited(void) 120{ 121 return m_argc != -1; 122} 123 124impl::app::options_set 125impl::app::options(void) 126{ 127 return specific_options(); 128} 129 130std::string 131impl::app::specific_args(void) 132 const 133{ 134 return ""; 135} 136 137impl::app::options_set 138impl::app::specific_options(void) 139 const 140{ 141 return options_set(); 142} 143 144void 145impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED, 146 const char* arg ATF_DEFS_ATTRIBUTE_UNUSED) 147{ 148} 149 150void 151impl::app::process_options(void) 152{ 153 PRE(inited()); 154 155 std::string optstr; 156#if defined(HAVE_GNU_GETOPT) 157 optstr += '+'; // Turn on POSIX behavior. 158#endif 159 optstr += ':'; 160 { 161 options_set opts = options(); 162 for (options_set::const_iterator iter = opts.begin(); 163 iter != opts.end(); iter++) { 164 const option& opt = (*iter); 165 166 optstr += opt.m_character; 167 if (!opt.m_argument.empty()) 168 optstr += ':'; 169 } 170 } 171 172 int ch; 173 const int old_opterr = ::opterr; 174 ::opterr = 0; 175 while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) { 176 switch (ch) { 177 case ':': 178 throw usage_error("Option -%c requires an argument.", 179 ::optopt); 180 181 case '?': 182 throw usage_error("Unknown option -%c.", ::optopt); 183 184 default: 185 process_option(ch, ::optarg); 186 } 187 } 188 m_argc -= ::optind; 189 m_argv += ::optind; 190 191 // Clear getopt state just in case the test wants to use it. 192 opterr = old_opterr; 193 optind = 1; 194#if defined(HAVE_OPTRESET) 195 optreset = 1; 196#endif 197} 198 199int 200impl::app::run(int argc, char* const* argv) 201{ 202 PRE(argc > 0); 203 PRE(argv != NULL); 204 205 m_argc = argc; 206 m_argv = argv; 207 208 m_argv0 = m_argv[0]; 209 210 m_prog_name = std::strrchr(m_argv[0], '/'); 211 if (m_prog_name == NULL) 212 m_prog_name = m_argv[0]; 213 else 214 m_prog_name++; 215 216 // Libtool workaround: if running from within the source tree (binaries 217 // that are not installed yet), skip the "lt-" prefix added to files in 218 // the ".libs" directory to show the real (not temporary) name. 219 if (std::strncmp(m_prog_name, "lt-", 3) == 0) 220 m_prog_name += 3; 221 222 const std::string bug = 223 std::string("This is probably a bug in ") + m_prog_name + 224 " or one of the libraries it uses. Please report this problem to " 225 PACKAGE_BUGREPORT " and provide as many details as possible " 226 "describing how you got to this condition."; 227 228 int errcode; 229 try { 230 process_options(); 231 errcode = main(); 232 } catch (const usage_error& e) { 233 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 234 std::cerr << m_prog_name << ": See " << m_manpage << " for usage " 235 "details.\n"; 236 errcode = EXIT_FAILURE; 237 } catch (const std::runtime_error& e) { 238 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 239 errcode = EXIT_FAILURE; 240 } catch (const std::exception& e) { 241 std::cerr << m_prog_name << ": ERROR: Caught unexpected error: " 242 << e.what() << "\n"; 243 errcode = EXIT_FAILURE; 244 } catch (...) { 245 std::cerr << m_prog_name << ": ERROR: Caught unknown error\n"; 246 errcode = EXIT_FAILURE; 247 } 248 return errcode; 249} 250