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