Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 12026 was 11099, checked in by muemart, 8 years ago

Fix loads of doxygen warnings and other documentation issues

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