1 #pragma once
2 /****************************************************************************
3 * console_utils.h: General command line parsing and other utilities (soon)
4 * This is part of the YafaRay package
5 * Copyright (C) 2010 Rodrigo Placencia
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #ifndef Y_CONSOLE_UTILS_H
23 #define Y_CONSOLE_UTILS_H
24
25 #include <yafray_constants.h>
26 #include <core_api/logging.h>
27 #include <iostream>
28 #include <iomanip>
29 #include <vector>
30 #include <string>
31 #include <sstream>
32
33 __BEGIN_YAFRAY
34
35 #define ystring std::string
36 #define ysstream std::stringstream
37 #define yisstream std::istringstream
38 #define yvector std::vector
39
40 //! Struct that holds the state of the registrered options and the values parsed from command line args
41
42 struct cliParserOption_t
43 {
44 cliParserOption_t(ystring sOpt, ystring lOpt, bool isFlag, ystring desc);
45 ystring shortOpt;
46 ystring longOpt;
47 bool isFlag;
48 ystring desc;
49 ystring value;
50 bool isSet;
51 };
52
cliParserOption_t(ystring sOpt,ystring lOpt,bool isFlag,ystring desc)53 cliParserOption_t::cliParserOption_t(ystring sOpt, ystring lOpt, bool isFlag, ystring desc) : shortOpt(sOpt), longOpt(lOpt), isFlag(isFlag), desc(desc)
54 {
55 if(!shortOpt.empty())
56 {
57 shortOpt = "-" + sOpt;
58 }
59 if(!longOpt.empty())
60 {
61 longOpt = "--" + lOpt;
62 }
63 value = "";
64 isSet = false;
65 }
66
67 //! The command line option parsing and handling class
68 /*!parses GNU style command line arguments pairs and flags with space (' ') as pair separator*/
69
70 class cliParser_t
71 {
72 public:
cliParser_t()73 cliParser_t() {} //! Default constructor for 2 step init
74 cliParser_t( int argc, char **argv, int cleanArgsNum, int cleanOptArgsNum, const ystring cleanArgError); //! One step init constructor
75 ~cliParser_t();
76 void setCommandLineArgs( int argc, char **argv); //! Initialization method for 2 step init
77 void setCleanArgsNumber(int argNum, int optArg, const ystring cleanArgError); //! Configures the parser to get arguments non-paired at the end of the command string with optional arg number
78 void setOption(ystring sOpt, ystring lOpt, bool isFlag, ystring desc); //! Option registrer method, it adds a valid parsing option to the list
79 ystring getOptionString(const ystring sOpt, const ystring lOpt = ""); //! Retrieves the string value asociated with the option if any, if no option returns an empty string
80 int getOptionInteger(const ystring sOpt, const ystring lOpt = ""); //! Retrieves the integer value asociated with the option if any, if no option returns -65535
81 bool getFlag(const ystring sOpt, const ystring lOpt = ""); //! Returns true is the flas was set in command line, false else
82 bool isSet(const ystring sOpt, const ystring lOpt = ""); //! Returns true if the option was set on the command line (only for paired options)
83 yvector<ystring> getCleanArgs() const;
84 void setAppName(const ystring name, const ystring bUsage);
85 void printUsage() const; //! Prints usage instructions with the registrered options
86 void printError() const; //! Prints error found during parsing (if any)
87 void clearOptions(); //! Clears the registrered options list
88 bool parseCommandLine(); //! Parses the input values from command line and fills the values on the right options if they exist on the args
89
90 private:
91 size_t argCount; //! Input arguments count
92 ystring appName; //! Holds the app name used in the usage construction, defaults to argv[0]
93 ystring binName; //! Holds the name of the executabl binary (argv[0])
94 ystring basicUsage; //! Holds the basic usage instructions of the command
95 yvector<ystring> argValues; //! Holds argv values
96 yvector<ystring> cleanValues; //! Holds clean (non-paired options) values
97 yvector<cliParserOption_t*> regOptions; //! Holds registrered options
98 size_t cleanArgs;
99 size_t cleanArgsOptional;
100 ystring cleanArgsError;
101 ystring parseError;
102 };
103
cliParser_t(int argc,char ** argv,int cleanArgsNum,int cleanOptArgsNum,const ystring cleanArgError)104 cliParser_t::cliParser_t(int argc, char **argv, int cleanArgsNum, int cleanOptArgsNum, const ystring cleanArgError)
105 {
106 setCommandLineArgs( argc, argv );
107 setCleanArgsNumber(cleanArgsNum, cleanOptArgsNum, cleanArgError);
108 }
109
~cliParser_t()110 cliParser_t::~cliParser_t()
111 {
112 clearOptions();
113 }
114
setCommandLineArgs(int argc,char ** argv)115 void cliParser_t::setCommandLineArgs( int argc, char **argv)
116 {
117 argCount = (size_t)argc;
118 argValues.clear();
119 appName = ystring(argv[0]);
120 binName = ystring(argv[0]);
121 for(size_t i = 1; i < argCount; i++)
122 {
123 argValues.push_back(ystring(argv[i]));
124 }
125 }
126
setCleanArgsNumber(int argNum,int optArg,const ystring cleanArgError)127 void cliParser_t::setCleanArgsNumber(int argNum, int optArg, const ystring cleanArgError)
128 {
129 cleanArgs = argNum;
130 cleanArgsOptional = optArg;
131 cleanArgsError = cleanArgError;
132 }
133
setOption(ystring sOpt,ystring lOpt,bool isFlag,ystring desc)134 void cliParser_t::setOption(ystring sOpt, ystring lOpt, bool isFlag, ystring desc)
135 {
136 if(!sOpt.empty() || !lOpt.empty()) regOptions.push_back(new cliParserOption_t(sOpt, lOpt, isFlag, desc));
137 }
138
getOptionString(const ystring sOpt,const ystring lOpt)139 ystring cliParser_t::getOptionString(const ystring sOpt, const ystring lOpt)
140 {
141 ystring cmpSOpt = "-" + sOpt;
142 ystring cmpLOpt = "--" + lOpt;
143
144 for(size_t j = 0; j < regOptions.size(); j++)
145 {
146 if(regOptions[j]->shortOpt.compare(cmpSOpt) == 0 || regOptions[j]->longOpt.compare(cmpLOpt) == 0)
147 {
148 if(!regOptions[j]->isFlag)
149 {
150 return regOptions[j]->value;
151 }
152 }
153 }
154
155 return ystring("");
156 }
157
getOptionInteger(const ystring sOpt,const ystring lOpt)158 int cliParser_t::getOptionInteger(const ystring sOpt, const ystring lOpt)
159 {
160 int ret = -65535;
161 ystring cmpSOpt = "-" + sOpt;
162 ystring cmpLOpt = "--" + lOpt;
163
164 for(size_t j = 0; j < regOptions.size(); j++)
165 {
166 if(regOptions[j]->shortOpt.compare(cmpSOpt) == 0 || regOptions[j]->longOpt.compare(cmpLOpt) == 0)
167 {
168 if(!regOptions[j]->isFlag)
169 {
170 yisstream i(regOptions[j]->value.c_str());
171 if(!(i >> ret))
172 return -65535;
173 else
174 return ret;
175 }
176 }
177 }
178
179 return ret;
180 }
181
getFlag(const ystring sOpt,const ystring lOpt)182 bool cliParser_t::getFlag(const ystring sOpt, const ystring lOpt)
183 {
184 ystring cmpSOpt = "-" + sOpt;
185 ystring cmpLOpt = "--" + lOpt;
186
187 for(size_t j = 0; j < regOptions.size(); j++)
188 {
189 if(regOptions[j]->shortOpt.compare(cmpSOpt) == 0 || regOptions[j]->longOpt.compare(cmpLOpt) == 0)
190 {
191 if(regOptions[j]->isFlag)
192 {
193 return regOptions[j]->isSet;
194 }
195 }
196 }
197
198 return false;
199 }
200
isSet(const ystring sOpt,const ystring lOpt)201 bool cliParser_t::isSet(const ystring sOpt, const ystring lOpt)
202 {
203 ystring cmpSOpt = "-" + sOpt;
204 ystring cmpLOpt = "--" + lOpt;
205
206 for(size_t j = 0; j < regOptions.size(); j++)
207 {
208 if(regOptions[j]->shortOpt.compare(cmpSOpt) == 0 || regOptions[j]->longOpt.compare(cmpLOpt) == 0)
209 {
210 if(!regOptions[j]->isFlag)
211 {
212 return regOptions[j]->isSet;
213 }
214 }
215 }
216
217 return false;
218 }
219
getCleanArgs()220 yvector<ystring> cliParser_t::getCleanArgs() const
221 {
222 return cleanValues;
223 }
224
setAppName(const ystring name,const ystring bUsage)225 void cliParser_t::setAppName(const ystring name, const ystring bUsage)
226 {
227 appName.clear();
228 appName = name;
229 basicUsage.clear();
230 basicUsage = bUsage;
231 }
printUsage()232 void cliParser_t::printUsage() const
233 {
234 Y_INFO << appName << yendl
235 << "Usage: " << binName << " " << basicUsage << yendl
236 << "OPTIONS:" << yendl;
237 for(size_t i = 0; i < regOptions.size(); i++)
238 {
239 ysstream name;
240 name << regOptions[i]->shortOpt << ", " << regOptions[i]->longOpt << (regOptions[i]->isFlag ? "" : " <value>");
241 std::cout << " "
242 << std::setiosflags(std::ios::left) << std::setw(35)
243 << name.str()
244 << regOptions[i]->desc << yendl;
245 }
246 Y_INFO << "Usage instructions end." << yendl;
247 }
248
printError()249 void cliParser_t::printError() const
250 {
251 Y_ERROR << parseError << yendl;
252 }
253
clearOptions()254 void cliParser_t::clearOptions()
255 {
256 if(regOptions.size() > 0)
257 {
258 for(size_t i = 0; i < regOptions.size(); i++)
259 {
260 delete regOptions[i];
261 }
262 regOptions.clear();
263 }
264 }
265
parseCommandLine()266 bool cliParser_t::parseCommandLine()
267 {
268 ysstream error;
269 cleanValues.clear();
270 for(size_t i = 0; i < argValues.size(); i++)
271 {
272 if((i >= argValues.size() - (cleanArgs - cleanArgsOptional)) || (i >= argValues.size() - cleanArgs))
273 {
274 if(argValues[i].compare(0,1,"-") != 0)
275 {
276 cleanValues.push_back(argValues[i]);
277 continue;
278 }
279 }
280
281 for(size_t j = 0; j < regOptions.size(); j++)
282 {
283 if(regOptions[j]->shortOpt.compare(argValues[i]) == 0 || regOptions[j]->longOpt.compare(argValues[i]) == 0)
284 {
285 if(!regOptions[j]->isFlag)
286 {
287 if(i < argValues.size())
288 {
289 i++;
290 if(argValues[i].compare(0,1,"-") != 0)
291 {
292 regOptions[j]->value.clear();
293 regOptions[j]->value.append(argValues[i]);
294 regOptions[j]->isSet = true;
295 }
296 else
297 {
298 error << "Option " << regOptions[j]->longOpt << " has no value";
299 parseError = error.str();
300 return false;
301 }
302 }
303 else
304 {
305 error << "Option " << regOptions[j]->longOpt << " has no value";
306 parseError = error.str();
307 return false;
308 }
309 }
310 else
311 {
312 regOptions[j]->isSet = true;
313 }
314 }
315 }
316 }
317
318 if(cleanValues.size() < cleanArgs && cleanValues.size() < cleanArgs - cleanArgsOptional)
319 {
320 error << cleanArgsError;
321 parseError = error.str();
322 return false;
323 }
324
325 return true;
326 }
327
328 __END_YAFRAY
329
330 #endif /* Y_CONSOLE_UTILS_H */
331