Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8828 was 8729, checked in by rgrieder, 13 years ago

Merged unity_build branch back to trunk.

Features:

  • Implemented fully automatic build units to speed up compilation if requested
  • Added DOUT macro for quick debug output
  • Activated text colouring in the POSIX IOConsole
  • DeclareToluaInterface is not necessary anymore

Improvements:

  • Output levels now change appropriately when switch back and forth from dev mode
  • Log level for the file output is now also correct during startup
  • Removed some header file dependencies in core and tools to speed up compilation

no more file for command line options

  • Improved util::tribool by adapting some concepts from boost::tribool

Regressions:

  • It is not possible anymore to specify command line arguments in an extra file because we've got config values for that purpose.
  • 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"
35#include "util/Debug.h"
36#include "util/Exception.h"
[3280]37#include "util/StringUtils.h"
[2103]38#include "util/SubString.h"
[5929]39#include "PathConfig.h"
[1690]40
[1663]41namespace orxonox
42{
[1664]43    /**
44    @brief
[2087]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    */
[8729]50    void CommandLineArgument::parse(const std::string& value)
[2087]51    {
[3280]52        if (value_.getType() == MT_Type::Bool)
[2087]53        {
54            // simulate command line switch
55            bool temp;
56            if (convertValue(&temp, value))
57            {
58                this->bHasDefaultValue_ = false;
59                this->value_ = temp;
60            }
[6417]61            else if (value.empty())
[2087]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_.setValue(value))
72            {
73                value_.setValue(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
[1664]84        Destructor destroys all CommandLineArguments with it.
85    */
[6021]86    CommandLineParser::~CommandLineParser()
[1663]87    {
[6021]88        CommandLineParser::destroyAllArguments();
[1663]89    }
[1664]90
91    /**
92    @brief
93        Returns a unique instance (Meyers Singleton).
94    */
[6021]95    CommandLineParser& CommandLineParser::_getInstance()
[1663]96    {
[6021]97        static CommandLineParser instance;
[1663]98        return instance;
99    }
100
[1664]101    /**
102    @brief
[2662]103        Destroys all command line arguments. This should be called at the end
104        of main. Do not use before that.
105    */
[6021]106    void CommandLineParser::destroyAllArguments()
[2662]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
[8729]114    /** Parses the command line string for arguments and stores these.
[1664]115    @note
116        The reason that you have to provide the string to be parsed as
[8729]117        space separated list is because of argc and argv. If you only have
[1664]118        a whole string, simply use getAllStrings() of SubString.
[8729]119    @param cmdLine
120        Command line string WITHOUT the execution path.
[1664]121    */
[8729]122    void CommandLineParser::_parse(const std::string& cmdLine)
[1663]123    {
[8729]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
[3280]129        try
[1663]130        {
[3280]131            // why this? See bFirstTimeParse_ declaration.
132            if (bFirstTimeParse_)
[1663]133            {
[3280]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.");
[6417]140                    if (!it->second->getShortcut().empty())
[3280]141                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
142                }
143                bFirstTimeParse_ = false;
[1663]144            }
145
[3280]146            std::string name;
147            std::string shortcut;
148            std::string value;
149            for (unsigned int i = 0; i < arguments.size(); ++i)
[1663]150            {
[3280]151                if (arguments[i].size() != 0)
[1663]152                {
[3280]153                    // sure not ""
154                    if (arguments[i][0] == '-')
[1663]155                    {
[3280]156                        // start with "-"
157                        if (arguments[i].size() == 1)
[1664]158                        {
[3280]159                            // argument[i] is "-", probably a minus sign
160                            value += "- ";
[1664]161                        }
[3280]162                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
[1664]163                        {
[3280]164                            // negative number as a value
[6417]165                            value += arguments[i] + ' ';
[1664]166                        }
[3280]167                        else
168                        {
169                            // can be shortcut or full name argument
[1664]170
[3280]171                            // save old data first
172                            value = removeTrailingWhitespaces(value);
[6417]173                            if (!name.empty())
[3280]174                            {
[8729]175                                checkFullArgument(name, value);
[6417]176                                name.clear();
177                                assert(shortcut.empty());
[3280]178                            }
[6417]179                            else if (!shortcut.empty())
[3280]180                            {
[8729]181                                checkShortcut(shortcut, value);
[6417]182                                shortcut.clear();
183                                assert(name.empty());
[3280]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
[6417]198                            value.clear();
[1664]199                        }
[3280]200                    }
201                    else
202                    {
203                        // value string
204
[6417]205                        if (name.empty() && shortcut.empty())
[1664]206                        {
[3280]207                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
[1664]208                        }
209
[3280]210                        // Concatenate strings as long as there's no new argument by "-" or "--"
211                        value += arguments[i] + ' ';
[1663]212                    }
213                }
[3280]214            }
[1664]215
[3280]216            // parse last argument
217            value = removeTrailingWhitespaces(value);
[6417]218            if (!name.empty())
[3280]219            {
[8729]220                checkFullArgument(name, value);
[6417]221                assert(shortcut.empty());
[1663]222            }
[6417]223            else if (!shortcut.empty())
[3280]224            {
[8729]225                checkShortcut(shortcut, value);
[6417]226                assert(name.empty());
[3280]227            }
[1663]228        }
[3280]229        catch (const ArgumentException& ex)
[1664]230        {
[8729]231            COUT(0) << "Could not parse command line: " << ex.what() << std::endl;
[6021]232            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
[3280]233            throw GeneralException("");
[1664]234        }
[1663]235    }
236
[1664]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
[7401]244    @param bParsingFile
245        Parsing a file or the command line itself
[1664]246    */
[8729]247    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value)
[1663]248    {
[2087]249        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
[1663]250        if (it == cmdLineArgs_.end())
251            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
252
[8729]253        it->second->parse(value);
[1663]254    }
255
[1664]256    /**
257    @brief
258        Parses an argument based on its shortcut.
259    @param shortcut
[7401]260        Shortcut to the argument
[1664]261    @param value
262        String containing the value
[7401]263    @param bParsingFile
264        Parsing a file or the command line itself
[1664]265    */
[8729]266    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value)
[1663]267    {
[2087]268        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
[1663]269        if (it == cmdLineArgsShortcut_.end())
270            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
271
[8729]272        it->second->parse(value);
[1663]273    }
[1664]274
[6021]275    std::string CommandLineParser::getUsageInformation()
[1664]276    {
[6021]277        CommandLineParser& inst = _getInstance();
[3280]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)
[1664]284        {
[3280]285            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
[1664]286        }
[3280]287
[5929]288        infoStr << std::endl;
[3280]289        infoStr << "Usage: orxonox [options]" << std::endl;
290        infoStr << "Available options:" << std::endl;
291
292        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
293            it != inst.cmdLineArgs_.end(); ++it)
294        {
[6417]295            if (!it->second->getShortcut().empty())
[3280]296                infoStr << " [-" << it->second->getShortcut() << "] ";
297            else
298                infoStr << "      ";
[6417]299            infoStr << "--" << it->second->getName() << ' ';
[3280]300            if (it->second->getValue().getType() != MT_Type::Bool)
301                infoStr << "ARG ";
302            else
303                infoStr << "    ";
304            // fill with the necessary amount of blanks
305            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
306            infoStr << ": " << it->second->getInformation();
307            infoStr << std::endl;
308        }
309        return infoStr.str();
[1664]310    }
311
[7401]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
[2087]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
[7401]326        You should of course not call this method before the command line has been parsed.
[2087]327    */
[6021]328    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
[2087]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    }
[1663]340}
Note: See TracBrowser for help on using the repository browser.