Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: sandbox_qt/src/libraries/core/CommandLineParser.cc @ 7425

Last change on this file since 7425 was 7424, checked in by rgrieder, 14 years ago

Added CommandlineParser again and adjusted it to work with QVariant instead of MultiType.
Also removed obsolete Game class.

  • Property svn:eol-style set to native
File size: 13.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Reto Grieder
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "CommandLineParser.h"
30
31#include <algorithm>
32#include <sstream>
33
34//#include "util/Convert.h"
35#include "util/Debug.h"
36#include "util/Exception.h"
37#include "util/StringUtils.h"
38#include "util/SubString.h"
39#include "PathConfig.h"
40
41namespace orxonox
42{
43    SetCommandLineOnlyArgument(optionsFile, "start.ini").shortcut("o");
44
45    /**
46    @brief
47        Parses a value string for a command line argument.
48        It simply uses convertValue(Output, Input) to do that.
49        Bools are treated specially. That is necessary
50        so that you can have simple command line switches.
51    */
52    void CommandLineArgument::parse(const std::string& value, bool bParsingFile)
53    {
54        if (bParsingFile && this->bCommandLineOnly_)
55            ThrowException(Argument, "Command line argument '" + getName() + "' is not allowed in files.");
56        if (value_.type() == QVariant::Bool)
57        {
58            // Command line switch activated
59            this->bHasDefaultValue_ = false;
60            this->value_ = true;
61        }
62        else
63        {
64            QVariant temp(QString::fromStdString(value));
65            if (!temp.convert(value_.type()))
66                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
67            else
68            {
69                this->bHasDefaultValue_ = false;
70                this->value_ = temp;
71            }
72        }
73    }
74
75
76    /**
77    @brief
78        Destructor destroys all CommandLineArguments with it.
79    */
80    CommandLineParser::~CommandLineParser()
81    {
82        CommandLineParser::destroyAllArguments();
83    }
84
85    /**
86    @brief
87        Returns a unique instance (Meyers Singleton).
88    */
89    CommandLineParser& CommandLineParser::_getInstance()
90    {
91        static CommandLineParser instance;
92        return instance;
93    }
94
95    /**
96    @brief
97        Destroys all command line arguments. This should be called at the end
98        of main. Do not use before that.
99    */
100    void CommandLineParser::destroyAllArguments()
101    {
102        for (std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.begin();
103            it != _getInstance().cmdLineArgs_.end(); ++it)
104            delete it->second;
105        _getInstance().cmdLineArgs_.clear();
106    }
107
108    /**
109    @brief
110        Reads the command line parses the values of each argument.
111        It is then stored in the corresponding CommandLineArgument.
112    @note
113        The reason that you have to provide the string to be parsed as
114        space separted list is because of argc and argv. If you only have
115        a whole string, simply use getAllStrings() of SubString.
116    @param arguments
117        Vector of space separated strings.
118    @param bParsingFile
119        Parsing a file or the command line itself
120    */
121    void CommandLineParser::_parse(const std::vector<std::string>& arguments, bool bParsingFile)
122    {
123        try
124        {
125            // why this? See bFirstTimeParse_ declaration.
126            if (bFirstTimeParse_)
127            {
128                // first shove all the shortcuts in a map
129                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
130                    it != cmdLineArgs_.end(); ++it)
131                {
132                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
133                        "Cannot have two command line shortcut with the same name.");
134                    if (!it->second->getShortcut().empty())
135                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
136                }
137                bFirstTimeParse_ = false;
138            }
139
140            std::string name;
141            std::string shortcut;
142            std::string value;
143            for (unsigned int i = 0; i < arguments.size(); ++i)
144            {
145                if (arguments[i].size() != 0)
146                {
147                    // sure not ""
148                    if (arguments[i][0] == '-')
149                    {
150                        // start with "-"
151                        if (arguments[i].size() == 1)
152                        {
153                            // argument[i] is "-", probably a minus sign
154                            value += "- ";
155                        }
156                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
157                        {
158                            // negative number as a value
159                            value += arguments[i] + ' ';
160                        }
161                        else
162                        {
163                            // can be shortcut or full name argument
164
165                            // save old data first
166                            value = removeTrailingWhitespaces(value);
167                            if (!name.empty())
168                            {
169                                checkFullArgument(name, value, bParsingFile);
170                                name.clear();
171                                assert(shortcut.empty());
172                            }
173                            else if (!shortcut.empty())
174                            {
175                                checkShortcut(shortcut, value, bParsingFile);
176                                shortcut.clear();
177                                assert(name.empty());
178                            }
179
180                            if (arguments[i][1] == '-')
181                            {
182                                // full name argument with "--name"
183                                name = arguments[i].substr(2);
184                            }
185                            else
186                            {
187                                // shortcut with "-s"
188                                shortcut = arguments[i].substr(1);
189                            }
190
191                            // reset value string
192                            value.clear();
193                        }
194                    }
195                    else
196                    {
197                        // value string
198
199                        if (name.empty() && shortcut.empty())
200                        {
201                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
202                        }
203
204                        // Concatenate strings as long as there's no new argument by "-" or "--"
205                        value += arguments[i] + ' ';
206                    }
207                }
208            }
209
210            // parse last argument
211            value = removeTrailingWhitespaces(value);
212            if (!name.empty())
213            {
214                checkFullArgument(name, value, bParsingFile);
215                assert(shortcut.empty());
216            }
217            else if (!shortcut.empty())
218            {
219                checkShortcut(shortcut, value, bParsingFile);
220                assert(name.empty());
221            }
222        }
223        catch (const ArgumentException& ex)
224        {
225            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
226            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
227            throw GeneralException("");
228        }
229    }
230
231    /**
232    @brief
233        Parses an argument based on its full name.
234    @param name
235        Full name of the argument
236    @param value
237        String containing the value
238    @param bParsingFile
239        Parsing a file or the command line itself
240    */
241    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
242    {
243        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
244        if (it == cmdLineArgs_.end())
245            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
246
247        it->second->parse(value, bParsingFile);
248    }
249
250    /**
251    @brief
252        Parses an argument based on its shortcut.
253    @param shortcut
254        Shortcut to the argument
255    @param value
256        String containing the value
257    @param bParsingFile
258        Parsing a file or the command line itself
259    */
260    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
261    {
262        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
263        if (it == cmdLineArgsShortcut_.end())
264            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
265
266        it->second->parse(value, bParsingFile);
267    }
268
269    std::string CommandLineParser::getUsageInformation()
270    {
271        CommandLineParser& inst = _getInstance();
272        std::ostringstream infoStr;
273
274        // determine maximum name size
275        size_t maxNameSize = 0;
276        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
277            it != inst.cmdLineArgs_.end(); ++it)
278        {
279            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
280        }
281
282        infoStr << std::endl;
283        infoStr << "Usage: orxonox [options]" << std::endl;
284        infoStr << "Available options:" << std::endl;
285
286        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
287            it != inst.cmdLineArgs_.end(); ++it)
288        {
289            if (!it->second->getShortcut().empty())
290                infoStr << " [-" << it->second->getShortcut() << "] ";
291            else
292                infoStr << "      ";
293            infoStr << "--" << it->second->getName() << ' ';
294            if (it->second->getValue().type() != QVariant::Bool)
295                infoStr << "ARG ";
296            else
297                infoStr << "    ";
298            // fill with the necessary amount of blanks
299            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
300            infoStr << ": " << it->second->getInformation();
301            infoStr << std::endl;
302        }
303        return infoStr.str();
304    }
305
306    void CommandLineParser::generateDoc(std::ofstream& file)
307    {
308        file << "/** @page cmdargspage Command Line Arguments Reference" << endl;
309        file << "    @verbatim"; /*no endl*/
310        file << getUsageInformation(); /*no endl*/
311        file << "    @endverbatim" << endl;
312        file << "*/" << endl;
313    }
314
315    /**
316    @brief
317        Retrieves a CommandLineArgument.
318        The method throws an exception if 'name' was not found or the value could not be converted.
319    @note
320        You should of course not call this method before the command line has been parsed.
321    */
322    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
323    {
324        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
325        if (it == _getInstance().cmdLineArgs_.end())
326        {
327            ThrowException(Argument, "Could find command line argument '" + name + "'.");
328        }
329        else
330        {
331            return it->second;
332        }
333    }
334
335    /**
336    @brief
337        Parses only the command line for CommandLineArguments.
338    */
339    void CommandLineParser::_parseCommandLine(const std::string& cmdLine)
340    {
341        std::vector<std::string> args;
342        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
343        for (unsigned i = 0; i < tokens.size(); ++i)
344            args.push_back(tokens[i]);
345        this->_parse(args, false);
346    }
347
348    /**
349    @brief
350        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
351    */
352    void CommandLineParser::_parseFile()
353    {
354        const std::string& filename = CommandLineParser::getValue("optionsFile").toString().toStdString();
355
356        // look for additional arguments in given file or start.ini as default
357        // They will not overwrite the arguments given directly
358        std::ifstream file;
359        file.open((PathConfig::getConfigPathString() + filename).c_str());
360        std::vector<std::string> args;
361        if (file)
362        {
363            while (!file.eof())
364            {
365                std::string line;
366                std::getline(file, line);
367                line = removeTrailingWhitespaces(line);
368                //if (!(line[0] == '#' || line[0] == '%'))
369                //{
370                SubString tokens(line, " ", " ", false, '\\', true, '"', true, '\0', '\0', false, '#');
371                for (unsigned i = 0; i < tokens.size(); ++i)
372                    if (tokens[i][0] != '#')
373                        args.push_back(tokens[i]);
374                //args.insert(args.end(), tokens.getAllStrings().begin(), tokens.getAllStrings().end());
375                //}
376            }
377            file.close();
378        }
379
380        _parse(args, true);
381    }
382}
Note: See TracBrowser for help on using the repository browser.