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