Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/doc/src/libraries/core/CommandLineParser.cc @ 7297

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

fixed lots of Doxygen warnings

Note: Doxygen prints a warning if only a part of the parameters of a function are documented.

Added documentation for missing parameters (as good as I could), removed documentation of obsolete parameters and fixed names of renamed parameters.
Some parameters are tagged with "FIXME", please replace this with an appropriate documentation if you know what it does.

  • 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_.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    @param bParsingFile
129        FIXME - add doc!
130    */
131    void CommandLineParser::_parse(const std::vector<std::string>& arguments, bool bParsingFile)
132    {
133        try
134        {
135            // why this? See bFirstTimeParse_ declaration.
136            if (bFirstTimeParse_)
137            {
138                // first shove all the shortcuts in a map
139                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
140                    it != cmdLineArgs_.end(); ++it)
141                {
142                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
143                        "Cannot have two command line shortcut with the same name.");
144                    if (!it->second->getShortcut().empty())
145                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
146                }
147                bFirstTimeParse_ = false;
148            }
149
150            std::string name;
151            std::string shortcut;
152            std::string value;
153            for (unsigned int i = 0; i < arguments.size(); ++i)
154            {
155                if (arguments[i].size() != 0)
156                {
157                    // sure not ""
158                    if (arguments[i][0] == '-')
159                    {
160                        // start with "-"
161                        if (arguments[i].size() == 1)
162                        {
163                            // argument[i] is "-", probably a minus sign
164                            value += "- ";
165                        }
166                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
167                        {
168                            // negative number as a value
169                            value += arguments[i] + ' ';
170                        }
171                        else
172                        {
173                            // can be shortcut or full name argument
174
175                            // save old data first
176                            value = removeTrailingWhitespaces(value);
177                            if (!name.empty())
178                            {
179                                checkFullArgument(name, value, bParsingFile);
180                                name.clear();
181                                assert(shortcut.empty());
182                            }
183                            else if (!shortcut.empty())
184                            {
185                                checkShortcut(shortcut, value, bParsingFile);
186                                shortcut.clear();
187                                assert(name.empty());
188                            }
189
190                            if (arguments[i][1] == '-')
191                            {
192                                // full name argument with "--name"
193                                name = arguments[i].substr(2);
194                            }
195                            else
196                            {
197                                // shortcut with "-s"
198                                shortcut = arguments[i].substr(1);
199                            }
200
201                            // reset value string
202                            value.clear();
203                        }
204                    }
205                    else
206                    {
207                        // value string
208
209                        if (name.empty() && shortcut.empty())
210                        {
211                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
212                        }
213
214                        // Concatenate strings as long as there's no new argument by "-" or "--"
215                        value += arguments[i] + ' ';
216                    }
217                }
218            }
219
220            // parse last argument
221            value = removeTrailingWhitespaces(value);
222            if (!name.empty())
223            {
224                checkFullArgument(name, value, bParsingFile);
225                assert(shortcut.empty());
226            }
227            else if (!shortcut.empty())
228            {
229                checkShortcut(shortcut, value, bParsingFile);
230                assert(name.empty());
231            }
232        }
233        catch (const ArgumentException& ex)
234        {
235            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
236            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
237            throw GeneralException("");
238        }
239    }
240
241    /**
242    @brief
243        Parses an argument based on its full name.
244    @param name
245        Full name of the argument
246    @param value
247        String containing the value
248    @param bParsingFile
249        FIXME - add doc!
250    */
251    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
252    {
253        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
254        if (it == cmdLineArgs_.end())
255            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
256
257        it->second->parse(value, bParsingFile);
258    }
259
260    /**
261    @brief
262        Parses an argument based on its shortcut.
263    @param shortcut
264        Shotcut to the argument
265    @param value
266        String containing the value
267    @param bParsingFile
268        FIXME - add doc!
269    */
270    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
271    {
272        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
273        if (it == cmdLineArgsShortcut_.end())
274            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
275
276        it->second->parse(value, bParsingFile);
277    }
278
279    std::string CommandLineParser::getUsageInformation()
280    {
281        CommandLineParser& inst = _getInstance();
282        std::ostringstream infoStr;
283
284        // determine maximum name size
285        size_t maxNameSize = 0;
286        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
287            it != inst.cmdLineArgs_.end(); ++it)
288        {
289            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
290        }
291
292        infoStr << std::endl;
293        infoStr << "Usage: orxonox [options]" << std::endl;
294        infoStr << "Available options:" << std::endl;
295
296        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
297            it != inst.cmdLineArgs_.end(); ++it)
298        {
299            if (!it->second->getShortcut().empty())
300                infoStr << " [-" << it->second->getShortcut() << "] ";
301            else
302                infoStr << "      ";
303            infoStr << "--" << it->second->getName() << ' ';
304            if (it->second->getValue().getType() != MT_Type::Bool)
305                infoStr << "ARG ";
306            else
307                infoStr << "    ";
308            // fill with the necessary amount of blanks
309            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
310            infoStr << ": " << it->second->getInformation();
311            infoStr << std::endl;
312        }
313        return infoStr.str();
314    }
315
316    /**
317    @brief
318        Retrieves a CommandLineArgument.
319        The method throws an exception if 'name' was not found or the value could not be converted.
320    @note
321        You shold of course not call this method before the command line has been parsed.
322    */
323    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
324    {
325        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
326        if (it == _getInstance().cmdLineArgs_.end())
327        {
328            ThrowException(Argument, "Could find command line argument '" + name + "'.");
329        }
330        else
331        {
332            return it->second;
333        }
334    }
335
336    /**
337    @brief
338        Parses only the command line for CommandLineArguments.
339    */
340    void CommandLineParser::_parseCommandLine(const std::string& cmdLine)
341    {
342        std::vector<std::string> args;
343        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
344        for (unsigned i = 0; i < tokens.size(); ++i)
345            args.push_back(tokens[i]);
346        this->_parse(args, false);
347    }
348
349    /**
350    @brief
351        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
352    */
353    void CommandLineParser::_parseFile()
354    {
355        const std::string& filename = CommandLineParser::getValue("optionsFile").getString();
356
357        // look for additional arguments in given file or start.ini as default
358        // They will not overwrite the arguments given directly
359        std::ifstream file;
360        file.open((PathConfig::getConfigPathString() + filename).c_str());
361        std::vector<std::string> args;
362        if (file)
363        {
364            while (!file.eof())
365            {
366                std::string line;
367                std::getline(file, line);
368                line = removeTrailingWhitespaces(line);
369                //if (!(line[0] == '#' || line[0] == '%'))
370                //{
371                SubString tokens(line, " ", " ", false, '\\', true, '"', true, '\0', '\0', false, '#');
372                for (unsigned i = 0; i < tokens.size(); ++i)
373                    if (tokens[i][0] != '#')
374                        args.push_back(tokens[i]);
375                //args.insert(args.end(), tokens.getAllStrings().begin(), tokens.getAllStrings().end());
376                //}
377            }
378            file.close();
379        }
380
381        _parse(args, true);
382    }
383}
Note: See TracBrowser for help on using the repository browser.