Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11071 was 11071, checked in by landauf, 8 years ago

merged branch cpp11_v3 back to trunk

  • Property svn:eol-style set to native
File size: 11.7 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
[7401]220    @param bParsingFile
221        Parsing a file or the command line itself
[1664]222    */
[8729]223    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value)
[1663]224    {
[2087]225        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
[1663]226        if (it == cmdLineArgs_.end())
227            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
228
[8729]229        it->second->parse(value);
[1663]230    }
231
[1664]232    /**
233    @brief
234        Parses an argument based on its shortcut.
235    @param shortcut
[7401]236        Shortcut to the argument
[1664]237    @param value
238        String containing the value
[7401]239    @param bParsingFile
240        Parsing a file or the command line itself
[1664]241    */
[8729]242    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value)
[1663]243    {
[2087]244        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
[1663]245        if (it == cmdLineArgsShortcut_.end())
246            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
247
[8729]248        it->second->parse(value);
[1663]249    }
[1664]250
[6021]251    std::string CommandLineParser::getUsageInformation()
[1664]252    {
[10542]253        CommandLineParser& inst = getInstance();
[3280]254        std::ostringstream infoStr;
255
256        // determine maximum name size
257        size_t maxNameSize = 0;
[11071]258        for (const auto& mapEntry : inst.cmdLineArgs_)
[1664]259        {
[11071]260            maxNameSize = std::max(mapEntry.second->getName().size(), maxNameSize);
[1664]261        }
[3280]262
[8858]263        infoStr << endl;
264        infoStr << "Usage: orxonox [options]" << endl;
265        infoStr << "Available options:" << endl;
[3280]266
[11071]267        for (const auto& mapEntry : inst.cmdLineArgs_)
[3280]268        {
[11071]269            if (!mapEntry.second->getShortcut().empty())
270                infoStr << " [-" << mapEntry.second->getShortcut() << "] ";
[3280]271            else
272                infoStr << "      ";
[11071]273            infoStr << "--" << mapEntry.second->getName() << ' ';
274            if (mapEntry.second->getValue().isType<bool>())
[9550]275                infoStr << "    ";
276            else
[3280]277                infoStr << "ARG ";
278            // fill with the necessary amount of blanks
[11071]279            infoStr << std::string(maxNameSize - mapEntry.second->getName().size(), ' ');
280            infoStr << ": " << mapEntry.second->getInformation();
[8858]281            infoStr << endl;
[3280]282        }
283        return infoStr.str();
[1664]284    }
285
[7401]286    void CommandLineParser::generateDoc(std::ofstream& file)
287    {
288        file << "/** @page cmdargspage Command Line Arguments Reference" << endl;
289        file << "    @verbatim"; /*no endl*/
290        file << getUsageInformation(); /*no endl*/
291        file << "    @endverbatim" << endl;
292        file << "*/" << endl;
293    }
294
[2087]295    /**
296    @brief
297        Retrieves a CommandLineArgument.
298        The method throws an exception if 'name' was not found or the value could not be converted.
299    @note
[7401]300        You should of course not call this method before the command line has been parsed.
[2087]301    */
[6021]302    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
[2087]303    {
[10542]304        std::map<std::string, CommandLineArgument*>::const_iterator it = getInstance().cmdLineArgs_.find(name);
305        if (it == getInstance().cmdLineArgs_.end())
[2087]306        {
307            ThrowException(Argument, "Could find command line argument '" + name + "'.");
308        }
309        else
310        {
311            return it->second;
312        }
313    }
[10345]314
315    /**
316    @brief
317        Adds a new CommandLineArgument to the internal map.
318        Note that only such arguments are actually valid.
319    */
320    void CommandLineParser::addArgument(CommandLineArgument* argument)
321    {
[10542]322        OrxAssert(!getInstance().existsArgument(argument->getName()),
[10345]323            "Cannot add a command line argument with name '" + argument->getName() + "' twice.");
324        OrxAssert(!argument->getDefaultValue().isType<bool>() || argument->getDefaultValue().get<bool>() != true,
325               "Boolean command line arguments with positive default values are not supported." << endl
326            << "Please use SetCommandLineSwitch and adjust your argument: " << argument->getName());
327
[10542]328        getInstance().cmdLineArgs_[argument->getName()] = argument;
[10345]329    }
[10404]330
331    /**
332     * @brief Removes a CommandLineArgument from the internal map.
333     */
334    void CommandLineParser::removeArgument(CommandLineArgument* argument)
335    {
[10542]336        getInstance().cmdLineArgs_.erase(argument->getName());
[10404]337    }
[1663]338}
Note: See TracBrowser for help on using the repository browser.