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