From 2032c82d206dd40e35d118242272417431094823 Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Sun, 19 Apr 2015 19:03:43 +0200 Subject: [PATCH] GNU-style for command line. No positional arguments accepted. Supported single/double hyphens for letters/words respectively. Added 'dumpLog' in launcher to allow write traffic log in disk. --- example/diameter/launcher/DEPLOY.sh | 2 +- .../launcher/deployments/advanced/loadXml.sh | 2 +- .../launcher/deployments/basic/configure.sh | 16 +- example/diameter/launcher/main.cpp | 47 +++- example/http/rServer/example_args | 1 + example/http/rServer/go | 2 - include/anna/core/util/CommandLine.hpp | 57 ++--- source/core/util/CommandLine.cpp | 226 +++++++++++------- 8 files changed, 225 insertions(+), 128 deletions(-) create mode 100644 example/http/rServer/example_args delete mode 100755 example/http/rServer/go diff --git a/example/diameter/launcher/DEPLOY.sh b/example/diameter/launcher/DEPLOY.sh index 034ccf9..98392ad 100755 --- a/example/diameter/launcher/DEPLOY.sh +++ b/example/diameter/launcher/DEPLOY.sh @@ -58,7 +58,7 @@ STARTED=\`pgrep \$EXE 2>/dev/null\` 0> launcher.trace rm -f counters/* # Execution line: -./\$EXE -cntDir counters $(for i in `cat args.txt | grep -v "^#"`; do echo -n "$i "; done)& +./\$EXE --cntDir counters $(for i in `cat args.txt | grep -v "^#"`; do echo -n "$i "; done)& echo \$! > .pid EOF diff --git a/example/diameter/launcher/deployments/advanced/loadXml.sh b/example/diameter/launcher/deployments/advanced/loadXml.sh index 26749d5..c2ac03f 100755 --- a/example/diameter/launcher/deployments/advanced/loadXml.sh +++ b/example/diameter/launcher/deployments/advanced/loadXml.sh @@ -9,7 +9,7 @@ use () { echo echo "Load xml file and show on stdout again." echo "It is useful to test xml loader and xml viewer, but also for show aliases and," - echo "if '-ignoreFlags' commandline option is missing, for flag coherence checking." + echo "if '--ignoreFlags' commandline option is missing, for flag coherence checking." echo exit } diff --git a/example/diameter/launcher/deployments/basic/configure.sh b/example/diameter/launcher/deployments/basic/configure.sh index c377a13..ac4fa74 100755 --- a/example/diameter/launcher/deployments/basic/configure.sh +++ b/example/diameter/launcher/deployments/basic/configure.sh @@ -69,12 +69,12 @@ tol=$(get_tol $option) [ "$tol" = "" ] && _exit "Option '$option' not implemented !!" # Tracing -TRACING="-cntDir counters" +TRACING="--cntDir counters" echo echo "Enable debug traces ? (y/n) [n]:" read enable [ "$enable" = "" ] && enable=n -[ "$enable" = "y" ] && TRACING="$TRACING -trace debug" +[ "$enable" = "y" ] && TRACING="$TRACING --trace debug" # Kindness KINDNESS= @@ -83,7 +83,7 @@ echo "Strict xml for decoded messages ? (y/n) [y]:" echo " (ignoring flags turns a made-up xml representation; execute './$EXE_BN | grep -A1 ignoreFlags:' for more help)" read strict [ "$strict" = "" ] && strict=y -[ "$strict" = "n" ] && KINDNESS="-ignoreFlags" +[ "$strict" = "n" ] && KINDNESS="--ignoreFlags" if [ "$option" = "s" ] then echo @@ -91,7 +91,7 @@ then echo " (ignoring errors, the process won't answer Failed-AVP automatically; execute './$EXE_BN | grep -A1 ignoreErrors:' for more help)" read i_errors [ "$i_errors" = "" ] && i_errors=n - [ "$i_errors" = "y" ] && KINDNESS="$KINDNESS -ignoreErrors" + [ "$i_errors" = "y" ] && KINDNESS="$KINDNESS --ignoreErrors" fi # Run script: @@ -102,22 +102,22 @@ case $tol in client) ENTITY=$LOCAL_STANDARD_ENDPOINT - createRunScript $EXE_LINK -dictionary $DICTIONARY -entity $ENTITY -entityServerSessions $CONNS -diameterServerSessions 0 $KINDNESS $TRACING & + createRunScript $EXE_LINK --dictionary $DICTIONARY --entity $ENTITY --entityServerSessions $CONNS --diameterServerSessions 0 $KINDNESS $TRACING & ;; server) SERVER=$LOCAL_STANDARD_ENDPOINT - createRunScript $EXE_LINK -dictionary $DICTIONARY -diameterServer $SERVER -diameterServerSessions $CONNS -entityServerSessions 0 $KINDNESS $TRACING & + createRunScript $EXE_LINK --dictionary $DICTIONARY --diameterServer $SERVER --diameterServerSessions $CONNS --entityServerSessions 0 $KINDNESS $TRACING & ;; balancer) SERVER=$LOCAL_STANDARD_ENDPOINT ENTITY=$EXAMPLE_ENTITY_4_BALANCER - createRunScript $EXE_LINK -dictionary $DICTIONARY -entity $ENTITY -entityServerSessions $CONNS -diameterServer $SERVER -diameterServerSessions $CONNS -balance $KINDNESS $TRACING & + createRunScript $EXE_LINK --dictionary $DICTIONARY --entity $ENTITY --entityServerSessions $CONNS --diameterServer $SERVER --diameterServerSessions $CONNS --balance $KINDNESS $TRACING & ;; dummy) - createRunScript $EXE_LINK -dictionary $DICTIONARY -entityServerSessions 0 -diameterServerSessions 0 $KINDNESS $TRACING & + createRunScript $EXE_LINK --dictionary $DICTIONARY --entityServerSessions 0 --diameterServerSessions 0 $KINDNESS $TRACING & ;; esac diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index ab5ea0a..49612e9 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -119,7 +119,7 @@ typedef std::map < int /* message code */, codec_messages_deque* >::const_iterat ProgrammedAnswers() { a_rotate = false; } ~ProgrammedAnswers() { clear(); } - bool rotate() const const throw() { return a_rotate; } + bool rotate() const throw() { return a_rotate; } void rotate(bool r) throw() { a_rotate = r; } void clear () throw() { @@ -397,7 +397,7 @@ class Launcher : public anna::comm::Application { anna::diameter::comm::Entity *a_entity; std::string a_logFile, a_burstLogFile; std::ofstream a_burstLogStream; - bool a_splitLog, a_detailedLog; + bool a_splitLog, a_detailedLog, a_dumpLog; anna::time::Date a_start_time; anna::timex::Engine* a_timeEngine; MyCounterRecorder *a_counterRecorder; @@ -866,10 +866,10 @@ std::string Launcher::help() const throw() { result += "\nsendxml| Same as 'sendxml2e'."; result += "\nanswerxml2e|[source_file] Answer xml source file (pathfile) for incoming request with same code from entity."; result += "\n The answer is stored in a FIFO queue for a specific message code, then there are"; - result += "\n as many queues as different message codes have been received."; + result += "\n as many queues as different message codes have been programmed."; result += "\nanswerxml2c|[source_file] Answer xml source file (pathfile) for incoming request with same code from client."; result += "\n The answer is stored in a FIFO queue for a specific message code, then there are"; - result += "\n as many queues as different message codes have been received."; + result += "\n as many queues as different message codes have been programmed."; result += "\nanswerxml|[source_file] Same as 'answerxml2c'."; result += "\nanswerxml(2e/2c) List programmed answers (to entity/client) if no parameter provided."; result += "\nanswerxml(2e/2c)|dump Write programmed answers (to entity/client) to file 'programmed_answer..',"; @@ -1067,10 +1067,27 @@ int main(int argc, const char** argv) { try { CommandLine& commandLine(anna::CommandLine::instantiate()); // General + commandLine.add(NULL, anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("juan,pepe,maria", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("dos,palabras", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("x,y", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("-x", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("-ooox", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("--ooox", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("--x", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("x,-y", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("x,-lly", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("bueno,a-medias", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("bueno,en-te-ro", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + commandLine.add("b,a-ho-ra-si", anna::CommandLine::Argument::Optional, "XXXXXXXXXXXXXXXXXXXXX"); + + + commandLine.add("trace", anna::CommandLine::Argument::Optional, "Trace level (emergency, alert, critical, error, warning, notice, information, debug, local0..local7)"); commandLine.add("log", anna::CommandLine::Argument::Optional, "Process log file (operations result, traffic log, etc.). By default 'launcher.log'. Empty string or \"null\" name, to disable. Warning: there is no rotation for log files (use logrotate or whatever)"); commandLine.add("splitLog", anna::CommandLine::Argument::Optional, "Splits log file (appends to log filename, extensions with the type of event: see help on startup information-level traces). No log files for code/decode and load operations are created", false); commandLine.add("detailedLog", anna::CommandLine::Argument::Optional, "Insert detailed information at log files. Should be disabled on automatic tests. Useful on '-balance' mode to know messages flow along the sockets", false); + commandLine.add("dumpLog", anna::CommandLine::Argument::Optional, "Write to disk every incoming/outcoming message named as '....xml'", false); commandLine.add("logStatisticSamples", anna::CommandLine::Argument::Optional, "Log statistics samples for the provided concept id list, over './sample..csv' files. For example: \"1,2\" will log concepts 1 and 2. Reserved word \"all\" activates all registered statistics concept identifiers. That ids are shown at context dump (see help to get it)."); commandLine.add("burstLog", anna::CommandLine::Argument::Optional, "Burst operations log file. By default 'launcher.burst'. Empty string or \"null\" name, to disable. Warning: there is no rotation for log files (use logrotate or whatever). Output: dot (.) for each burst message sent/pushed, cross (x) for popped ones, and order number when multiple of 1% of burst list size, plus OTA requests when changed."); commandLine.add("cntDir", anna::CommandLine::Argument::Optional, "Counters directory. By default is the current execution directory. Warning: a counter file will be dump per record period; take care about the possible accumulation of files"); @@ -1119,6 +1136,7 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", " a_burstLogFile = "launcher.burst"; a_splitLog = false; a_detailedLog = false; + a_dumpLog = false; a_timeEngine = NULL; a_counterRecorder = NULL; a_counterRecorderClock = NULL; @@ -1295,6 +1313,8 @@ void Launcher::writeLogFile(const anna::diameter::codec::Message & decodedMessag title += "]"; // Build complete log: std::string log = "\n"; + std::string xml = decodedMessage.asXMLString(); + if(a_detailedLog) { anna::time::Date now; @@ -1302,7 +1322,7 @@ void Launcher::writeLogFile(const anna::diameter::codec::Message & decodedMessag title += " "; title += now.asString(); log += anna::functions::highlight(title, anna::functions::TextHighlightMode::OverAndUnderline); - log += decodedMessage.asXMLString(); + log += xml; log += "\n"; log += anna::functions::highlight("Used resource"); log += detail; @@ -1310,10 +1330,23 @@ void Launcher::writeLogFile(const anna::diameter::codec::Message & decodedMessag } else { log += title; log += "\n"; - log += decodedMessage.asXMLString(); + log += xml; log += "\n"; } + if(a_dumpLog) { + std::string name = anna::functions::asString(decodedMessage.getHopByHop()); + name += "."; + name += anna::functions::asString(decodedMessage.getEndToEnd()); + name += "."; + name += anna::functions::asString(decodedMessage.getId().first); + name += "."; + name += ((decodedMessage.getId().second) ? "request.xml":"answer.xml"); + ofstream outMsg(name.c_str(), ifstream::out | ifstream::app); + outMsg.write(xml.c_str(), xml.size()); + outMsg.close(); + } + // Write and close out.write(log.c_str(), log.size()); out.close(); @@ -1677,6 +1710,8 @@ throw(anna::RuntimeException) { if(cl.exists("detailedLog")) a_detailedLog = true; + if(cl.exists("dumpLog")) a_dumpLog = true; + if(cl.exists("burstLog")) a_burstLogFile = cl.getValue("burstLog"); // Log statistics concepts diff --git a/example/http/rServer/example_args b/example/http/rServer/example_args new file mode 100644 index 0000000..9ee9013 --- /dev/null +++ b/example/http/rServer/example_args @@ -0,0 +1 @@ +-a 127.0.0.1 -p 2000 --limit 60 -n 500 -d 0 --trace notice --timeout 15000 --quota 512 & diff --git a/example/http/rServer/go b/example/http/rServer/go deleted file mode 100755 index c418706..0000000 --- a/example/http/rServer/go +++ /dev/null @@ -1,2 +0,0 @@ -rm *.trace *.old -../$GMAKE_TARGET_DIR/http_rserver -a 127.0.0.1 -p 2000 -limit 60 -n 500 -d 0 -trace notice -timeout 15000 -quota 512 & diff --git a/include/anna/core/util/CommandLine.hpp b/include/anna/core/util/CommandLine.hpp index 6d2569a..a869f4e 100644 --- a/include/anna/core/util/CommandLine.hpp +++ b/include/anna/core/util/CommandLine.hpp @@ -54,11 +54,15 @@ class Node; /** - Facilita la recogida de parametros desde la linea de comandos. Tambien - verifica que todos los parametros necesarios han sido indicados por el - usuario. + Command line parser helper for our application. It's close to GNU-style, supporting + single letter (single hyphen) and long argument names (double hyphen). No bare hyphen + or double-dash end of parsing separator are supported. No positional arguments are supported. */ class CommandLine : public Singleton { + + /* returns first no-leading hyphen position; -1 is error */ + static int removeLeadingHyphens(std::string &argv) throw(); + public: /** Define los tipos de argumento @@ -98,18 +102,16 @@ public: } /** - Registra un argumentName que sera recocido por nuestra aplicacion. - - Se pueden indicar tantos argumentNames como sea necesario. - - @param argumentName Nombre del argumento. - @param type Tipo de argumento. Ver Variable#Type. - @param comment Explicacion acerca de cometido de este argumento. - @param needValue Indica si el parametro que estamos definido debe tener un - valor adicional. Por ejemplo un parametro "usuario" deberia tener un valor adicional - que sea el valor que toma. + Register an argument name in our application + + @param argumentExpression Argument name, or comma-separated set with both short and long argument names. For example 'v,version', 'h,help', or simply 'f' or 'file'. If both, + provided, one of them shall be a single letter and the other will be a word. In other case, nothing will be registered. Command line arguments stands for - + and -- when proceed. If NULL provided, nothing is done. + @param type Argument type. See Variable#Type. + @param comment Argument explanation. + @param needValue If our argument has an additional associated value, this will be true. False in other case (flags). */ - void add(const char* argumentName, Argument::Type type, const char* comment, const bool needValue = true) throw(); + void add(const char* argumentExpression, Argument::Type type, const char* comment, const bool needValue = true) throw(); /** Obtiene el valor asociado al argumento recibido como parametro. @@ -118,34 +120,34 @@ public: Si el argumento es obligatorio y no este en la linea de comandos o no tiene valor asociado la ejecucion del programa TERMINA inmediatamente. - @param argumentName Nombre del argumento del que deseamos obtener el valor. + @param argumentExpression You should look for the registered expression (#add), internally tokenized if needed. @param exitOnFault Indica el funcionamiento del metodo en caso de que el argumento solicitado no halla sido indicado. Si el parametro no existe si vale @em true la aplicacion terminara, si vale @em false devolvera NULL. @return Valor asociadoal argumento recibido como parametro. Puede ser NULL. */ - const char* getValue(const char* argumentName, const bool exitOnFault = true) throw(); + const char* getValue(const char* argumentExpression, const bool exitOnFault = true) throw(); /** Obtiene el valor asociado al argumento recibido, pero convirtiendo a numero el valor obtenido por #getValue. - @param argumentName Nombre del argumento del que deseamos obtener el valor. + @param argumentExpression You should look for the registered expression (#add), internally tokenized if needed. @return Valor numerico del valor devuelto por #getValue. */ - int getIntegerValue(const char* argumentName) throw() { return atoi(getValue(argumentName)); } + int getIntegerValue(const char* argumentExpression) throw() { return atoi(getValue(argumentExpression)); } /** Comprueba si el argumento recibido como parametro estña presente en la linea de comandos. - @param argumentName Nombre del argumento del que deseamos obtener el valor. + @param argumentExpression You should look for the registered expression (#add), internally tokenized if needed. @return true si el argumento esta en la linea de comandos y false en otro caso. */ - bool exists(const char* argumentName) throw() { return (getValue(argumentName, false) != NULL) ? true : false; } + bool exists(const char* argumentExpression) throw() { return (getValue(argumentExpression, false) != NULL) ? true : false; } /** Comprueba la linea de comandos del programa para verificar que coincide con los argumentos @@ -182,15 +184,16 @@ private: class Variable { public: - // Constructores - Variable(const char* name, const Argument::Type type, const char* comment, const bool needValue = true) : - a_name(name), a_type(type), a_comment(comment), a_needValue(needValue), - a_isOn(false), a_value(NULL) { + // Constructors + Variable(const std::string &name1, const std::string &name2, const Argument::Type type, const char* comment, const bool needValue = true) : + a_name1(name1), a_name2(name2), a_type(type), a_comment(comment), a_needValue(needValue), a_isOn(false), a_value(NULL) { } virtual ~Variable() { if(a_value) free(a_value); } // Accesores - const std::string& getName() const throw() { return a_name; } + const std::string& getName1() const throw() { return a_name1; } + const std::string& getName2() const throw() { return a_name2; } + std::string getHelpExpression() const throw(); const char* getValue() const throw() { return a_value; } const char* getComment() const throw() { return a_comment; } bool getNeedValue() const throw() { return a_needValue; } @@ -205,7 +208,7 @@ private: std::string asString() const throw(); protected: - std::string a_name; + std::string a_name1, a_name2; const char* a_comment; char* a_value; Argument::Type a_type; @@ -221,7 +224,7 @@ private: CommandLine() : a_argv(NULL), a_argc(0) {;} bool analize() throw(); - const Variable* search(const char *argumentName) const throw(); + const Variable* search(const char *argumentExpression) const throw(); void printUsage() const throw(); friend class Singleton ; diff --git a/source/core/util/CommandLine.cpp b/source/core/util/CommandLine.cpp index 2764e45..74c34e1 100644 --- a/source/core/util/CommandLine.cpp +++ b/source/core/util/CommandLine.cpp @@ -41,29 +41,56 @@ #include #include +#include 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 '[,]' or '[,]' 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 '[,]' or '[,]' are supported for command-line registration (provided two letters !)" << std::endl; return; } + if ((s1 != 1) && (s2 != 1)) { std::cerr << "Invalid argument expression '" << argumentExpression << "': only '[,]' or '[,]' are supported for command-line registration (provided two words !)" << std::endl; return; } + } + + // assign: + a_arguments.push_back(new Variable(arg1, arg2, type, comment, needValue)); } //-------------------------------------------------------------------------------- -// 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() @@ -73,26 +100,26 @@ throw(RuntimeException) { 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; @@ -100,7 +127,7 @@ throw() { bool analized; if((analized = analize()) == true) { // (1) - if((argument = search(argumentName)) != NULL) { // (2) + if((argument = search(argumentExpression)) != NULL) { // (2) error = false; result = argument->getValue(); @@ -122,7 +149,7 @@ throw() { 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); } @@ -130,84 +157,127 @@ throw() { 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; 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 (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 (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 ::const_iterator ii, maxii; + if (!argumentExpression) return NULL; + std::string name1, name2, arg; + + vector ::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; } @@ -223,31 +293,22 @@ throw() { 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 += " getValue(); if(value) { - result += (*ii)->getName(); + result += (*ii)->getHelpExpression(); result += ": "; result += value; result += "\n"; @@ -277,10 +338,9 @@ xml::Node* CommandLine::asXML(xml::Node* parent) const throw() { value = (*ii)->getValue(); if(value) - result->createAttribute((*ii)->getName().c_str(), value); + result->createAttribute((*ii)->getHelpExpression().c_str(), value); } return result; } - -- 2.20.1