Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / cmd_args.cpp
blobe6211faafc9a47d254e2a94c41c858fbab8607c9
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
18 // Includes
20 #include "stdmisc.h"
21 #include "nel/misc/cmd_args.h"
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NLMISC
34 CCmdArgs::CCmdArgs()
36 #ifdef NL_VERSION
37 _Version = NL_VERSION;
38 #endif
40 // add help
41 addArg("h", "help", "", "Display this help");
43 // add version
44 addArg("v", "version", "", "Display version of this program");
47 void CCmdArgs::addArg(const TArg &arg)
49 _Args.push_back(arg);
52 void CCmdArgs::addArg(const std::string &shortName, const std::string &longName, const std::string &helpName, const std::string &helpDescription, bool onlyOnce)
54 TArg arg;
55 arg.shortName = shortName;
56 arg.longName = longName;
57 arg.helpName = helpName;
58 arg.helpDescription = helpDescription;
59 arg.onlyOnce = onlyOnce;
60 arg.found = false;
61 arg.required = false;
63 addArg(arg);
66 void CCmdArgs::addAdditionalArg(const std::string &helpName, const std::string &helpDescription, bool onlyOnce, bool required)
68 TArg arg;
69 arg.helpName = helpName;
70 arg.helpDescription = helpDescription;
71 arg.onlyOnce = onlyOnce;
72 arg.found = false;
73 arg.required = required;
75 addArg(arg);
78 bool CCmdArgs::haveArg(const std::string &argName) const
80 // process each argument
81 for(uint i = 0; i < _Args.size(); ++i)
83 const TArg &arg = _Args[i];
85 // return true if long arg found
86 if (arg.shortName == argName) return arg.found;
89 return false;
92 std::vector<std::string> CCmdArgs::getArg(const std::string &argName) const
94 // process each argument
95 for(uint i = 0; i < _Args.size(); ++i)
97 const TArg &arg = _Args[i];
99 // return values if short arg found
100 if (arg.shortName == argName && arg.found) return arg.values;
103 // return an empty vector
104 return std::vector<std::string>();
107 bool CCmdArgs::haveLongArg(const std::string &argName) const
109 // process each argument
110 for(uint i = 0; i < _Args.size(); ++i)
112 const TArg &arg = _Args[i];
114 // return true if long arg found
115 if (arg.longName == argName) return arg.found;
118 return false;
121 std::vector<std::string> CCmdArgs::getLongArg(const std::string &argName) const
123 // process each argument
124 for(uint i = 0; i < _Args.size(); ++i)
126 const TArg &arg = _Args[i];
128 // return values if long arg found
129 if (arg.longName == argName && arg.found) return arg.values;
132 // return an empty vector
133 return std::vector<std::string>();
136 bool CCmdArgs::needAdditionalArg() const
138 // process each argument
139 for(uint i = 0; i < _Args.size(); ++i)
141 const TArg &arg = _Args[i];
143 // they don't have any short or long name, but need a name in help
144 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty() && arg.required && !arg.found)
145 return true;
148 return false;
151 bool CCmdArgs::haveAdditionalArg() const
153 // process each argument
154 for(uint i = 0; i < _Args.size(); ++i)
156 const TArg &arg = _Args[i];
158 // they don't have any short or long name, but need a name in help
159 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty() && arg.found)
160 return true;
163 return false;
166 bool CCmdArgs::haveAdditionalArg(const std::string &name) const
168 // process each argument
169 for(uint i = 0; i < _Args.size(); ++i)
171 const TArg &arg = _Args[i];
173 // they don't have any short or long name, but need a name in help
174 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty() && arg.helpName == name && arg.found)
175 return true;
178 return false;
181 std::vector<std::string> CCmdArgs::getAdditionalArg(const std::string &name) const
183 // process each argument
184 for(uint i = 0; i < _Args.size(); ++i)
186 const TArg &arg = _Args[i];
188 // they don't have any short or long name, but need a name in help
189 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty() && arg.helpName == name)
190 return arg.values;
193 // return an empty vector
194 return std::vector<std::string>();
197 bool CCmdArgs::parse(const std::string &args)
199 std::vector<std::string> argv;
201 #ifdef NL_OS_WINDOWS
202 wchar_t str[4096];
203 uint len = GetModuleFileNameW(NULL, str, 4096);
205 // first argument should be full path to executable
206 if (len && len < 4096)
207 argv.push_back(wideToUtf8(str));
208 #endif
210 // convert string with arguments to array
211 explodeArguments(args, argv);
213 return parse(argv);
216 bool CCmdArgs::parse(int argc, char **argv)
218 // convert C strings to STL strings
219 std::vector<std::string> args;
221 for(sint i = 0; i < argc; ++i)
223 #ifdef NL_OS_MAC
224 // get rid of -psn_* arguments under OS X
225 if (strncmp(argv[i], "-psn_", 5) == 0) continue;
226 #endif
228 args.push_back(argv[i]);
231 return parse(args);
234 bool CCmdArgs::parse(const std::vector<std::string> &argv)
236 // no parameters
237 if (argv.empty()) return false;
239 // first argument is always the program name
240 _ProgramName = CFile::getFilename(argv.front());
241 _ProgramPath = CPath::makePathAbsolute(CPath::standardizePath(CFile::getPath(argv.front())), CPath::getCurrentPath(), true);
243 // current path
244 _StartupPath = CPath::standardizePath(CPath::getCurrentPath());
246 // set process name for logs
247 CLog::setProcessName(_ProgramName);
249 // arguments count
250 uint argc = argv.size();
252 // process each argument
253 for (uint i = 1; i < argc; i++)
255 std::string name = argv[i];
257 #ifdef NL_OS_WINDOWS
258 // support / and - under Windows, arguments should be at least 2 characters
259 if (name.size() > 1 && (name[0] == '-' || name[0] == '/'))
260 #else
261 if (name.size() > 1 && name[0] == '-')
262 #endif
264 // it's a long name if using --
265 bool useLongName = name[0] == '-' && name[1] == '-';
267 // extract argument name
268 name = name.substr(useLongName ? 2:1);
270 std::string value;
272 if (useLongName)
274 // look if using = to define value
275 std::string::size_type pos = name.find('=');
277 if (pos != std::string::npos)
279 // value is second part, name the first one
280 value = name.substr(pos+1);
281 name = name.substr(0, pos);
284 else if (name.length() > 1)
286 value = name.substr(1);
287 name = name.substr(0, 1);
290 bool found = false;
292 // process each argument definition
293 for(uint j = 0; j < _Args.size(); ++j)
295 TArg &arg = _Args[j];
297 // only process arguments of the right type
298 if ((useLongName && name != arg.longName) || (!useLongName && name != arg.shortName)) continue;
300 // already get the only once argument
301 if (arg.found && arg.onlyOnce)
303 // the last one is the only kept, so discard previous ones
304 arg.values.clear();
307 // argument is found
308 found = arg.found = true;
310 // another argument is required
311 if (!arg.helpName.empty())
313 // if the value hasn't be specified by =
314 if (value.empty() && i+1 < argc)
316 // take next argument
317 value = argv[++i];
320 // add argument value if not empty
321 if (!value.empty())
323 arg.values.push_back(value);
327 break;
330 if (!found)
332 printf("Warning: Argument %s not recognized, skip it!\n", name.c_str());
335 else
337 // process each argument definition
338 for(uint j = 0, len = _Args.size(); j < len; ++j)
340 TArg &arg = _Args[j];
342 // only process arguments that don't have a name
343 if (!arg.shortName.empty() || !arg.longName.empty()) continue;
345 // already get the only once argument
346 if (arg.found && arg.onlyOnce) continue;
348 arg.found = true;
350 // in fact, if there are more than one required arguments, all arguments are added in first one to simplify
351 arg.values.push_back(name);
353 break;
358 // process version
359 if (haveLongArg("version"))
361 displayVersion();
362 return false;
365 // process help if requested or if required arguments are missing
366 if (haveLongArg("help") || needAdditionalArg())
368 displayHelp();
369 return false;
372 return true;
375 void CCmdArgs::displayHelp()
377 // display program name
378 printf("Usage: %s ", _ProgramName.c_str());
380 // display optional parameters
381 for(uint i = 0; i < _Args.size(); ++i)
383 const TArg &arg = _Args[i];
385 // only short argument is displayed
386 if (!arg.shortName.empty())
388 printf("[-%s", arg.shortName.c_str());
390 // a parameter is required
391 if (!arg.helpName.empty())
393 printf("<%s>", arg.helpName.c_str());
396 printf("]");
400 // display required arguments
401 for(uint i = 0; i < _Args.size(); ++i)
403 const TArg &arg = _Args[i];
405 // they don't have any short or long name, but need a name in help
406 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty())
408 printf(" %c%s", arg.required ? '<':'[', arg.helpName.c_str());
410 // if support more than once argument
411 if (!arg.onlyOnce) printf("...");
413 printf("%c", arg.required ? '>':']');
417 printf("\n");
419 if (!_Description.empty())
421 printf("\n%s\n", _Description.c_str());
424 printf("\nWhere options are:\n");
426 // display details on each argument
427 for(uint i = 0; i < _Args.size(); ++i)
429 const TArg &arg = _Args[i];
431 // not an optional argument
432 if (arg.shortName.empty() && arg.longName.empty()) continue;
434 // 2 spaces
435 printf(" ");
437 std::vector<std::string> syntaxes;
439 // display short argument
440 if (!arg.shortName.empty())
442 // and it's required argument
443 if (!arg.helpName.empty())
445 syntaxes.push_back(toString("-%s <%s>", arg.shortName.c_str(), arg.helpName.c_str()));
447 else
449 syntaxes.push_back(toString("-%s", arg.shortName.c_str()));
453 // display long argument
454 if (!arg.longName.empty())
456 if (!arg.helpName.empty())
458 // display first syntax for long argument, --arg <value>
459 syntaxes.push_back(toString("--%s <%s>", arg.longName.c_str(), arg.helpName.c_str()));
461 else
463 syntaxes.push_back(toString("--%s", arg.longName.c_str()));
467 for(uint j = 0; j < syntaxes.size(); ++j)
469 if (j > 0)
471 printf("%s ", (j == syntaxes.size() - 1) ? " or":",");
474 printf("%s", syntaxes[j].c_str());
477 // display argument description
478 if (!arg.helpDescription.empty())
480 printf(" : %s", arg.helpDescription.c_str());
483 printf("\n");
486 // process each argument
487 for(uint i = 0; i < _Args.size(); ++i)
489 const TArg &arg = _Args[i];
491 // only display required arguments
492 if (arg.shortName.empty() && arg.longName.empty() && !arg.helpName.empty() && !arg.helpDescription.empty())
494 printf(" %s : %s\n", arg.helpName.c_str(), arg.helpDescription.c_str());
499 void CCmdArgs::displayVersion()
501 // display a verbose version string
502 #ifdef BUILD_DATE
503 printf("%s %s (built on %s)\nCopyright (C) %s\n", _ProgramName.c_str(), _Version.c_str(), BUILD_DATE, COPYRIGHT);
504 #endif
507 }; // NAMESPACE NLMISC