1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
11 #include <anna/config/defines.hpp>
12 #include <anna/core/RuntimeException.hpp>
13 #include <anna/xml/xml.hpp>
15 #include <anna/core/util/CommandLine.hpp>
16 #include <anna/core/util/Tokenizer.hpp>
21 //--------------------------------------------------------------------------------
22 // Notifica al Parser que la opcion 'argumentExpression' es soportada
24 // (1) Verifica que la opcion no haya sido registrada previamente.
25 //--------------------------------------------------------------------------------
26 void CommandLine::add(const char* argumentExpression, Argument::Type type, const char* comment, const bool needValue)
28 Guard guard(a_mutex, "CommandLine::add");
30 std::string arg1 = "", arg2 = "";
31 if (!argumentExpression) { std::cerr << "Invalid argument expression: cannot register NULL literal on command-line" << std::endl; return; }
33 anna::Tokenizer expression;
34 expression.apply (argumentExpression, ",");
35 if (expression.size() > 2) { std::cerr << "Invalid argument expression '" << argumentExpression << "': only '<letter>[,<word>]' or '<word>[,<letter>]' are supported for command-line registration" << std::endl; return; }
37 anna::Tokenizer::const_iterator tok_it = expression.begin();
38 arg1 = anna::Tokenizer::data(tok_it);
39 // No leading hyphens in the name:
40 if (arg1[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
43 if (tok_it != expression.end()) {
44 arg2 = anna::Tokenizer::data(tok_it);
45 // No hyphens in the name:
46 if (arg2[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
49 // swap to have first non empty:
50 if (arg1 == "") { arg1 = arg2; arg2 = ""; }
52 // If both provided, one will be single letter and the other, a word:
56 if ((s1 + s2) == 2) { std::cerr << "Invalid argument expression '" << argumentExpression << "': only '<letter>[,<word>]' or '<word>[,<letter>]' are supported for command-line registration (provided two letters !)" << std::endl; return; }
57 if ((s1 != 1) && (s2 != 1)) { std::cerr << "Invalid argument expression '" << argumentExpression << "': only '<letter>[,<word>]' or '<word>[,<letter>]' are supported for command-line registration (provided two words !)" << std::endl; return; }
61 a_arguments.push_back(new Variable(arg1, arg2, type, comment, needValue));
64 void CommandLine::initialize(const char** argv, const int argc, int positionalArguments)
66 if (argc < 1) throw RuntimeException("Provided argc < 1 as command-line argv size !", ANNA_FILE_LOCATION);
67 if (positionalArguments < 0) throw RuntimeException("Provided negative number of positional arguments as command-line initializer", ANNA_FILE_LOCATION);
68 if (positionalArguments > (argc-1)) throw RuntimeException("Provided positional arguments > (argc - 1) as command-line initializer", ANNA_FILE_LOCATION);
69 a_positionalArguments = positionalArguments;
75 //--------------------------------------------------------------------------------
76 // Verifica que todos los argumentos declarados como obligatorios estan en la
78 //--------------------------------------------------------------------------------
79 void CommandLine::verify()
82 throw RuntimeException("CommandLine was not initialized", ANNA_FILE_LOCATION);
84 for(int i = 0, maxi = a_arguments.size(); i < maxi; i ++) {
85 if(a_arguments [i]->getType() == Argument::Mandatory)
86 getValue(a_arguments [i]->getName1().c_str()); // exit program if not found (exitOnFault = true by default)
90 //--------------------------------------------------------------------------------
91 // Obtiene el valor para opcion 'argumentExpression'.
93 // (1) Realiza el analisis de la linea de comandos.
94 // (2) Verifica que la opcion recibida existe en la lista de opciones registradas.
95 // (3) Si la opcion 'argumentExpression' existe en la linea de comandos ...
96 // (3.1) Si el argumentExpression debe ir seguido por un valor/valores y no existe
98 // (3.2) Si el argumentExpression no necesita valor, simplemente, le asigna una cadena
99 // que contiene 'true', en otro caso devolvera NULL.
101 // (4) Si no existe en la linea de comandos y es un argumentExpression obligatorio => error.
103 // Si el arguemento solicitado no esta en la LC y es opcional devolvera NULL.
104 //--------------------------------------------------------------------------------
105 const char* CommandLine::getValue(const char* argumentExpression, const bool exitOnFault)
107 const char* result = NULL;
108 const Variable* argument = NULL;
112 if((analized = analize()) == true) { // (1)
113 if((argument = search(argumentExpression)) != NULL) { // (2)
115 result = argument->getValue();
117 if(argument->getIsOn() == true) { // (3)
118 if(argument->getNeedValue() == true && result == NULL) // (3.1)
120 else if(argument->getNeedValue() == false) {
126 } else if(argument->getType() == Argument::Mandatory) // (4)
131 if(analized == false) {
134 } else if(error == true && exitOnFault == true) {
135 cout << "Variable: '" << argumentExpression << "' is not valid" << endl << endl;
143 // Auxiliary function:
144 // Returns first no-leading hyphen position
145 int removeLeadingHyphens(std::string &argv) {
147 // Move until no hyphen:
148 for (int i=0; i < argv.size(); i++)
149 if (argv[i] != '-') { startPos = i; argv = argv.substr(i); break; }
152 if (startPos == -1) return -1;
156 if (argv.size() != 1) return -1;
160 if (argv.size() == 1) return -1;
163 if (startPos > 2) return -1;
168 //--------------------------------------------------------------------------------
169 // Analize command line to store the arguments
170 //--------------------------------------------------------------------------------
171 bool CommandLine::analize()
175 int i = a_positionalArguments + 1;
178 if(a_wasParsed == true) // already analyzed
181 Guard guard(a_mutex, "CommandLine::analyze");
183 if(a_wasParsed == true)
187 while(i < a_argc && result == true) {
190 if (::removeLeadingHyphens(aux) == -1) {
194 if((variable = const_cast <Variable*>(search(aux.c_str()))) != NULL) {
195 variable->setIsOn(true);
196 if (variable->getNeedValue()) {
198 if (i < a_argc) { variable->setValue(a_argv[i]); }
199 else { result = false ; break; }
203 cout << "Variable: " << aux << " unreconized" << endl;
211 return (a_wasParsed = result);
214 const CommandLine::Variable* CommandLine::search(const char *argumentExpression) const
216 if (!argumentExpression) return NULL;
217 std::string name1, name2, arg;
219 vector <Variable*>::const_iterator arg_min(a_arguments.begin()), arg_max(a_arguments.end()), arg_it;
222 anna::Tokenizer expression;
223 expression.apply (argumentExpression, ",");
224 if (expression.size() > 2) return NULL;
226 anna::Tokenizer::const_iterator tok_it;
227 for (tok_it = expression.begin(); tok_it != expression.end(); tok_it++) { // one or two
228 arg = anna::Tokenizer::data(tok_it);
229 for(arg_it = arg_min; arg_it != arg_max; arg_it++) {
230 name1 = (*arg_it)->getName1();
231 name2 = (*arg_it)->getName2();
232 if ((name1 != "") && (name1 == arg)) return *arg_it;
233 if ((name2 != "") && (name2 == arg)) return *arg_it;
240 string CommandLine::Variable::getHelpExpression() const
242 std::string long_name, short_name, result = "";
244 int s1 = a_name1.size();
245 int s2 = a_name2.size();
246 if ((s1 + s2) > 2) { // both provided
247 if (s1 > 1) { short_name = a_name2; long_name = a_name1; }
248 else { short_name = a_name1; long_name = a_name2; }
249 if (short_name != "") {
251 result += short_name;
254 if (long_name != "") {
261 if (s1 > 1) result = "-";
268 void CommandLine::printUsage() const
270 int i, maxi(a_arguments.size());
271 cout << "Usage: " << a_argv [0] << " ";
273 for(i = 0; i < maxi; i ++)
274 cout << a_arguments [i]->asString() << " ";
276 cout << endl << "Where: " << endl;
278 for(i = 0; i < maxi; i ++) {
279 cout << " " << a_arguments [i]->getHelpExpression() << ": " << endl;
280 cout << " " << a_arguments [i]->getComment() << endl;
284 string CommandLine::Variable::asString() const
287 result = ((a_type == Argument::Optional) ? "[ " : "");
288 result += getHelpExpression();
289 if(a_needValue == true) result += " <value>"; // result += " <value_"; result += a_name; result += ">";
290 if(a_type == Argument::Optional) result += " ]";
295 string CommandLine::asString() const
297 string result = "Provided command-line parameters:\n\n";
298 vector <Variable*>::const_iterator ii, maxii;
301 for(int pos = 1; pos <= a_positionalArguments; pos++)
302 result += anna::functions::asString("Positional argument [%d]: %s\n", pos, getPositional(pos));
304 for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
305 value = (*ii)->getValue();
308 result += (*ii)->getHelpExpression();
318 xml::Node* CommandLine::asXML(xml::Node* parent) const {
319 xml::Node* result = parent->createChild("CommandLine");
320 vector <Variable*>::const_iterator ii, maxii;
323 for(int pos = 1; pos <= a_positionalArguments; pos++)
324 result->createAttribute(anna::functions::asString("PositionalArgument_%d", pos).c_str(), getPositional(pos));
326 for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
327 value = (*ii)->getValue();
330 result->createAttribute((*ii)->getHelpExpression().c_str(), value);