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