-// ANNA - Anna is Not Nothingness Anymore
-//
-// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
-//
-// http://redmine.teslayout.com/projects/anna-suite
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Authors: eduardo.ramos.testillano@gmail.com
-// cisco.tierra@gmail.com
+// ANNA - Anna is Not Nothingness Anymore //
+// //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
+// //
+// See project site at http://redmine.teslayout.com/projects/anna-suite //
+// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
#include <iostream>
#include <anna/xml/xml.hpp>
#include <anna/core/util/CommandLine.hpp>
+#include <anna/core/util/Tokenizer.hpp>
using namespace std;
using namespace anna;
-#ifndef TOKENINDICATOR
-#define TOKENINDICATOR '-'
-#endif
-
//--------------------------------------------------------------------------------
-// Notifica al Parser que la opcion 'argumentName' es soportada
+// Notifica al Parser que la opcion 'argumentExpression' es soportada
//
// (1) Verifica que la opcion no haya sido registrada previamente.
//--------------------------------------------------------------------------------
-void CommandLine::add(const char* argumentName, Argument::Type type, const char* comment, const bool needValue)
+void CommandLine::add(const char* argumentExpression, Argument::Type type, const char* comment, const bool needValue)
throw() {
Guard guard(a_mutex, "CommandLine::add");
- if(search(argumentName) == NULL)
- a_arguments.push_back(new Variable(argumentName, type, comment, needValue));
+ std::string arg1 = "", arg2 = "";
+ if (!argumentExpression) { std::cerr << "Invalid argument expression: cannot register NULL literal on command-line" << std::endl; return; }
+
+ anna::Tokenizer expression;
+ expression.apply (argumentExpression, ",");
+ 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; }
+
+ anna::Tokenizer::const_iterator tok_it = expression.begin();
+ arg1 = anna::Tokenizer::data(tok_it);
+ // No leading hyphens in the name:
+ if (arg1[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
+
+ tok_it++;
+ if (tok_it != expression.end()) {
+ arg2 = anna::Tokenizer::data(tok_it);
+ // No hyphens in the name:
+ if (arg2[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
+ }
+
+ // swap to have first non empty:
+ if (arg1 == "") { arg1 = arg2; arg2 = ""; }
+
+ // If both provided, one will be single letter and the other, a word:
+ if (arg2 != "") {
+ int s1 = arg1.size();
+ int s2 = arg2.size();
+ 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; }
+ 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; }
+ }
+
+ // assign:
+ a_arguments.push_back(new Variable(arg1, arg2, type, comment, needValue));
+}
+
+void CommandLine::initialize(const char** argv, const int argc, int positionalArguments)
+throw(RuntimeException) {
+ if (argc < 1) throw RuntimeException("Provided argc < 1 as command-line argv size !", ANNA_FILE_LOCATION);
+ if (positionalArguments < 0) throw RuntimeException("Provided negative number of positional arguments as command-line initializer", ANNA_FILE_LOCATION);
+ if (positionalArguments > (argc-1)) throw RuntimeException("Provided positional arguments > (argc - 1) as command-line initializer", ANNA_FILE_LOCATION);
+ a_positionalArguments = positionalArguments;
+ a_argv = argv;
+ a_argc = argc;
+ a_wasParsed = false;
}
//--------------------------------------------------------------------------------
-// Verifica que todos los argumentNameos declarados como obligatorios estan en la
+// Verifica que todos los argumentos declarados como obligatorios estan en la
// linea de comandos.
//--------------------------------------------------------------------------------
void CommandLine::verify()
for(int i = 0, maxi = a_arguments.size(); i < maxi; i ++) {
if(a_arguments [i]->getType() == Argument::Mandatory)
- getValue(a_arguments [i]->getName().c_str()); // JEDS 24/09/2003
+ getValue(a_arguments [i]->getName1().c_str()); // exit program if not found (exitOnFault = true by default)
}
}
//--------------------------------------------------------------------------------
-// Obtiene el valor para opcion 'argumentName'.
+// Obtiene el valor para opcion 'argumentExpression'.
//
// (1) Realiza el analisis de la linea de comandos.
// (2) Verifica que la opcion recibida existe en la lista de opciones registradas.
-// (3) Si la opcion 'argumentName' existe en la linea de comandos ...
-// (3.1) Si el argumentNameo debe ir seguido por un valor/valores y no existe
+// (3) Si la opcion 'argumentExpression' existe en la linea de comandos ...
+// (3.1) Si el argumentExpression debe ir seguido por un valor/valores y no existe
// ninguno => error.
-// (3.2) Si el argumentNameo no necesita valor, simplemente, le asigna una cadena
+// (3.2) Si el argumentExpression no necesita valor, simplemente, le asigna una cadena
// que contiene 'true', en otro caso devolvera NULL.
//
-// (4) Si no existe en la linea de comandos y es un argumentNameo obligatorio => error.
+// (4) Si no existe en la linea de comandos y es un argumentExpression obligatorio => error.
//
// Si el arguemento solicitado no esta en la LC y es opcional devolvera NULL.
//--------------------------------------------------------------------------------
-const char* CommandLine::getValue(const char* argumentName, const bool exitOnFault)
+const char* CommandLine::getValue(const char* argumentExpression, const bool exitOnFault)
throw() {
const char* result = NULL;
const Variable* argument = NULL;
bool analized;
if((analized = analize()) == true) { // (1)
- if((argument = search(argumentName)) != NULL) { // (2)
+ if((argument = search(argumentExpression)) != NULL) { // (2)
error = false;
result = argument->getValue();
printUsage();
exit(-1);
} else if(error == true && exitOnFault == true) {
- cout << "Variable: '" << argumentName << "' is not valid" << endl << endl;
+ cout << "Variable: '" << argumentExpression << "' is not valid" << endl << endl;
printUsage();
exit(-1);
}
return result;
}
+// Auxiliary function:
+// Returns first no-leading hyphen position
+int removeLeadingHyphens(std::string &argv) throw() {
+ int startPos = -1;
+ // Move until no hyphen:
+ for (int i=0; i < argv.size(); i++)
+ if (argv[i] != '-') { startPos = i; argv = argv.substr(i); break; }
+
+ // No word found:
+ if (startPos == -1) return -1;
+
+ // one hyphen:
+ if (startPos == 1)
+ if (argv.size() != 1) return -1;
+
+ // two hyphens:
+ if (startPos == 2)
+ if (argv.size() == 1) return -1;
+
+ // more than two !
+ if (startPos > 2) return -1;
+
+ return startPos;
+}
+
//--------------------------------------------------------------------------------
-// Analiza la linea de comandos para completar la informacion de los argumentNameos.
-//
-// (1) Comprueba que el analisis no se ha completado con existo previamente.
-// (2) El primer argumentNameo despues del nombre del ejecutable debe comenzar por
-// el caracter de indicador de argumentNameo.
-// (3) Comprueba si el argumentNameo de la LC esta registrado como posible argumentNameo.
-// (4) Recoge todo lo que haya entre el argumentNameo actual y el siguiente argumentNameo
-// (si lo hay) y todo esto lo asigna como valor del argumentNameo actual.
-// (5) Quita el ltimo car�ter
+// Analize command line to store the arguments
//--------------------------------------------------------------------------------
bool CommandLine::analize()
throw() {
- Variable* argument;
+ Variable* variable;
bool result = true;
- int i = 1;
+ int i = a_positionalArguments + 1;
string aux;
- if(a_wasParsed == true)
+ if(a_wasParsed == true) // already analyzed
return true;
- Guard guard(a_mutex, "CommandLine::analize");
+ Guard guard(a_mutex, "CommandLine::analyze");
if(a_wasParsed == true)
return true;
+ // Control state:
while(i < a_argc && result == true) {
- if(i == 1 && a_argv [1][0] != TOKENINDICATOR) { // (2)
+
+ aux = a_argv[i];
+ if (::removeLeadingHyphens(aux) == -1) {
result = false;
break;
}
-
- if((argument = const_cast <Variable*>(search(a_argv [i]))) != NULL) { // (3)
- aux = "";
- argument->setIsOn(true);
- // @Eduardo (allow dashes on values)
-// while (++ i < a_argc && a_argv [i][0] != TOKENINDICATOR) { // (4)
-// aux += a_argv [i];
-// aux += " ";
-// }
- bool notArgument = true;
-
- while(notArgument && (++ i < a_argc)) { // (4)
- if(a_argv [i][0] == TOKENINDICATOR)
- notArgument = (search(a_argv[i]) == NULL);
-
- if(notArgument) {
- aux += a_argv [i];
- aux += " ";
- }
+ if((variable = const_cast <Variable*>(search(aux.c_str()))) != NULL) {
+ variable->setIsOn(true);
+ if (variable->getNeedValue()) {
+ i++;
+ if (i < a_argc) { variable->setValue(a_argv[i]); }
+ else { result = false ; break; }
}
-
- if(aux.length() > 0) {
- aux.erase(aux.length() - 1, 1); // 5)
- argument->setValue(aux.c_str()); // JEDS 24/09/2003
- }
- } else {
- cout << "Variable: " << a_argv [i] << " unreconized" << endl;
+ }
+ else {
+ cout << "Variable: " << aux << " unreconized" << endl;
result = false;
+ break;
}
- }
+
+ i++;
+ } // while
return (a_wasParsed = result);
}
-const CommandLine::Variable* CommandLine::search(const char *argumentName) const
+const CommandLine::Variable* CommandLine::search(const char *argumentExpression) const
throw() {
- const Variable* result = NULL;
- vector <Variable*>::const_iterator ii, maxii;
+ if (!argumentExpression) return NULL;
+ std::string name1, name2, arg;
+
+ vector <Variable*>::const_iterator arg_min(a_arguments.begin()), arg_max(a_arguments.end()), arg_it;
+
+ // Tokenize:
+ anna::Tokenizer expression;
+ expression.apply (argumentExpression, ",");
+ if (expression.size() > 2) return NULL;
+
+ anna::Tokenizer::const_iterator tok_it;
+ for (tok_it = expression.begin(); tok_it != expression.end(); tok_it++) { // one or two
+ arg = anna::Tokenizer::data(tok_it);
+ for(arg_it = arg_min; arg_it != arg_max; arg_it++) {
+ name1 = (*arg_it)->getName1();
+ name2 = (*arg_it)->getName2();
+ if ((name1 != "") && (name1 == arg)) return *arg_it;
+ if ((name2 != "") && (name2 == arg)) return *arg_it;
+ }
+ }
- if(*argumentName == TOKENINDICATOR) argumentName ++;
+ return NULL;
+}
- for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
- if(anna_strcmp((*ii)->getName().c_str(), argumentName) == 0) {
- result = *ii;
- break;
+string CommandLine::Variable::getHelpExpression() const
+throw() {
+ std::string long_name, short_name, result = "";
+
+ int s1 = a_name1.size();
+ int s2 = a_name2.size();
+ if ((s1 + s2) > 2) { // both provided
+ if (s1 > 1) { short_name = a_name2; long_name = a_name1; }
+ else { short_name = a_name1; long_name = a_name2; }
+ if (short_name != "") {
+ result += "-";
+ result += short_name;
+ result += "|";
+ }
+ if (long_name != "") {
+ result += "--";
+ result += long_name;
}
}
+ else {
+ result = "-";
+ if (s1 > 1) result = "-";
+ result += a_name1;
+ }
return result;
}
void CommandLine::printUsage() const
throw() {
int i, maxi(a_arguments.size());
- cout << "Use: " << a_argv [0] << " ";
+ cout << "Usage: " << a_argv [0] << " ";
for(i = 0; i < maxi; i ++)
cout << a_arguments [i]->asString() << " ";
cout << endl << "Where: " << endl;
for(i = 0; i < maxi; i ++) {
- cout << " " << a_arguments [i]->getName() << ": " << endl;
+ cout << " " << a_arguments [i]->getHelpExpression() << ": " << endl;
cout << " " << a_arguments [i]->getComment() << endl;
}
}
-
+
string CommandLine::Variable::asString() const
throw() {
string result;
result = ((a_type == Argument::Optional) ? "[ " : "");
- result += TOKENINDICATOR;
- result += a_name;
-
- if(a_needValue == true) {
- result += " <value_";
- result += a_name;
- result += ">";
- }
-
- if(a_type == Argument::Optional)
- result += " ]";
+ result += getHelpExpression();
+ if(a_needValue == true) result += " <value>"; // result += " <value_"; result += a_name; result += ">";
+ if(a_type == Argument::Optional) result += " ]";
return result;
}
-
string CommandLine::asString() const
throw() {
string result = "Provided command-line parameters:\n\n";
vector <Variable*>::const_iterator ii, maxii;
const char *value;
+ for(int pos = 1; pos <= a_positionalArguments; pos++)
+ result += anna::functions::asString("Positional argument [%d]: %s\n", pos, getPositional(pos));
+
for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
value = (*ii)->getValue();
if(value) {
- result += (*ii)->getName();
+ result += (*ii)->getHelpExpression();
result += ": ";
result += value;
result += "\n";
vector <Variable*>::const_iterator ii, maxii;
const char *value;
+ for(int pos = 1; pos <= a_positionalArguments; pos++)
+ result->createAttribute(anna::functions::asString("PositionalArgument_%d", pos).c_str(), getPositional(pos));
+
for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
value = (*ii)->getValue();
if(value)
- result->createAttribute((*ii)->getName().c_str(), value);
+ result->createAttribute((*ii)->getHelpExpression().c_str(), value);
}
return result;
}
-