GNU-style for command line. No positional arguments accepted. Supported single/double...
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Sun, 19 Apr 2015 17:03:43 +0000 (19:03 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Sun, 19 Apr 2015 17:03:43 +0000 (19:03 +0200)
example/diameter/launcher/DEPLOY.sh
example/diameter/launcher/deployments/advanced/loadXml.sh
example/diameter/launcher/deployments/basic/configure.sh
example/diameter/launcher/main.cpp
example/http/rServer/example_args [new file with mode: 0644]
example/http/rServer/go [deleted file]
include/anna/core/util/CommandLine.hpp
source/core/util/CommandLine.cpp

index 034ccf9..98392ad 100755 (executable)
@@ -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
 
index 26749d5..c2ac03f 100755 (executable)
@@ -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
 }
index c377a13..ac4fa74 100755 (executable)
@@ -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
index ab5ea0a..49612e9 100644 (file)
@@ -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|<source_file>      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.<message code>.<sequence>',";
@@ -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 '<hop by hop>.<end to end>.<message code>.<request|answer>.xml'", false);
     commandLine.add("logStatisticSamples", anna::CommandLine::Argument::Optional, "Log statistics samples for the provided concept id list, over './sample.<concept id>.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 (file)
index 0000000..9ee9013
--- /dev/null
@@ -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 (executable)
index c418706..0000000
+++ /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 &
index 6d2569a..a869f4e 100644 (file)
@@ -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 <CommandLine> {
+
+  /* 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 -<single letter option>
+     and --<word option> 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 <CommandLine>;
index 2764e45..74c34e1 100644 (file)
 #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));
 }
 
 //--------------------------------------------------------------------------------
-// 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 <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;
 }
@@ -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 += " <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";
@@ -258,7 +319,7 @@ throw() {
     value = (*ii)->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;
 }
 
-