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 51 #if !defined(HAVE_VSNPRINTF_IN_STD) 52 namespace std { 53 using ::vsnprintf; 54 } 55 #endif // !defined(HAVE_VSNPRINTF_IN_STD) 56 57 namespace impl = atf::application; 58 #define IMPL_NAME "atf::application" 59 60 // ------------------------------------------------------------------------ 61 // The "usage_error" class. 62 // ------------------------------------------------------------------------ 63 64 impl::usage_error::usage_error(const char *fmt, ...) 65 throw() : 66 std::runtime_error("usage_error; message unformatted") 67 { 68 va_list ap; 69 70 va_start(ap, fmt); 71 std::vsnprintf(m_text, sizeof(m_text), fmt, ap); 72 va_end(ap); 73 } 74 75 impl::usage_error::~usage_error(void) 76 throw() 77 { 78 } 79 80 const char* 81 impl::usage_error::what(void) 82 const throw() 83 { 84 return m_text; 85 } 86 87 // ------------------------------------------------------------------------ 88 // The "application" class. 89 // ------------------------------------------------------------------------ 90 91 impl::option::option(char ch, 92 const std::string& a, 93 const std::string& desc) : 94 m_character(ch), 95 m_argument(a), 96 m_description(desc) 97 { 98 } 99 100 bool 101 impl::option::operator<(const impl::option& o) 102 const 103 { 104 return m_character < o.m_character; 105 } 106 107 impl::app::app(const std::string& description, 108 const std::string& manpage) : 109 m_argc(-1), 110 m_argv(NULL), 111 m_prog_name(NULL), 112 m_description(description), 113 m_manpage(manpage) 114 { 115 } 116 117 impl::app::~app(void) 118 { 119 } 120 121 bool 122 impl::app::inited(void) 123 { 124 return m_argc != -1; 125 } 126 127 impl::app::options_set 128 impl::app::options(void) 129 { 130 return specific_options(); 131 } 132 133 std::string 134 impl::app::specific_args(void) 135 const 136 { 137 return ""; 138 } 139 140 impl::app::options_set 141 impl::app::specific_options(void) 142 const 143 { 144 return options_set(); 145 } 146 147 void 148 impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED, 149 const char* arg ATF_DEFS_ATTRIBUTE_UNUSED) 150 { 151 } 152 153 void 154 impl::app::process_options(void) 155 { 156 PRE(inited()); 157 158 std::string optstr; 159 #if defined(HAVE_GNU_GETOPT) 160 optstr += '+'; // Turn on POSIX behavior. 161 #endif 162 optstr += ':'; 163 { 164 options_set opts = options(); 165 for (options_set::const_iterator iter = opts.begin(); 166 iter != opts.end(); iter++) { 167 const option& opt = (*iter); 168 169 optstr += opt.m_character; 170 if (!opt.m_argument.empty()) 171 optstr += ':'; 172 } 173 } 174 175 int ch; 176 const int old_opterr = ::opterr; 177 ::opterr = 0; 178 while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) { 179 switch (ch) { 180 case ':': 181 throw usage_error("Option -%c requires an argument.", 182 ::optopt); 183 184 case '?': 185 throw usage_error("Unknown option -%c.", ::optopt); 186 187 default: 188 process_option(ch, ::optarg); 189 } 190 } 191 m_argc -= ::optind; 192 m_argv += ::optind; 193 194 // Clear getopt state just in case the test wants to use it. 195 opterr = old_opterr; 196 optind = 1; 197 #if defined(HAVE_OPTRESET) 198 optreset = 1; 199 #endif 200 } 201 202 int 203 impl::app::run(int argc, char* const* argv) 204 { 205 PRE(argc > 0); 206 PRE(argv != NULL); 207 208 m_argc = argc; 209 m_argv = argv; 210 211 m_argv0 = m_argv[0]; 212 213 m_prog_name = std::strrchr(m_argv[0], '/'); 214 if (m_prog_name == NULL) 215 m_prog_name = m_argv[0]; 216 else 217 m_prog_name++; 218 219 // Libtool workaround: if running from within the source tree (binaries 220 // that are not installed yet), skip the "lt-" prefix added to files in 221 // the ".libs" directory to show the real (not temporary) name. 222 if (std::strncmp(m_prog_name, "lt-", 3) == 0) 223 m_prog_name += 3; 224 225 const std::string bug = 226 std::string("This is probably a bug in ") + m_prog_name + 227 " or one of the libraries it uses. Please report this problem to " 228 PACKAGE_BUGREPORT " and provide as many details as possible " 229 "describing how you got to this condition."; 230 231 int errcode; 232 try { 233 process_options(); 234 errcode = main(); 235 } catch (const usage_error& e) { 236 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 237 std::cerr << m_prog_name << ": See " << m_manpage << " for usage " 238 "details.\n"; 239 errcode = EXIT_FAILURE; 240 } catch (const std::runtime_error& e) { 241 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 242 errcode = EXIT_FAILURE; 243 } catch (const std::exception& e) { 244 std::cerr << m_prog_name << ": ERROR: Caught unexpected error: " 245 << e.what() << "\n"; 246 errcode = EXIT_FAILURE; 247 } catch (...) { 248 std::cerr << m_prog_name << ": ERROR: Caught unknown error\n"; 249 errcode = EXIT_FAILURE; 250 } 251 return errcode; 252 } 253