application.cpp revision 256281
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#include "ui.hpp" 51 52#if !defined(HAVE_VSNPRINTF_IN_STD) 53namespace std { 54using ::vsnprintf; 55} 56#endif // !defined(HAVE_VSNPRINTF_IN_STD) 57 58namespace impl = atf::application; 59#define IMPL_NAME "atf::application" 60 61// ------------------------------------------------------------------------ 62// The "usage_error" class. 63// ------------------------------------------------------------------------ 64 65impl::usage_error::usage_error(const char *fmt, ...) 66 throw() : 67 std::runtime_error("usage_error; message unformatted") 68{ 69 va_list ap; 70 71 va_start(ap, fmt); 72 std::vsnprintf(m_text, sizeof(m_text), fmt, ap); 73 va_end(ap); 74} 75 76impl::usage_error::~usage_error(void) 77 throw() 78{ 79} 80 81const char* 82impl::usage_error::what(void) 83 const throw() 84{ 85 return m_text; 86} 87 88// ------------------------------------------------------------------------ 89// The "application" class. 90// ------------------------------------------------------------------------ 91 92impl::option::option(char ch, 93 const std::string& a, 94 const std::string& desc) : 95 m_character(ch), 96 m_argument(a), 97 m_description(desc) 98{ 99} 100 101bool 102impl::option::operator<(const impl::option& o) 103 const 104{ 105 return m_character < o.m_character; 106} 107 108impl::app::app(const std::string& description, 109 const std::string& manpage, 110 const std::string& global_manpage, 111 const bool use_ui) : 112 m_hflag(false), 113 m_argc(-1), 114 m_argv(NULL), 115 m_prog_name(NULL), 116 m_description(description), 117 m_manpage(manpage), 118 m_global_manpage(global_manpage), 119 m_use_ui(use_ui) 120{ 121} 122 123impl::app::~app(void) 124{ 125} 126 127bool 128impl::app::inited(void) 129{ 130 return m_argc != -1; 131} 132 133impl::app::options_set 134impl::app::options(void) 135{ 136 options_set opts = specific_options(); 137 if (m_use_ui) { 138 opts.insert(option('h', "", "Shows this help message")); 139 } 140 return opts; 141} 142 143std::string 144impl::app::specific_args(void) 145 const 146{ 147 return ""; 148} 149 150impl::app::options_set 151impl::app::specific_options(void) 152 const 153{ 154 return options_set(); 155} 156 157void 158impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED, 159 const char* arg ATF_DEFS_ATTRIBUTE_UNUSED) 160{ 161} 162 163void 164impl::app::process_options(void) 165{ 166 PRE(inited()); 167 168 std::string optstr; 169#if defined(HAVE_GNU_GETOPT) 170 optstr += '+'; // Turn on POSIX behavior. 171#endif 172 optstr += ':'; 173 { 174 options_set opts = options(); 175 for (options_set::const_iterator iter = opts.begin(); 176 iter != opts.end(); iter++) { 177 const option& opt = (*iter); 178 179 optstr += opt.m_character; 180 if (!opt.m_argument.empty()) 181 optstr += ':'; 182 } 183 } 184 185 int ch; 186 const int old_opterr = ::opterr; 187 ::opterr = 0; 188 while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) { 189 switch (ch) { 190 case 'h': 191 INV(m_use_ui); 192 m_hflag = true; 193 break; 194 195 case ':': 196 throw usage_error("Option -%c requires an argument.", 197 ::optopt); 198 199 case '?': 200 throw usage_error("Unknown option -%c.", ::optopt); 201 202 default: 203 process_option(ch, ::optarg); 204 } 205 } 206 m_argc -= ::optind; 207 m_argv += ::optind; 208 209 // Clear getopt state just in case the test wants to use it. 210 opterr = old_opterr; 211 optind = 1; 212#if defined(HAVE_OPTRESET) 213 optreset = 1; 214#endif 215} 216 217void 218impl::app::usage(std::ostream& os) 219{ 220 PRE(inited()); 221 222 std::string args = specific_args(); 223 if (!args.empty()) 224 args = " " + args; 225 os << ui::format_text_with_tag(std::string(m_prog_name) + " [options]" + 226 args, "Usage: ", false) << "\n\n" 227 << ui::format_text(m_description) << "\n\n"; 228 229 options_set opts = options(); 230 INV(!opts.empty()); 231 os << "Available options:\n"; 232 size_t coldesc = 0; 233 for (options_set::const_iterator iter = opts.begin(); 234 iter != opts.end(); iter++) { 235 const option& opt = (*iter); 236 237 if (opt.m_argument.length() + 1 > coldesc) 238 coldesc = opt.m_argument.length() + 1; 239 } 240 for (options_set::const_iterator iter = opts.begin(); 241 iter != opts.end(); iter++) { 242 const option& opt = (*iter); 243 244 std::string tag = std::string(" -") + opt.m_character; 245 if (opt.m_argument.empty()) 246 tag += " "; 247 else 248 tag += " " + opt.m_argument + " "; 249 os << ui::format_text_with_tag(opt.m_description, tag, false, 250 coldesc + 10) << "\n"; 251 } 252 os << "\n"; 253 254 std::string gmp; 255 if (!m_global_manpage.empty()) 256 gmp = " and " + m_global_manpage; 257 os << ui::format_text("For more details please see " + m_manpage + 258 gmp + ".") 259 << "\n"; 260} 261 262int 263impl::app::run(int argc, char* const* argv) 264{ 265 PRE(argc > 0); 266 PRE(argv != NULL); 267 268 m_argc = argc; 269 m_argv = argv; 270 271 m_argv0 = m_argv[0]; 272 273 m_prog_name = std::strrchr(m_argv[0], '/'); 274 if (m_prog_name == NULL) 275 m_prog_name = m_argv[0]; 276 else 277 m_prog_name++; 278 279 // Libtool workaround: if running from within the source tree (binaries 280 // that are not installed yet), skip the "lt-" prefix added to files in 281 // the ".libs" directory to show the real (not temporary) name. 282 if (std::strncmp(m_prog_name, "lt-", 3) == 0) 283 m_prog_name += 3; 284 285 const std::string bug = 286 std::string("This is probably a bug in ") + m_prog_name + 287 " or one of the libraries it uses. Please report this problem to " 288 PACKAGE_BUGREPORT " and provide as many details as possible " 289 "describing how you got to this condition."; 290 291 int errcode; 292 try { 293 int oldargc = m_argc; 294 295 process_options(); 296 297 if (m_hflag) { 298 INV(m_use_ui); 299 if (oldargc != 2) 300 throw usage_error("-h must be given alone."); 301 302 usage(std::cout); 303 errcode = EXIT_SUCCESS; 304 } else 305 errcode = main(); 306 } catch (const usage_error& e) { 307 if (m_use_ui) { 308 std::cerr << ui::format_error(m_prog_name, e.what()) << "\n" 309 << ui::format_info(m_prog_name, std::string("Type `") + 310 m_prog_name + " -h' for more details.") 311 << "\n"; 312 } else { 313 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 314 std::cerr << m_prog_name << ": See " << m_manpage << " for usage " 315 "details.\n"; 316 } 317 errcode = EXIT_FAILURE; 318 } catch (const std::runtime_error& e) { 319 if (m_use_ui) { 320 std::cerr << ui::format_error(m_prog_name, std::string(e.what())) 321 << "\n"; 322 } else { 323 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 324 } 325 errcode = EXIT_FAILURE; 326 } catch (const std::exception& e) { 327 if (m_use_ui) { 328 std::cerr << ui::format_error(m_prog_name, std::string("Caught " 329 "unexpected error: ") + e.what() + "\n" + bug) << "\n"; 330 } else { 331 std::cerr << m_prog_name << ": ERROR: Caught unexpected error: " 332 << e.what() << "\n"; 333 } 334 errcode = EXIT_FAILURE; 335 } catch (...) { 336 if (m_use_ui) { 337 std::cerr << ui::format_error(m_prog_name, std::string("Caught " 338 "unknown error\n") + bug) << "\n"; 339 } else { 340 std::cerr << m_prog_name << ": ERROR: Caught unknown error\n"; 341 } 342 errcode = EXIT_FAILURE; 343 } 344 return errcode; 345} 346