Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core7/src/libraries/core/commandline/CommandLineParser.cc @ 10513

Last change on this file since 10513 was 10509, checked in by landauf, 9 years ago

moved static application paths (root, executable, modules) into new class named ApplicationPaths
moved configurable data paths (data, log, config) into new class named ConfigurablePaths
removed PathConfig

  • Property svn:eol-style set to native
File size: 12.6 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/Output.h"
36#include "util/Exception.h"
37#include "util/StringUtils.h"
38#include "util/SubString.h"
39
40namespace orxonox
41{
42    /**
43    @brief
44        Parses a value string for a command line argument.
45        It simply uses convertValue(Output, Input) to do that.
46        Bools are treated specially. That is necessary
47        so that you can have simple command line switches.
48    */
49    void CommandLineArgument::parse(const std::string& value)
50    {
51        if (value_.isType<bool>())
52        {
53            // simulate command line switch
54            bool temp;
55            if (convertValue(&temp, value))
56            {
57                this->bHasDefaultValue_ = false;
58                this->value_ = temp;
59            }
60            else if (value.empty())
61            {
62                this->bHasDefaultValue_ = false;
63                this->value_ = true;
64            }
65            else
66                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
67        }
68        else
69        {
70            if (!value_.set(value))
71            {
72                value_.set(defaultValue_);
73                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
74            }
75            else
76                this->bHasDefaultValue_ = false;
77        }
78    }
79
80
81    /**
82    @brief
83        Destructor destroys all CommandLineArguments with it.
84    */
85    CommandLineParser::~CommandLineParser()
86    {
87        CommandLineParser::destroyAllArguments();
88    }
89
90    /**
91    @brief
92        Returns a unique instance (Meyers Singleton).
93    */
94    CommandLineParser& CommandLineParser::_getInstance()
95    {
96        static CommandLineParser instance;
97        return instance;
98    }
99
100    /**
101    @brief
102        Destroys all command line arguments. This should be called at the end
103        of main. Do not use before that.
104    */
105    void CommandLineParser::destroyAllArguments()
106    {
107        for (std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.begin();
108            it != _getInstance().cmdLineArgs_.end(); ++it)
109            delete it->second;
110        _getInstance().cmdLineArgs_.clear();
111    }
112
113    /** Parses the command line string for arguments and stores these.
114    @note
115        The reason that you have to provide the string to be parsed as
116        space separated list is because of argc and argv. If you only have
117        a whole string, simply use getAllStrings() of SubString.
118    @param cmdLine
119        Command line string WITHOUT the execution path.
120    */
121    void CommandLineParser::_parse(const std::string& cmdLine)
122    {
123        std::vector<std::string> arguments;
124        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
125        for (unsigned i = 0; i < tokens.size(); ++i)
126            arguments.push_back(tokens[i]);
127
128        try
129        {
130            // why this? See bFirstTimeParse_ declaration.
131            if (bFirstTimeParse_)
132            {
133                // first shove all the shortcuts in a map
134                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
135                    it != cmdLineArgs_.end(); ++it)
136                {
137                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
138                        "Cannot have two command line shortcut with the same name.");
139                    if (!it->second->getShortcut().empty())
140                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
141                }
142                bFirstTimeParse_ = false;
143            }
144
145            std::string name;
146            std::string shortcut;
147            std::string value;
148            for (unsigned int i = 0; i < arguments.size(); ++i)
149            {
150                if (arguments[i].size() != 0)
151                {
152                    // sure not ""
153                    if (arguments[i][0] == '-')
154                    {
155                        // start with "-"
156                        if (arguments[i].size() == 1)
157                        {
158                            // argument[i] is "-", probably a minus sign
159                            value += "- ";
160                        }
161                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
162                        {
163                            // negative number as a value
164                            value += arguments[i] + ' ';
165                        }
166                        else
167                        {
168                            // can be shortcut or full name argument
169
170                            // save old data first
171                            value = removeTrailingWhitespaces(value);
172                            if (!name.empty())
173                            {
174                                checkFullArgument(name, value);
175                                name.clear();
176                                assert(shortcut.empty());
177                            }
178                            else if (!shortcut.empty())
179                            {
180                                checkShortcut(shortcut, value);
181                                shortcut.clear();
182                                assert(name.empty());
183                            }
184
185                            if (arguments[i][1] == '-')
186                            {
187                                // full name argument with "--name"
188                                name = arguments[i].substr(2);
189                            }
190                            else
191                            {
192                                // shortcut with "-s"
193                                shortcut = arguments[i].substr(1);
194                            }
195
196                            // reset value string
197                            value.clear();
198                        }
199                    }
200                    else
201                    {
202                        // value string
203
204                        if (name.empty() && shortcut.empty())
205                        {
206                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
207                        }
208
209                        // Concatenate strings as long as there's no new argument by "-" or "--"
210                        value += arguments[i] + ' ';
211                    }
212                }
213            }
214
215            // parse last argument
216            value = removeTrailingWhitespaces(value);
217            if (!name.empty())
218            {
219                checkFullArgument(name, value);
220                assert(shortcut.empty());
221            }
222            else if (!shortcut.empty())
223            {
224                checkShortcut(shortcut, value);
225                assert(name.empty());
226            }
227        }
228        catch (const ArgumentException& ex)
229        {
230            orxout(user_error) << "Could not parse command line: " << ex.what() << endl;
231            orxout(user_error) << CommandLineParser::getUsageInformation() << endl;
232            throw GeneralException("");
233        }
234    }
235
236    /**
237    @brief
238        Parses an argument based on its full name.
239    @param name
240        Full name of the argument
241    @param value
242        String containing the value
243    @param bParsingFile
244        Parsing a file or the command line itself
245    */
246    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value)
247    {
248        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
249        if (it == cmdLineArgs_.end())
250            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
251
252        it->second->parse(value);
253    }
254
255    /**
256    @brief
257        Parses an argument based on its shortcut.
258    @param shortcut
259        Shortcut to the argument
260    @param value
261        String containing the value
262    @param bParsingFile
263        Parsing a file or the command line itself
264    */
265    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value)
266    {
267        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
268        if (it == cmdLineArgsShortcut_.end())
269            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
270
271        it->second->parse(value);
272    }
273
274    std::string CommandLineParser::getUsageInformation()
275    {
276        CommandLineParser& inst = _getInstance();
277        std::ostringstream infoStr;
278
279        // determine maximum name size
280        size_t maxNameSize = 0;
281        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
282            it != inst.cmdLineArgs_.end(); ++it)
283        {
284            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
285        }
286
287        infoStr << endl;
288        infoStr << "Usage: orxonox [options]" << endl;
289        infoStr << "Available options:" << endl;
290
291        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
292            it != inst.cmdLineArgs_.end(); ++it)
293        {
294            if (!it->second->getShortcut().empty())
295                infoStr << " [-" << it->second->getShortcut() << "] ";
296            else
297                infoStr << "      ";
298            infoStr << "--" << it->second->getName() << ' ';
299            if (it->second->getValue().isType<bool>())
300                infoStr << "    ";
301            else
302                infoStr << "ARG ";
303            // fill with the necessary amount of blanks
304            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
305            infoStr << ": " << it->second->getInformation();
306            infoStr << endl;
307        }
308        return infoStr.str();
309    }
310
311    void CommandLineParser::generateDoc(std::ofstream& file)
312    {
313        file << "/** @page cmdargspage Command Line Arguments Reference" << endl;
314        file << "    @verbatim"; /*no endl*/
315        file << getUsageInformation(); /*no endl*/
316        file << "    @endverbatim" << endl;
317        file << "*/" << endl;
318    }
319
320    /**
321    @brief
322        Retrieves a CommandLineArgument.
323        The method throws an exception if 'name' was not found or the value could not be converted.
324    @note
325        You should of course not call this method before the command line has been parsed.
326    */
327    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
328    {
329        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
330        if (it == _getInstance().cmdLineArgs_.end())
331        {
332            ThrowException(Argument, "Could find command line argument '" + name + "'.");
333        }
334        else
335        {
336            return it->second;
337        }
338    }
339
340    /**
341    @brief
342        Adds a new CommandLineArgument to the internal map.
343        Note that only such arguments are actually valid.
344    */
345    void CommandLineParser::addArgument(CommandLineArgument* argument)
346    {
347        OrxAssert(!_getInstance().existsArgument(argument->getName()),
348            "Cannot add a command line argument with name '" + argument->getName() + "' twice.");
349        OrxAssert(!argument->getDefaultValue().isType<bool>() || argument->getDefaultValue().get<bool>() != true,
350               "Boolean command line arguments with positive default values are not supported." << endl
351            << "Please use SetCommandLineSwitch and adjust your argument: " << argument->getName());
352
353        _getInstance().cmdLineArgs_[argument->getName()] = argument;
354    }
355
356    /**
357     * @brief Removes a CommandLineArgument from the internal map.
358     */
359    void CommandLineParser::removeArgument(CommandLineArgument* argument)
360    {
361        _getInstance().cmdLineArgs_.erase(argument->getName());
362    }
363}
Note: See TracBrowser for help on using the repository browser.