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