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 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 extern "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) 53 namespace std { 54 using ::vsnprintf; 55 } 56 #endif // !defined(HAVE_VSNPRINTF_IN_STD) 57 58 namespace impl = atf::application; 59 #define IMPL_NAME "atf::application" 60 61 // ------------------------------------------------------------------------ 62 // The "usage_error" class. 63 // ------------------------------------------------------------------------ 64 65 impl::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 76 impl::usage_error::~usage_error(void) 77 throw() 78 { 79 } 80 81 const char* 82 impl::usage_error::what(void) 83 const throw() 84 { 85 return m_text; 86 } 87 88 // ------------------------------------------------------------------------ 89 // The "application" class. 90 // ------------------------------------------------------------------------ 91 92 impl::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 101 bool 102 impl::option::operator<(const impl::option& o) 103 const 104 { 105 return m_character < o.m_character; 106 } 107 108 impl::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 123 impl::app::~app(void) 124 { 125 } 126 127 bool 128 impl::app::inited(void) 129 { 130 return m_argc != -1; 131 } 132 133 impl::app::options_set 134 impl::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 143 std::string 144 impl::app::specific_args(void) 145 const 146 { 147 return ""; 148 } 149 150 impl::app::options_set 151 impl::app::specific_options(void) 152 const 153 { 154 return options_set(); 155 } 156 157 void 158 impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED, 159 const char* arg ATF_DEFS_ATTRIBUTE_UNUSED) 160 { 161 } 162 163 void 164 impl::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 217 void 218 impl::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 262 int 263 impl::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