1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
11 // * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 // * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
17 // * Neither the name of the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
39 #include <anna/config/defines.hpp>
40 #include <anna/core/RuntimeException.hpp>
41 #include <anna/xml/xml.hpp>
43 #include <anna/core/util/CommandLine.hpp>
44 #include <anna/core/util/Tokenizer.hpp>
49 //--------------------------------------------------------------------------------
50 // Notifica al Parser que la opcion 'argumentExpression' es soportada
52 // (1) Verifica que la opcion no haya sido registrada previamente.
53 //--------------------------------------------------------------------------------
54 void CommandLine::add(const char* argumentExpression, Argument::Type type, const char* comment, const bool needValue)
56 Guard guard(a_mutex, "CommandLine::add");
58 std::string arg1 = "", arg2 = "";
59 if (!argumentExpression) { std::cerr << "Invalid argument expression: cannot register NULL literal on command-line" << std::endl; return; }
61 anna::Tokenizer expression;
62 expression.apply (argumentExpression, ",");
63 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; }
65 anna::Tokenizer::const_iterator tok_it = expression.begin();
66 arg1 = anna::Tokenizer::data(tok_it);
67 // No leading hyphens in the name:
68 if (arg1[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
71 if (tok_it != expression.end()) {
72 arg2 = anna::Tokenizer::data(tok_it);
73 // No hyphens in the name:
74 if (arg2[0] == '-') { std::cerr << "Invalid argument expression '" << argumentExpression << "': no leading hyphens allowed in argument names for command-line registration" << std::endl; return; }
77 // swap to have first non empty:
78 if (arg1 == "") { arg1 = arg2; arg2 = ""; }
80 // If both provided, one will be single letter and the other, a word:
84 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; }
85 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; }
89 a_arguments.push_back(new Variable(arg1, arg2, type, comment, needValue));
92 //--------------------------------------------------------------------------------
93 // Verifica que todos los argumentos declarados como obligatorios estan en la
95 //--------------------------------------------------------------------------------
96 void CommandLine::verify()
97 throw(RuntimeException) {
99 throw RuntimeException("CommandLine was not initialized", ANNA_FILE_LOCATION);
101 for(int i = 0, maxi = a_arguments.size(); i < maxi; i ++) {
102 if(a_arguments [i]->getType() == Argument::Mandatory)
103 getValue(a_arguments [i]->getName1().c_str()); // exit program if not found (exitOnFault = true by default)
107 //--------------------------------------------------------------------------------
108 // Obtiene el valor para opcion 'argumentExpression'.
110 // (1) Realiza el analisis de la linea de comandos.
111 // (2) Verifica que la opcion recibida existe en la lista de opciones registradas.
112 // (3) Si la opcion 'argumentExpression' existe en la linea de comandos ...
113 // (3.1) Si el argumentExpression debe ir seguido por un valor/valores y no existe
115 // (3.2) Si el argumentExpression no necesita valor, simplemente, le asigna una cadena
116 // que contiene 'true', en otro caso devolvera NULL.
118 // (4) Si no existe en la linea de comandos y es un argumentExpression obligatorio => error.
120 // Si el arguemento solicitado no esta en la LC y es opcional devolvera NULL.
121 //--------------------------------------------------------------------------------
122 const char* CommandLine::getValue(const char* argumentExpression, const bool exitOnFault)
124 const char* result = NULL;
125 const Variable* argument = NULL;
129 if((analized = analize()) == true) { // (1)
130 if((argument = search(argumentExpression)) != NULL) { // (2)
132 result = argument->getValue();
134 if(argument->getIsOn() == true) { // (3)
135 if(argument->getNeedValue() == true && result == NULL) // (3.1)
137 else if(argument->getNeedValue() == false) {
143 } else if(argument->getType() == Argument::Mandatory) // (4)
148 if(analized == false) {
151 } else if(error == true && exitOnFault == true) {
152 cout << "Variable: '" << argumentExpression << "' is not valid" << endl << endl;
160 // Auxiliary function:
161 // Returns first no-leading hyphen position
162 int removeLeadingHyphens(std::string &argv) throw() {
164 // Move until no hyphen:
165 for (int i=0; i < argv.size(); i++)
166 if (argv[i] != '-') { startPos = i; argv = argv.substr(i); break; }
169 if (startPos == -1) return -1;
173 if (argv.size() != 1) return -1;
177 if (argv.size() == 1) return -1;
180 if (startPos > 2) return -1;
185 //--------------------------------------------------------------------------------
186 // Analize command line to store the arguments
187 //--------------------------------------------------------------------------------
188 bool CommandLine::analize()
195 if(a_wasParsed == true) // already analyzed
198 Guard guard(a_mutex, "CommandLine::analyze");
200 if(a_wasParsed == true)
204 while(i < a_argc && result == true) {
207 if (::removeLeadingHyphens(aux) == -1) {
211 if((variable = const_cast <Variable*>(search(aux.c_str()))) != NULL) {
212 variable->setIsOn(true);
213 if (variable->getNeedValue()) {
215 if (i < a_argc) { variable->setValue(a_argv[i]); }
216 else { result = false ; break; }
220 cout << "Variable: " << aux << " unreconized" << endl;
228 return (a_wasParsed = result);
231 const CommandLine::Variable* CommandLine::search(const char *argumentExpression) const
233 if (!argumentExpression) return NULL;
234 std::string name1, name2, arg;
236 vector <Variable*>::const_iterator arg_min(a_arguments.begin()), arg_max(a_arguments.end()), arg_it;
239 anna::Tokenizer expression;
240 expression.apply (argumentExpression, ",");
241 if (expression.size() > 2) return NULL;
243 anna::Tokenizer::const_iterator tok_it;
244 for (tok_it = expression.begin(); tok_it != expression.end(); tok_it++) { // one or two
245 arg = anna::Tokenizer::data(tok_it);
246 for(arg_it = arg_min; arg_it != arg_max; arg_it++) {
247 name1 = (*arg_it)->getName1();
248 name2 = (*arg_it)->getName2();
249 if ((name1 != "") && (name1 == arg)) return *arg_it;
250 if ((name2 != "") && (name2 == arg)) return *arg_it;
257 string CommandLine::Variable::getHelpExpression() const
259 std::string long_name, short_name, result = "";
261 int s1 = a_name1.size();
262 int s2 = a_name2.size();
263 if ((s1 + s2) > 2) { // both provided
264 if (s1 > 1) { short_name = a_name2; long_name = a_name1; }
265 else { short_name = a_name1; long_name = a_name2; }
266 if (short_name != "") {
268 result += short_name;
271 if (long_name != "") {
278 if (s1 > 1) result = "-";
285 void CommandLine::printUsage() const
287 int i, maxi(a_arguments.size());
288 cout << "Use: " << a_argv [0] << " ";
290 for(i = 0; i < maxi; i ++)
291 cout << a_arguments [i]->asString() << " ";
293 cout << endl << "Where: " << endl;
295 for(i = 0; i < maxi; i ++) {
296 cout << " " << a_arguments [i]->getHelpExpression() << ": " << endl;
297 cout << " " << a_arguments [i]->getComment() << endl;
301 string CommandLine::Variable::asString() const
304 result = ((a_type == Argument::Optional) ? "[ " : "");
305 result += getHelpExpression();
306 if(a_needValue == true) result += " <value>"; // result += " <value_"; result += a_name; result += ">";
307 if(a_type == Argument::Optional) result += " ]";
312 string CommandLine::asString() const
314 string result = "Provided command-line parameters:\n\n";
315 vector <Variable*>::const_iterator ii, maxii;
318 for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
319 value = (*ii)->getValue();
322 result += (*ii)->getHelpExpression();
332 xml::Node* CommandLine::asXML(xml::Node* parent) const throw() {
333 xml::Node* result = parent->createChild("CommandLine");
334 vector <Variable*>::const_iterator ii, maxii;
337 for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
338 value = (*ii)->getValue();
341 result->createAttribute((*ii)->getHelpExpression().c_str(), value);