1 // Copyright 2017-2019 VMware, Inc. 2 // SPDX-License-Identifier: BSD-2-Clause 3 // 4 // The BSD-2 license (the License) set forth below applies to all parts of the 5 // Cascade project. You may not use this file except in compliance with the 6 // License. 7 // 8 // BSD-2 License 9 // 10 // Redistribution and use in source and binary forms, with or without 11 // modification, are permitted provided that the following conditions are met: 12 // 13 // 1. Redistributions of source code must retain the above copyright notice, this 14 // list of conditions and the following disclaimer. 15 // 16 // 2. Redistributions in binary form must reproduce the above copyright notice, 17 // this list of conditions and the following disclaimer in the documentation 18 // and/or other materials provided with the distribution. 19 // 20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND 21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 #ifndef CASCADE_SRC_CL_SIMPLE_H 32 #define CASCADE_SRC_CL_SIMPLE_H 33 34 #include <iomanip> 35 #include "args.h" 36 #include "flag_arg.h" 37 38 namespace cascade::cl { 39 40 class Simple { 41 public: 42 static void read(int argc, char** argv, std::ostream& out = std::cout, std::ostream& err = std::cerr) { 43 Group::create("Help and Command Line Options"); 44 auto& help = FlagArg::create("--help") 45 .alias("-h") 46 .description("Print command line information and quit"); 47 StrArg<std::string>::create("--config") 48 .usage("<path>") 49 .initial("...") 50 .description("Import command line arguments from a config file"); 51 52 read_args(argc, argv, err); 53 if (help) { 54 write_help(out); 55 exit(0); 56 } 57 for (auto i = Args::arg_begin(), ie = Args::arg_end(); i != ie; ++i) { 58 if (error(*i)) { 59 err << "Error (" << *((*i)->alias_begin()) << "): "; 60 write_error(err, *i); 61 err << std::endl; 62 exit(1); 63 } 64 } 65 } 66 67 private: read_args(int argc,char ** argv,std::ostream & err)68 static void read_args(int argc, char** argv, std::ostream& err) { 69 std::vector<std::string> args; 70 std::stringstream ss; 71 for (int i = 0; i < argc; ++i) { 72 ss << std::quoted(argv[i]) << std::endl; 73 } 74 get_args(ss, args, err); 75 std::vector<char*> cps; 76 for (const auto& a : args) { 77 cps.push_back((char*)a.c_str()); 78 } 79 Args::read(cps.size(), cps.data()); 80 } get_args(std::istream & is,std::vector<std::string> & args,std::ostream & err)81 static void get_args(std::istream& is, std::vector<std::string>& args, std::ostream& err) { 82 while (!is.eof()) { 83 const auto arg = get_arg(is); 84 if (arg == "--config") { 85 const auto path = get_arg(is); 86 std::ifstream ifs(path); 87 if (!ifs.is_open()) { 88 err << "Error: Unable to open config file \"" << path << "\"!" << std::endl; 89 exit(1); 90 } 91 get_args(ifs, args, err); 92 } else { 93 args.push_back(arg); 94 } 95 } 96 args.pop_back(); 97 } get_arg(std::istream & is)98 static std::string get_arg(std::istream& is) { 99 while (isspace(is.peek()) || is.peek() == '#') { 100 for (; isspace(is.peek()); is.get()); 101 if (is.peek() == '#') { 102 while (is.get() != '\n'); 103 } 104 } 105 std::string s = ""; 106 is >> std::quoted(s); 107 return s; 108 } 109 error(const Arg * a)110 static bool error(const Arg* a) { 111 return a->error() || a->duplicated() || (a->required() && !a->provided()); 112 } write_error(std::ostream & os,Arg * a)113 static void write_error(std::ostream& os, Arg* a) { 114 if (a->error()) { 115 os << "Unable to parse argument!"; 116 } else if (a->duplicated()) { 117 os << "Argument appears more than once!"; 118 } else { 119 os << "Required value not provided!"; 120 } 121 } 122 write_help(std::ostream & os)123 static void write_help(std::ostream& os) { 124 std::vector<Group*> gs(Args::group_begin(), Args::group_end()); 125 std::sort(gs.begin(), gs.end(), [](Group* g1, Group* g2) {return g1->name() <= g2->name();}); 126 127 for (auto i = gs.begin(), ie = gs.end(); i != ie; ++i) { 128 os << std::endl << (*i)->name() << ":" << std::endl; 129 130 std::vector<Arg*> as((*i)->arg_begin(), (*i)->arg_end()); 131 std::sort(as.begin(), as.end(), [](Arg* a1, Arg* a2) {return *(a1->alias_begin()) <= *(a2->alias_begin());}); 132 133 for (auto j = as.begin(), je = as.end(); j != je; ++j) { 134 os << " "; 135 for (auto k = (*j)->alias_begin(), ke = (*j)->alias_end(); k != ke; ++k) { 136 os << *k << " "; 137 } 138 os << ((*j)->usage() == "" ? "" : (*j)->usage()) << std::endl; 139 os << " Desc: " << ((*j)->description() == "" ? "<none>" : (*j)->description()) << std::endl; 140 os << " Required: " << ((*j)->required() ? "yes" : "no") << std::endl; 141 if (error(*j)) { 142 os << " Error: "; 143 write_error(os, *j); 144 os << std::endl; 145 } else { 146 os << " Value: "; 147 (*j)->write(os); 148 os << std::endl; 149 } 150 } 151 } 152 os << std::endl; 153 } 154 }; 155 156 } // namespace cascade::cl 157 158 #endif 159