1 // Copyright (c) 2007 The NetBSD Foundation, Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions 6 // are met: 7 // 1. Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright 10 // notice, this list of conditions and the following disclaimer in the 11 // documentation and/or other materials provided with the distribution. 12 // 13 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 14 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 15 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 18 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 20 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 22 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 24 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 #include "atf-c++/detail/application.hpp" 27 28 #if defined(HAVE_CONFIG_H) 29 #include "config.h" 30 #endif 31 32 extern "C" { 33 #include <unistd.h> 34 } 35 36 #include <cstdarg> 37 #include <cstdio> 38 #include <cstdlib> 39 #include <cstring> 40 #include <iostream> 41 42 extern "C" { 43 #include "atf-c/defs.h" 44 } 45 46 #include "atf-c++/detail/sanity.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 m_argc(-1), 107 m_argv(NULL), 108 m_prog_name(NULL), 109 m_description(description), 110 m_manpage(manpage) 111 { 112 } 113 114 impl::app::~app(void) 115 { 116 } 117 118 bool 119 impl::app::inited(void) 120 { 121 return m_argc != -1; 122 } 123 124 impl::app::options_set 125 impl::app::options(void) 126 { 127 return specific_options(); 128 } 129 130 std::string 131 impl::app::specific_args(void) 132 const 133 { 134 return ""; 135 } 136 137 impl::app::options_set 138 impl::app::specific_options(void) 139 const 140 { 141 return options_set(); 142 } 143 144 void 145 impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED, 146 const char* arg ATF_DEFS_ATTRIBUTE_UNUSED) 147 { 148 } 149 150 void 151 impl::app::process_options(void) 152 { 153 PRE(inited()); 154 155 std::string optstr; 156 #if defined(HAVE_GNU_GETOPT) 157 optstr += '+'; // Turn on POSIX behavior. 158 #endif 159 optstr += ':'; 160 { 161 options_set opts = options(); 162 for (options_set::const_iterator iter = opts.begin(); 163 iter != opts.end(); iter++) { 164 const option& opt = (*iter); 165 166 optstr += opt.m_character; 167 if (!opt.m_argument.empty()) 168 optstr += ':'; 169 } 170 } 171 172 int ch; 173 const int old_opterr = ::opterr; 174 ::opterr = 0; 175 while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) { 176 switch (ch) { 177 case ':': 178 throw usage_error("Option -%c requires an argument.", 179 ::optopt); 180 181 case '?': 182 throw usage_error("Unknown option -%c.", ::optopt); 183 184 default: 185 process_option(ch, ::optarg); 186 } 187 } 188 m_argc -= ::optind; 189 m_argv += ::optind; 190 191 // Clear getopt state just in case the test wants to use it. 192 opterr = old_opterr; 193 optind = 1; 194 #if defined(HAVE_OPTRESET) 195 optreset = 1; 196 #endif 197 } 198 199 int 200 impl::app::run(int argc, char* const* argv) 201 { 202 PRE(argc > 0); 203 PRE(argv != NULL); 204 205 m_argc = argc; 206 m_argv = argv; 207 208 m_argv0 = m_argv[0]; 209 210 m_prog_name = std::strrchr(m_argv[0], '/'); 211 if (m_prog_name == NULL) 212 m_prog_name = m_argv[0]; 213 else 214 m_prog_name++; 215 216 // Libtool workaround: if running from within the source tree (binaries 217 // that are not installed yet), skip the "lt-" prefix added to files in 218 // the ".libs" directory to show the real (not temporary) name. 219 if (std::strncmp(m_prog_name, "lt-", 3) == 0) 220 m_prog_name += 3; 221 222 const std::string bug = 223 std::string("This is probably a bug in ") + m_prog_name + 224 " or one of the libraries it uses. Please report this problem to " 225 PACKAGE_BUGREPORT " and provide as many details as possible " 226 "describing how you got to this condition."; 227 228 int errcode; 229 try { 230 process_options(); 231 errcode = main(); 232 } catch (const usage_error& e) { 233 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 234 std::cerr << m_prog_name << ": See " << m_manpage << " for usage " 235 "details.\n"; 236 errcode = EXIT_FAILURE; 237 } catch (const std::runtime_error& e) { 238 std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; 239 errcode = EXIT_FAILURE; 240 } catch (const std::exception& e) { 241 std::cerr << m_prog_name << ": ERROR: Caught unexpected error: " 242 << e.what() << "\n"; 243 errcode = EXIT_FAILURE; 244 } catch (...) { 245 std::cerr << m_prog_name << ": ERROR: Caught unknown error\n"; 246 errcode = EXIT_FAILURE; 247 } 248 return errcode; 249 } 250