Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7284 was 7284, checked in by landauf, 14 years ago

merged consolecommands3 branch back to trunk.

note: the console command interface has changed completely, but the documentation is not yet up to date. just copy an existing command and change it to your needs, it's pretty self-explanatory. also the include files related to console commands are now located in core/command/. in the game it should work exactly like before, except for some changes in the auto-completion.

  • Property svn:eol-style set to native
File size: 13.2 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_.getType() == MT_Type::Bool)
57        {
58            // simulate command line switch
59            bool temp;
60            if (convertValue(&temp, value))
61            {
62                this->bHasDefaultValue_ = false;
63                this->value_ = temp;
64            }
65            else if (value.empty())
66            {
67                this->bHasDefaultValue_ = false;
68                this->value_ = true;
69            }
70            else
71                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
72        }
73        else
74        {
75            if (!value_.setValue(value))
76            {
77                value_.setValue(defaultValue_);
78                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
79            }
80            else
81                this->bHasDefaultValue_ = false;
82        }
83    }
84
85
86    /**
87    @brief
88        Destructor destroys all CommandLineArguments with it.
89    */
90    CommandLineParser::~CommandLineParser()
91    {
92        CommandLineParser::destroyAllArguments();
93    }
94
95    /**
96    @brief
97        Returns a unique instance (Meyers Singleton).
98    */
99    CommandLineParser& CommandLineParser::_getInstance()
100    {
101        static CommandLineParser instance;
102        return instance;
103    }
104
105    /**
106    @brief
107        Destroys all command line arguments. This should be called at the end
108        of main. Do not use before that.
109    */
110    void CommandLineParser::destroyAllArguments()
111    {
112        for (std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.begin();
113            it != _getInstance().cmdLineArgs_.end(); ++it)
114            delete it->second;
115        _getInstance().cmdLineArgs_.clear();
116    }
117
118    /**
119    @brief
120        Reads the command line parses the values of each argument.
121        It is then stored in the corresponding CommandLineArgument.
122    @note
123        The reason that you have to provide the string to be parsed as
124        space separted list is because of argc and argv. If you only have
125        a whole string, simply use getAllStrings() of SubString.
126    @param arguments
127        Vector of space separated strings.
128    */
129    void CommandLineParser::_parse(const std::vector<std::string>& arguments, bool bParsingFile)
130    {
131        try
132        {
133            // why this? See bFirstTimeParse_ declaration.
134            if (bFirstTimeParse_)
135            {
136                // first shove all the shortcuts in a map
137                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
138                    it != cmdLineArgs_.end(); ++it)
139                {
140                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
141                        "Cannot have two command line shortcut with the same name.");
142                    if (!it->second->getShortcut().empty())
143                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
144                }
145                bFirstTimeParse_ = false;
146            }
147
148            std::string name;
149            std::string shortcut;
150            std::string value;
151            for (unsigned int i = 0; i < arguments.size(); ++i)
152            {
153                if (arguments[i].size() != 0)
154                {
155                    // sure not ""
156                    if (arguments[i][0] == '-')
157                    {
158                        // start with "-"
159                        if (arguments[i].size() == 1)
160                        {
161                            // argument[i] is "-", probably a minus sign
162                            value += "- ";
163                        }
164                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
165                        {
166                            // negative number as a value
167                            value += arguments[i] + ' ';
168                        }
169                        else
170                        {
171                            // can be shortcut or full name argument
172
173                            // save old data first
174                            value = removeTrailingWhitespaces(value);
175                            if (!name.empty())
176                            {
177                                checkFullArgument(name, value, bParsingFile);
178                                name.clear();
179                                assert(shortcut.empty());
180                            }
181                            else if (!shortcut.empty())
182                            {
183                                checkShortcut(shortcut, value, bParsingFile);
184                                shortcut.clear();
185                                assert(name.empty());
186                            }
187
188                            if (arguments[i][1] == '-')
189                            {
190                                // full name argument with "--name"
191                                name = arguments[i].substr(2);
192                            }
193                            else
194                            {
195                                // shortcut with "-s"
196                                shortcut = arguments[i].substr(1);
197                            }
198
199                            // reset value string
200                            value.clear();
201                        }
202                    }
203                    else
204                    {
205                        // value string
206
207                        if (name.empty() && shortcut.empty())
208                        {
209                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
210                        }
211
212                        // Concatenate strings as long as there's no new argument by "-" or "--"
213                        value += arguments[i] + ' ';
214                    }
215                }
216            }
217
218            // parse last argument
219            value = removeTrailingWhitespaces(value);
220            if (!name.empty())
221            {
222                checkFullArgument(name, value, bParsingFile);
223                assert(shortcut.empty());
224            }
225            else if (!shortcut.empty())
226            {
227                checkShortcut(shortcut, value, bParsingFile);
228                assert(name.empty());
229            }
230        }
231        catch (const ArgumentException& ex)
232        {
233            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
234            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
235            throw GeneralException("");
236        }
237    }
238
239    /**
240    @brief
241        Parses an argument based on its full name.
242    @param name
243        Full name of the argument
244    @param value
245        String containing the value
246    */
247    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
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, bParsingFile);
254    }
255
256    /**
257    @brief
258        Parses an argument based on its shortcut.
259    @param shortcut
260        Shotcut to the argument
261    @param value
262        String containing the value
263    */
264    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
265    {
266        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
267        if (it == cmdLineArgsShortcut_.end())
268            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
269
270        it->second->parse(value, bParsingFile);
271    }
272
273    std::string CommandLineParser::getUsageInformation()
274    {
275        CommandLineParser& inst = _getInstance();
276        std::ostringstream infoStr;
277
278        // determine maximum name size
279        size_t maxNameSize = 0;
280        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
281            it != inst.cmdLineArgs_.end(); ++it)
282        {
283            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
284        }
285
286        infoStr << std::endl;
287        infoStr << "Usage: orxonox [options]" << std::endl;
288        infoStr << "Available options:" << std::endl;
289
290        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
291            it != inst.cmdLineArgs_.end(); ++it)
292        {
293            if (!it->second->getShortcut().empty())
294                infoStr << " [-" << it->second->getShortcut() << "] ";
295            else
296                infoStr << "      ";
297            infoStr << "--" << it->second->getName() << ' ';
298            if (it->second->getValue().getType() != MT_Type::Bool)
299                infoStr << "ARG ";
300            else
301                infoStr << "    ";
302            // fill with the necessary amount of blanks
303            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
304            infoStr << ": " << it->second->getInformation();
305            infoStr << std::endl;
306        }
307        return infoStr.str();
308    }
309
310    /**
311    @brief
312        Retrieves a CommandLineArgument.
313        The method throws an exception if 'name' was not found or the value could not be converted.
314    @note
315        You shold of course not call this method before the command line has been parsed.
316    */
317    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
318    {
319        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
320        if (it == _getInstance().cmdLineArgs_.end())
321        {
322            ThrowException(Argument, "Could find command line argument '" + name + "'.");
323        }
324        else
325        {
326            return it->second;
327        }
328    }
329
330    /**
331    @brief
332        Parses only the command line for CommandLineArguments.
333    */
334    void CommandLineParser::_parseCommandLine(const std::string& cmdLine)
335    {
336        std::vector<std::string> args;
337        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
338        for (unsigned i = 0; i < tokens.size(); ++i)
339            args.push_back(tokens[i]);
340        this->_parse(args, false);
341    }
342
343    /**
344    @brief
345        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
346    */
347    void CommandLineParser::_parseFile()
348    {
349        const std::string& filename = CommandLineParser::getValue("optionsFile").getString();
350
351        // look for additional arguments in given file or start.ini as default
352        // They will not overwrite the arguments given directly
353        std::ifstream file;
354        file.open((PathConfig::getConfigPathString() + filename).c_str());
355        std::vector<std::string> args;
356        if (file)
357        {
358            while (!file.eof())
359            {
360                std::string line;
361                std::getline(file, line);
362                line = removeTrailingWhitespaces(line);
363                //if (!(line[0] == '#' || line[0] == '%'))
364                //{
365                SubString tokens(line, " ", " ", false, '\\', true, '"', true, '\0', '\0', false, '#');
366                for (unsigned i = 0; i < tokens.size(); ++i)
367                    if (tokens[i][0] != '#')
368                        args.push_back(tokens[i]);
369                //args.insert(args.end(), tokens.getAllStrings().begin(), tokens.getAllStrings().end());
370                //}
371            }
372            file.close();
373        }
374
375        _parse(args, true);
376    }
377}
Note: See TracBrowser for help on using the repository browser.