Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/core/config/CommandLineParser.cc @ 10471

Last change on this file since 10471 was 9560, checked in by landauf, 12 years ago

moved CommandLineParser to core/config/

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