]> git.armaanb.net Git - gen-shell.git/blob - src/Sarge/src/sarge.cpp
added argument parsing with Sarge
[gen-shell.git] / src / Sarge / src / sarge.cpp
1 /*
2         sarge.cpp - Implementation file for the Sarge command line argument parser project.
3         
4         Revision 0
5         
6         Features:
7                         - 
8         
9         Notes:
10                         -
11                          
12         2019/03/16, Maya Posch
13         
14 */
15
16
17 #include "sarge.h"
18
19 #include <iostream>
20
21
22 // --- SET ARGUMENT ---
23 void Sarge::setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal) {
24         std::unique_ptr<Argument> arg(new Argument);
25         arg->arg_short = arg_short;
26         arg->arg_long = arg_long;
27         arg->description = desc;
28         arg->hasValue = hasVal;
29         args.push_back(std::move(arg));
30         
31         // Set up links.
32         if (!arg_short.empty()) {
33                 argNames.insert(std::pair<std::string, Argument*>(arg_short, args.back().get()));
34         }
35         
36         if (!arg_long.empty()) {
37                 argNames.insert(std::pair<std::string, Argument*>(arg_long, args.back().get()));
38         }
39 }
40         
41
42 // --- SET ARGUMENTS ---
43 void Sarge::setArguments(std::vector<Argument> args) {
44         for (Argument a : args) {
45                 setArgument(a.arg_short, a.arg_long, a.description, a.hasValue);
46         }
47 }
48
49
50 // --- PARSE ARGUMENTS ---
51 bool Sarge::parseArguments(int argc, char** argv) {
52         // The first argument is the name of the executable. After it we loop through the remaining
53         // arguments, linking flags and values.
54         execName = std::string(argv[0]);
55         bool expectValue = false;
56         std::map<std::string, Argument*>::const_iterator flag_it;
57         for (int i = 1; i < argc; ++i) {
58                 // Each flag will start with a '-' character. Multiple flags can be joined together in the
59                 // same string if they're the short form flag type (one character per flag).
60                 std::string entry(argv[i]);
61                 
62                 if (expectValue) {
63                         // Copy value.
64                         flag_it->second->value = entry;                 
65                         expectValue = false;
66                 }
67                 else if (entry.compare(0, 1, "-") == 0) {
68                         if (textArguments.size() > 0) { 
69                                 std::cerr << "Flags not allowed after text arguments." << std::endl; 
70                         }
71                         
72                         // Parse flag.
73                         // First check for the long form.
74                         if (entry.compare(0, 2, "--") == 0) {
75                                 // Long form of flag.
76                                 entry.erase(0, 2); // Erase the double dash since we no longer need it.
77                         
78                                 flag_it = argNames.find(entry);
79                                 if (flag_it == argNames.end()) {
80                                         // Flag wasn't found. Abort.
81                                         std::cerr << "Long flag " << entry << " wasn't found." << std::endl;
82                                         return false;
83                                 }
84                                 
85                                 // Mark as found.
86                                 flag_it->second->parsed = true;
87                                 ++flagCounter;
88                                 
89                                 if (flag_it->second->hasValue) {
90                                         expectValue = true; // Next argument has to be a value string.
91                                 }
92                         }
93                         else {
94                                 // Parse short form flag. Parse all of them sequentially. Only the last one
95                                 // is allowed to have an additional value following it.
96                                 entry.erase(0, 1); // Erase the dash.                           
97                                 for (int i = 0; i < entry.length(); ++i) {
98                                         std::string k(&(entry[i]), 1);
99                                         flag_it = argNames.find(k);
100                                         if (flag_it == argNames.end()) {
101                                                 // Flag wasn't found. Abort.
102                                                 std::cerr << "Short flag " << k << " wasn't found." << std::endl;
103                                                 return false;
104                                         }
105                                         
106                                         // Mark as found.
107                                         flag_it->second->parsed = true;
108                                         ++flagCounter;
109                                         
110                                         if (flag_it->second->hasValue) {
111                                                 if (i != (entry.length() - 1)) {
112                                                         // Flag isn't at end, thus cannot have value.
113                                                         std::cerr << "Flag " << k << " needs to be followed by a value string."
114                                                                 << std::endl;
115                                                         return false;
116                                                 } else {
117                                                         expectValue = true; // Next argument has to be a value string.
118                                                 }
119                                         }
120                                 }
121                         }
122                 }
123                 else {
124                         // Add to text argument vector.
125                         textArguments.push_back(entry);
126                 }
127         }
128         
129         parsed = true;
130         
131         return true;
132 }
133
134
135 // --- GET FLAG ---
136 // Returns whether the flag was found, along with the value if relevant.
137 bool Sarge::getFlag(std::string arg_flag, std::string &arg_value) {
138         if (!parsed) { return false; }
139         
140         std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
141         if (it == argNames.end()) { return false; }
142         if (!it->second->parsed) { return false; }
143         
144         if (it->second->hasValue) { arg_value = it->second->value; }
145         
146         return true;
147 }
148
149
150 // --- EXISTS ---
151 // Returns whether the flag was found.
152 bool Sarge::exists(std::string arg_flag) {
153         if (!parsed) { return false; }
154         
155         std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
156         if (it == argNames.end()) { return false; }     
157         if (!it->second->parsed) { return false; }
158         
159         return true;
160 }
161
162
163 // --- GET TEXT ARGUMENT ---
164 // Updates the value parameter with the text argument (unbound value) if found.
165 // Index starts at 0.
166 // Returns true if found, else false.
167 bool Sarge::getTextArgument(uint32_t index, std::string &value) {
168         if (index < textArguments.size()) { value = textArguments.at(index); return true; }
169         
170         return false;
171 }
172
173
174 // --- PRINT HELP ---
175 // Prints out the application description, usage and available options.
176 void Sarge::printHelp() {
177         std::cout << std::endl << description << std::endl;
178         std::cout << std::endl << "Usage:" << std::endl;
179         std::cout << "\t" << usage << std::endl;
180         std::cout << std::endl;
181         std::cout << "Options: " << std::endl;
182         
183         // Determine whitespace needed between arg_long and description.
184         int count = 1; 
185         std::vector<std::unique_ptr<Argument> >::const_iterator it;
186         for (it = args.cbegin(); it != args.cend(); ++it) {
187                 if ((*it)->arg_long.size() > count) { 
188                         count = (*it)->arg_long.size();
189                 }
190         }
191         
192         count += 3; // Number of actual spaces between the longest arg_long and description.
193
194         // Print out the options.
195         for (it = args.cbegin(); it != args.cend(); ++it) {
196                 std::cout << ((*it)->arg_short.empty() ? "    " : "-" + (*it)->arg_short + ", ") 
197                                                                                                                                         << "--" << (*it)->arg_long;
198                 std::cout << std::string(count - (*it)->arg_long.size(), ' ') << (*it)->description 
199                                                                                                                                         << std::endl;
200         }
201 }