source: GTP/trunk/App/Demos/Vis/KdTreeDemo/OGRE/include/WinCmdLineParser.h @ 1204

Revision 1204, 12.7 KB checked in by szydlowski, 18 years ago (diff)

Demo mode complete, added command line parsing for easy benchmark scripting

Line 
1/**
2Utility class for parsing the commandline passed to WinMain and
3extracting options and option arguments
4
5Author: Martin Szydlowski
6*/
7
8#include <vector>
9#include <string>
10
11//using namespace std;
12
13static enum arg_types
14{
15        ARGUMENT_NONE,
16        ARGUMENT_REQUIRED,
17        ARGUMENT_OPTIONAL
18};
19
20class WinCmdLineParser
21{
22public:
23        // describes one option
24        struct option_info
25        {
26                option_info():
27                shortname(""), longname(""), argtype(ARGUMENT_NONE)
28                {
29
30                }
31
32                option_info(const std::string& sname, const std::string& lname, arg_types atype):
33                shortname(sname), longname(lname), argtype(atype)
34                {
35
36                }
37
38                bool operator == (const option_info& rhs) const
39                {
40                        if ((!(shortname.empty() || rhs.shortname.empty()) && shortname == rhs.shortname) ||
41                                (!(longname.empty() || rhs.longname.empty()) && longname == rhs.longname))
42                                return true;
43                        else
44                                return false;
45                }
46
47                std::string shortname;
48                std::string longname;
49                arg_types argtype;
50        };
51
52        struct option_found
53        {
54                option_found():
55                shortname(""), longname(""), optarg("")
56                {
57
58                }
59
60                option_found(const std::string& sname, const std::string& lname, const std::string& arg):
61                shortname(sname), longname(lname), optarg(arg)
62                {
63
64                }
65
66                bool operator == (const option_found& rhs) const
67                {
68                        if ((!(shortname.empty() || rhs.shortname.empty()) && shortname == rhs.shortname) ||
69                                (!(longname.empty() || rhs.longname.empty()) && longname == rhs.longname))
70                                return true;
71                        else
72                                return false;
73                }
74
75                std::string shortname;
76                std::string longname;
77                std::string optarg;
78        };
79
80        WinCmdLineParser(const char * strCmdLine, bool caseSensitive = false):
81                case_sensitive(caseSensitive), reparse(true)
82        {
83                op_case_transform = tolower;
84                splitStrCmdLine(strCmdLine, m_argv);
85        }
86
87        ~WinCmdLineParser()
88        {
89
90        }
91
92        String dumpOptSet()
93        {
94                std::string sDump;
95
96                for (std::vector<option_info>::iterator it = m_optset.begin(); it != m_optset.end(); it ++)
97                {
98                        sDump += it->shortname + ", " + it->longname + "\n";
99                }
100
101                return sDump;
102        }
103
104        // add an option with short & long name (each optional, but one mandatory)
105        // only alphanumeric chars valid
106        WinCmdLineParser& addOpt(const std::string& sname, const std::string& lname, arg_types argtype)
107        {
108                std::string shortname = sname;
109                std::string longname = lname;
110
111                //bool invalid = false;
112               
113                // check sanity: presence & length
114                if (shortname.size() == 0 && longname.size() == 0)
115                        throw std::string("Invalid option specification: Specify at least a short name or a long name");
116                        //invalid = true;
117                if (shortname.size() > 1)
118                        throw std::string("Invalid option specification: Short name too long, must be one character");
119                //invalid = true;
120                if (longname.size() == 1)
121                        throw std::string("Invalid option specification: Long name too short, must be at least two characters");
122                //invalid = true;
123
124                // check sanity: valid characters
125                static std::string valid_chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
126
127                for (size_t i = 0; i < shortname.length(); i ++)
128                        if (valid_chars.find(shortname[i]) == std::string::npos)
129                                throw std::string("Invalid option specification: Invalid character in short name: " + shortname);
130                                //invalid = true;
131
132                for (size_t i = 0; i < longname.length(); i ++)
133                        if (valid_chars.find(longname[i]) == std::string::npos)
134                                throw std::string("Invalid option specification: Invalid character in long name: " + longname);
135                                //invalid = true;
136
137                // if all is well add option to list
138                //if (!invalid)
139                //{
140                        // convert to all upper case when case-insensitive options
141                        if (!case_sensitive)
142                        {
143                                std::transform(shortname.begin(), shortname.end(), shortname.begin(), op_case_transform);
144                                std::transform(longname.begin(), longname.end(), longname.begin(), op_case_transform);
145                        }
146
147                        // try inserting
148                        std::pair<option_info, bool> res = insertOpt(option_info(shortname, longname, argtype), m_optset);
149                        if (!res.second)
150                        {
151                                throw std::string("Conflicting option specification: (" +
152                                        (shortname.empty() ? "" : " -" + shortname) +
153                                        (longname.empty() ? "" : " --" + longname) +
154                                        ") conflicts with (" +
155                                        (res.first.shortname.empty() ? "" : " -" + res.first.shortname) +
156                                        (res.first.longname.empty() ? "" : " --" + res.first.longname) + ")");
157                        }
158                        else
159                        {
160                                reparse = true;
161                        }
162                //}
163
164                return *this;
165        }
166
167        // returns true when option present, false otherwise
168        bool getOpt(const std::string& opt)
169        {
170                // dummy string
171                std::string optarg;
172                return getOpt(opt, optarg);
173        }
174
175
176        // returns true when option was present in the cmd-line and puts the argument
177        // in optarg, when present
178        bool getOpt(const std::string& opt, std::string& optarg)
179        {
180                if (reparse)
181                {
182                        parseOpt(m_argv, m_optset, m_optfound, m_noopt);
183                        reparse = false;
184                }
185
186                optarg.clear();
187
188                std::string option = opt;
189                option_found of;
190
191                if (!case_sensitive)
192                        std::transform(option.begin(), option.end(), option.begin(), op_case_transform);
193
194                if (option.length() == 1) // short option
195                        of.shortname = option;
196                else if (option.length() > 1) // long option
197                        of.longname = option;
198                else // don't mess with me
199                        return false;
200
201                if (findOptArg(m_optfound, of))
202                {
203                        optarg = of.optarg;
204                        return true;
205                }
206                else
207                {
208                        return false;
209                }
210        }
211
212        // returns true when any non-option arguments were in the cmd line
213        // and places them in the arguments vector
214        bool getArgs(std::vector<std::string>& arguments)
215        {
216                if (reparse)
217                {
218                        parseOpt(m_argv, m_optset, m_optfound, m_noopt);
219                        reparse = false;
220                }
221
222                arguments.clear();
223
224                arguments = m_noopt;
225
226                if (m_noopt.size() > 0)
227                        return true;
228                else
229                        return false;
230        }
231
232protected:
233        // split a space-separated string argument in an array of strings
234        // sensitive to ", the only windows way to pass strings with embedded spaces
235        void splitStrCmdLine(const char *strCmdLine, std::vector<std::string>& argv)
236        {
237                std::string cmdline(strCmdLine);
238                argv.clear();
239
240                std::string tmp;
241                unsigned int strpos = 0;
242                bool skipspace = false;
243
244                while (strpos < cmdline.length())
245                {
246                        while (strpos < cmdline.length() && cmdline[strpos] == ' ')
247                                strpos++;
248                        tmp.clear();
249                        while (strpos < cmdline.length() && (cmdline[strpos] != ' ' || skipspace))
250                        {
251                                if (cmdline[strpos] == '"')
252                                {
253                                        ++ strpos;
254                                        skipspace = !skipspace;
255                                        if (!skipspace)
256                                                continue;
257                                }
258
259                                tmp += cmdline[strpos ++];
260                        }
261                        argv.push_back(tmp);
262                }
263
264                //std::ofstream testout("test.txt");
265
266                //testout << cmdline << "\n\n";
267
268                //std::vector<std::string>::iterator it = argv.begin();
269                //while (it != argv.end())
270                //{
271                //      testout << *it << "\n";
272                //      ++ it;
273                //}
274
275                //testout.close();
276        }
277
278        void parseOpt(const std::vector<std::string>& argv, const std::vector<option_info>& optset,
279                std::vector<option_found>& optfound, std::vector<std::string>& noopt)
280        {
281                // reset old options list
282                optfound.clear();
283
284                // bail out when nothing to parse
285                if (argv.empty() || optset.empty())
286                        return;
287
288                // iterate through argv and create opt_founds
289                size_t item = 0;
290                while (item < argv.size())
291                {
292                        // dummy info for lookup
293                        std::string optstr;
294                        option_info info;
295
296                        // skip empty items, shold not happen actually ...
297                        if (argv[item].length() > 0)
298                        {
299                                // check if item might be option (starts with /, - or --)
300                                if (argv[item][0] == '-')
301                                {
302                                        // unix-style option, one dash - long or short option
303                                        // two dashes - long option
304                                        optstr = argv[item].substr(1, argv[item].length());
305                                        if (optstr.length() > 1 && optstr[0] == '-') // long option
306                                        {
307                                                optstr = optstr.substr(1, optstr.length());
308                                                if (optstr.length() == 1) // invalid
309                                                        optstr = "";
310                                        }
311                                }
312                                else if (argv[item][0] == '/')
313                                {
314                                        // windows-style option, long or short
315                                        optstr = argv[item].substr(1, argv[item].length());
316                                }
317                                else
318                                {
319                                        // no option and no option argument since these are skipped
320                                        // -> must be a program argument
321                                        noopt.push_back(argv[item]);
322                                        ++ item;
323                                        continue;
324                                }
325
326                                if (!case_sensitive)
327                                        std::transform(optstr.begin(), optstr.end(), optstr.begin(), op_case_transform);
328
329                                if (optstr.length() == 1) // short option
330                                        info.shortname = optstr;
331                                else if (optstr.length() > 1) // long option
332                                        info.longname = optstr;
333
334                                // lookup
335                                if (findOpt(optset, info))
336                                {
337                                         // find optarg
338                                        std::string optarg;
339                                        if (item + 1 < argv.size())
340                                                optarg = argv[item + 1];
341
342                                        std::pair<option_found, bool> res;
343
344                                        switch (info.argtype)
345                                        {
346                                        case ARGUMENT_NONE:
347                                                res = insertOptArg(option_found(info.shortname, info.longname, ""), optfound);
348                                                if (!res.second)
349                                                {
350                                                        throw std::string("Duplicate option: (" +
351                                                                (res.first.shortname.empty() ? "" : " -" + res.first.shortname) +
352                                                                (res.first.longname.empty() ? "" : " --" + res.first.longname) +
353                                                                ") allready specified");
354                                                }
355                                                break;
356                                        case ARGUMENT_REQUIRED:
357                                                if (optarg.length() > 0 && optarg[0] != '-' && optarg[0] != '/')
358                                                {
359                                                        res = insertOptArg(option_found(info.shortname, info.longname, optarg), optfound);
360                                                        if (!res.second)
361                                                        {
362                                                                throw std::string("Duplicate option: (" +
363                                                                        (res.first.shortname.empty() ? "" : " -" + res.first.shortname) +
364                                                                        (res.first.longname.empty() ? "" : " --" + res.first.longname) +
365                                                                        ") allready specified with argument: " + res.first.optarg);
366                                                        }
367                                                        ++ item;
368                                                }
369                                                else
370                                                {
371                                                        throw std::string("Missing argument for option: " + argv[item]);
372                                                }
373                                                break;
374                                        case ARGUMENT_OPTIONAL:
375                                                if (optarg.length() > 0 && optarg[0] != '-' && optarg[0] != '/')
376                                                {
377                                                        res = insertOptArg(option_found(info.shortname, info.longname, optarg), optfound);
378                                                        if (!res.second)
379                                                        {
380                                                                throw std::string("Duplicate option: (" +
381                                                                        (res.first.shortname.empty() ? "" : " -" + res.first.shortname) +
382                                                                        (res.first.longname.empty() ? "" : " --" + res.first.longname) +
383                                                                        ") allready specified with argument: " + res.first.optarg);
384                                                        }
385                                                        ++ item;
386                                                }
387                                                else
388                                                {
389                                                        res = insertOptArg(option_found(info.shortname, info.longname, ""), optfound);
390                                                        if (!res.second)
391                                                        {
392                                                                throw std::string("Duplicate option: (" +
393                                                                        (res.first.shortname.empty() ? "" : " -" + res.first.shortname) +
394                                                                        (res.first.longname.empty() ? "" : " --" + res.first.longname) +
395                                                                        ") allready specified");
396                                                        }
397                                                }
398                                                break;
399                                        default:
400                                                break;
401                                        }
402                                }
403                                // otherwise panic
404                                else
405                                {
406                                        throw std::string("Invalid option: " + argv[item]);
407                                }
408
409                        }
410
411                        ++ item;
412                }
413        }
414
415        std::pair<option_info, bool> insertOpt(const option_info& optinfo, std::vector<option_info>& optset)
416        {
417                // check for conflicts
418                for (size_t i = 0; i < optset.size(); i ++)
419                {
420                        if (optinfo == optset[i])
421                        {
422                                return std::pair<option_info, bool>(optset[i], false);
423                        }
424                }
425                // no conflicts - insert
426                optset.push_back(optinfo);
427                return std::pair<option_info, bool>(optinfo, true);
428        }
429
430        bool findOpt(const std::vector<option_info>& optset, option_info& optinfo)
431        {
432                // find entry
433                for (size_t i = 0; i < optset.size(); i ++)
434                {
435                        if (optinfo == optset[i])
436                        {
437                                optinfo = optset[i];
438                                return true;
439                        }
440                }
441                // not found - reset
442                return false;
443        }
444
445        std::pair<option_found, bool> insertOptArg(const option_found& of, std::vector<option_found>& optfound)
446        {
447                // check for conflicts
448                for (size_t i = 0; i < optfound.size(); i ++)
449                {
450                        if (of == optfound[i])
451                        {
452                                return std::pair<option_found, bool>(optfound[i], false);
453                        }
454                }
455                // no conflicts - insert
456                optfound.push_back(of);
457                return std::pair<option_found, bool>(of, true);
458        }
459
460        bool findOptArg(const std::vector<option_found>& optfound, option_found& of)
461        {
462                // find entry
463                for (size_t i = 0; i < optfound.size(); i ++)
464                {
465                        if (of == optfound[i])
466                        {
467                                // found - set values
468                                of = optfound[i];
469                                return true;
470                        }
471                }
472                // not found
473                return false;
474        }
475
476        // holds the elements of the command line
477        std::vector<std::string> m_argv;
478
479        // holds the elements of the command line which are not options or option arguments
480        std::vector<std::string> m_noopt;
481
482        // all options we recognize
483        std::vector<option_info> m_optset;
484
485        // all options we have found so far
486        std::vector<option_found> m_optfound;
487
488        // parse options case sensitive?
489        bool case_sensitive;
490
491        // reparse list for next getOpt
492        bool reparse;
493
494        // poiter to unary function for uppercase transform;
495        int (*op_case_transform)(int);
496};
Note: See TracBrowser for help on using the repository browser.