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