]> git.armaanb.net Git - gen-shell.git/blobdiff - src/Sarge/src/sarge.cpp
added argument parsing with Sarge
[gen-shell.git] / src / Sarge / src / sarge.cpp
diff --git a/src/Sarge/src/sarge.cpp b/src/Sarge/src/sarge.cpp
new file mode 100644 (file)
index 0000000..eb7d14f
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+       sarge.cpp - Implementation file for the Sarge command line argument parser project.
+       
+       Revision 0
+       
+       Features:
+                       - 
+       
+       Notes:
+                       -
+                        
+       2019/03/16, Maya Posch
+       
+*/
+
+
+#include "sarge.h"
+
+#include <iostream>
+
+
+// --- SET ARGUMENT ---
+void Sarge::setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal) {
+       std::unique_ptr<Argument> arg(new Argument);
+       arg->arg_short = arg_short;
+       arg->arg_long = arg_long;
+       arg->description = desc;
+       arg->hasValue = hasVal;
+       args.push_back(std::move(arg));
+       
+       // Set up links.
+       if (!arg_short.empty()) {
+               argNames.insert(std::pair<std::string, Argument*>(arg_short, args.back().get()));
+       }
+       
+       if (!arg_long.empty()) {
+               argNames.insert(std::pair<std::string, Argument*>(arg_long, args.back().get()));
+       }
+}
+       
+
+// --- SET ARGUMENTS ---
+void Sarge::setArguments(std::vector<Argument> args) {
+       for (Argument a : args) {
+               setArgument(a.arg_short, a.arg_long, a.description, a.hasValue);
+       }
+}
+
+
+// --- PARSE ARGUMENTS ---
+bool Sarge::parseArguments(int argc, char** argv) {
+       // The first argument is the name of the executable. After it we loop through the remaining
+       // arguments, linking flags and values.
+       execName = std::string(argv[0]);
+       bool expectValue = false;
+       std::map<std::string, Argument*>::const_iterator flag_it;
+       for (int i = 1; i < argc; ++i) {
+               // Each flag will start with a '-' character. Multiple flags can be joined together in the
+               // same string if they're the short form flag type (one character per flag).
+               std::string entry(argv[i]);
+               
+               if (expectValue) {
+                       // Copy value.
+                       flag_it->second->value = entry;                 
+                       expectValue = false;
+               }
+               else if (entry.compare(0, 1, "-") == 0) {
+                       if (textArguments.size() > 0) { 
+                               std::cerr << "Flags not allowed after text arguments." << std::endl; 
+                       }
+                       
+                       // Parse flag.
+                       // First check for the long form.
+                       if (entry.compare(0, 2, "--") == 0) {
+                               // Long form of flag.
+                               entry.erase(0, 2); // Erase the double dash since we no longer need it.
+                       
+                               flag_it = argNames.find(entry);
+                               if (flag_it == argNames.end()) {
+                                       // Flag wasn't found. Abort.
+                                       std::cerr << "Long flag " << entry << " wasn't found." << std::endl;
+                                       return false;
+                               }
+                               
+                               // Mark as found.
+                               flag_it->second->parsed = true;
+                               ++flagCounter;
+                               
+                               if (flag_it->second->hasValue) {
+                                       expectValue = true; // Next argument has to be a value string.
+                               }
+                       }
+                       else {
+                               // Parse short form flag. Parse all of them sequentially. Only the last one
+                               // is allowed to have an additional value following it.
+                               entry.erase(0, 1); // Erase the dash.                           
+                               for (int i = 0; i < entry.length(); ++i) {
+                                       std::string k(&(entry[i]), 1);
+                                       flag_it = argNames.find(k);
+                                       if (flag_it == argNames.end()) {
+                                               // Flag wasn't found. Abort.
+                                               std::cerr << "Short flag " << k << " wasn't found." << std::endl;
+                                               return false;
+                                       }
+                                       
+                                       // Mark as found.
+                                       flag_it->second->parsed = true;
+                                       ++flagCounter;
+                                       
+                                       if (flag_it->second->hasValue) {
+                                               if (i != (entry.length() - 1)) {
+                                                       // Flag isn't at end, thus cannot have value.
+                                                       std::cerr << "Flag " << k << " needs to be followed by a value string."
+                                                               << std::endl;
+                                                       return false;
+                                               } else {
+                                                       expectValue = true; // Next argument has to be a value string.
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else {
+                       // Add to text argument vector.
+                       textArguments.push_back(entry);
+               }
+       }
+       
+       parsed = true;
+       
+       return true;
+}
+
+
+// --- GET FLAG ---
+// Returns whether the flag was found, along with the value if relevant.
+bool Sarge::getFlag(std::string arg_flag, std::string &arg_value) {
+       if (!parsed) { return false; }
+       
+       std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
+       if (it == argNames.end()) { return false; }
+       if (!it->second->parsed) { return false; }
+       
+       if (it->second->hasValue) { arg_value = it->second->value; }
+       
+       return true;
+}
+
+
+// --- EXISTS ---
+// Returns whether the flag was found.
+bool Sarge::exists(std::string arg_flag) {
+       if (!parsed) { return false; }
+       
+       std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
+       if (it == argNames.end()) { return false; }     
+       if (!it->second->parsed) { return false; }
+       
+       return true;
+}
+
+
+// --- GET TEXT ARGUMENT ---
+// Updates the value parameter with the text argument (unbound value) if found.
+// Index starts at 0.
+// Returns true if found, else false.
+bool Sarge::getTextArgument(uint32_t index, std::string &value) {
+       if (index < textArguments.size()) { value = textArguments.at(index); return true; }
+       
+       return false;
+}
+
+
+// --- PRINT HELP ---
+// Prints out the application description, usage and available options.
+void Sarge::printHelp() {
+       std::cout << std::endl << description << std::endl;
+       std::cout << std::endl << "Usage:" << std::endl;
+       std::cout << "\t" << usage << std::endl;
+       std::cout << std::endl;
+       std::cout << "Options: " << std::endl;
+       
+       // Determine whitespace needed between arg_long and description.
+       int count = 1; 
+       std::vector<std::unique_ptr<Argument> >::const_iterator it;
+       for (it = args.cbegin(); it != args.cend(); ++it) {
+               if ((*it)->arg_long.size() > count) { 
+                       count = (*it)->arg_long.size();
+               }
+       }
+       
+       count += 3; // Number of actual spaces between the longest arg_long and description.
+
+       // Print out the options.
+       for (it = args.cbegin(); it != args.cend(); ++it) {
+               std::cout << ((*it)->arg_short.empty() ? "    " : "-" + (*it)->arg_short + ", ") 
+                                                                                                                                       << "--" << (*it)->arg_long;
+               std::cout << std::string(count - (*it)->arg_long.size(), ' ') << (*it)->description 
+                                                                                                                                       << std::endl;
+       }
+}