Updated license
[anna.git] / source / core / util / CommandLine.cpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
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
16 // distribution.
17 //     * Neither the name of Google Inc. 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.
20 //
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.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #include <iostream>
38
39 #include <anna/config/defines.hpp>
40 #include <anna/core/RuntimeException.hpp>
41 #include <anna/xml/xml.hpp>
42
43 #include <anna/core/util/CommandLine.hpp>
44
45 using namespace std;
46 using namespace anna;
47
48 #ifndef TOKENINDICATOR
49 #define TOKENINDICATOR '-'
50 #endif
51
52 //--------------------------------------------------------------------------------
53 // Notifica al Parser que la opcion 'argumentName' es soportada
54 //
55 // (1) Verifica que la opcion no haya sido registrada previamente.
56 //--------------------------------------------------------------------------------
57 void CommandLine::add(const char* argumentName, Argument::Type type, const char* comment, const bool needValue)
58 throw() {
59   Guard guard(a_mutex, "CommandLine::add");
60
61   if(search(argumentName) == NULL)
62     a_arguments.push_back(new Variable(argumentName, type, comment, needValue));
63 }
64
65 //--------------------------------------------------------------------------------
66 // Verifica que todos los argumentNameos declarados como obligatorios estan en la
67 // linea de comandos.
68 //--------------------------------------------------------------------------------
69 void CommandLine::verify()
70 throw(RuntimeException) {
71   if(a_argv == NULL)
72     throw RuntimeException("CommandLine was not initialized", ANNA_FILE_LOCATION);
73
74   for(int i = 0, maxi = a_arguments.size(); i < maxi; i ++) {
75     if(a_arguments [i]->getType() == Argument::Mandatory)
76       getValue(a_arguments [i]->getName().c_str());     // JEDS 24/09/2003
77   }
78 }
79
80 //--------------------------------------------------------------------------------
81 // Obtiene el valor para opcion 'argumentName'.
82 //
83 // (1) Realiza el analisis de la linea de comandos.
84 // (2) Verifica que la opcion recibida existe en la lista de opciones registradas.
85 // (3) Si la opcion 'argumentName' existe en la linea de comandos ...
86 //     (3.1) Si el argumentNameo debe ir seguido por un valor/valores y no existe
87 //           ninguno => error.
88 //     (3.2) Si el argumentNameo no necesita valor, simplemente, le asigna una cadena
89 //           que contiene 'true', en otro caso devolvera NULL.
90 //
91 // (4) Si no existe en la linea de comandos y es un argumentNameo obligatorio => error.
92 //
93 // Si el arguemento solicitado no esta en la LC y es opcional devolvera NULL.
94 //--------------------------------------------------------------------------------
95 const char* CommandLine::getValue(const char* argumentName, const bool exitOnFault)
96 throw() {
97   const char* result = NULL;
98   const Variable* argument = NULL;
99   bool error = true;
100   bool analized;
101
102   if((analized = analize()) == true) {     // (1)
103     if((argument = search(argumentName)) != NULL) {     // (2)
104       error = false;
105       result = argument->getValue();
106
107       if(argument->getIsOn() == true) {   // (3)
108         if(argument->getNeedValue() == true && result == NULL)   // (3.1)
109           error = true;
110         else if(argument->getNeedValue() == false) {
111           if(result != NULL)
112             error = true;
113           else
114             result = "true";
115         }
116       } else if(argument->getType() == Argument::Mandatory)  // (4)
117         error = true;
118     }
119   }
120
121   if(analized == false) {
122     printUsage();
123     exit(-1);
124   } else if(error == true && exitOnFault == true) {
125     cout << "Variable: '" << argumentName << "' is not valid" << endl << endl;
126     printUsage();
127     exit(-1);
128   }
129
130   return result;
131 }
132
133 //--------------------------------------------------------------------------------
134 // Analiza la linea de comandos para completar la informacion de los argumentNameos.
135 //
136 // (1) Comprueba que el analisis no se ha completado con existo previamente.
137 // (2) El primer argumentNameo despues del nombre del ejecutable debe comenzar por
138 //     el caracter de indicador de argumentNameo.
139 // (3) Comprueba si el argumentNameo de la LC esta registrado como posible argumentNameo.
140 // (4) Recoge todo lo que haya entre el argumentNameo actual y el siguiente argumentNameo
141 //     (si lo hay) y todo esto lo asigna como valor del argumentNameo actual.
142 // (5) Quita el ltimo car�ter
143 //--------------------------------------------------------------------------------
144 bool CommandLine::analize()
145 throw() {
146   Variable* argument;
147   bool result = true;
148   int i = 1;
149   string aux;
150
151   if(a_wasParsed == true)
152     return true;
153
154   Guard guard(a_mutex, "CommandLine::analize");
155
156   if(a_wasParsed == true)
157     return true;
158
159   while(i < a_argc && result == true) {
160     if(i == 1 && a_argv [1][0] != TOKENINDICATOR) {  // (2)
161       result = false;
162       break;
163     }
164
165     if((argument = const_cast <Variable*>(search(a_argv [i]))) != NULL) {    // (3)
166       aux = "";
167       argument->setIsOn(true);
168       // @Eduardo (allow dashes on values)
169 //         while (++ i < a_argc && a_argv [i][0] != TOKENINDICATOR) { // (4)
170 //            aux += a_argv [i];
171 //            aux += " ";
172 //         }
173       bool notArgument = true;
174
175       while(notArgument && (++ i < a_argc)) {  // (4)
176         if(a_argv [i][0] == TOKENINDICATOR)
177           notArgument = (search(a_argv[i]) == NULL);
178
179         if(notArgument) {
180           aux += a_argv [i];
181           aux += " ";
182         }
183       }
184
185       if(aux.length() > 0) {
186         aux.erase(aux.length() - 1, 1);    // 5)
187         argument->setValue(aux.c_str());    // JEDS 24/09/2003
188       }
189     } else {
190       cout << "Variable: " << a_argv [i] << " unreconized" << endl;
191       result = false;
192     }
193   }
194
195   return (a_wasParsed = result);
196 }
197
198 const CommandLine::Variable* CommandLine::search(const char *argumentName) const
199 throw() {
200   const Variable* result = NULL;
201   vector <Variable*>::const_iterator ii, maxii;
202
203   if(*argumentName == TOKENINDICATOR) argumentName ++;
204
205   for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
206     if(anna_strcmp((*ii)->getName().c_str(), argumentName) == 0) {
207       result = *ii;
208       break;
209     }
210   }
211
212   return result;
213 }
214
215 void CommandLine::printUsage() const
216 throw() {
217   int i, maxi(a_arguments.size());
218   cout << "Use: " << a_argv [0] << " ";
219
220   for(i = 0; i < maxi; i ++)
221     cout << a_arguments [i]->asString() << " ";
222
223   cout << endl << "Where: " << endl;
224
225   for(i = 0; i < maxi; i ++) {
226     cout << "   " << a_arguments [i]->getName() << ": " << endl;
227     cout << "     " << a_arguments [i]->getComment() << endl;
228   }
229 }
230
231 string CommandLine::Variable::asString() const
232 throw() {
233   string result;
234   result = ((a_type == Argument::Optional) ? "[ " : "");
235   result += TOKENINDICATOR;
236   result += a_name;
237
238   if(a_needValue == true) {
239     result += " <value_";
240     result += a_name;
241     result += ">";
242   }
243
244   if(a_type == Argument::Optional)
245     result += " ]";
246
247   return result;
248 }
249
250
251 string CommandLine::asString() const
252 throw() {
253   string result = "Provided command-line parameters:\n\n";
254   vector <Variable*>::const_iterator ii, maxii;
255   const char *value;
256
257   for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
258     value = (*ii)->getValue();
259
260     if(value) {
261       result += (*ii)->getName();
262       result += ": ";
263       result += value;
264       result += "\n";
265     }
266   }
267
268   return result;
269 }
270
271 xml::Node* CommandLine::asXML(xml::Node* parent) const throw() {
272   xml::Node* result = parent->createChild("CommandLine");
273   vector <Variable*>::const_iterator ii, maxii;
274   const char *value;
275
276   for(ii = a_arguments.begin(), maxii = a_arguments.end(); ii != maxii; ii ++) {
277     value = (*ii)->getValue();
278
279     if(value)
280       result->createAttribute((*ii)->getName().c_str(), value);
281   }
282
283   return result;
284 }
285
286