Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8924 was 8858, checked in by landauf, 13 years ago

merged output branch back to trunk.

Changes:

  • you have to include util/Output.h instead of util/Debug.h
  • COUT(x) is now called orxout(level)
  • output levels are now defined by an enum instead of numbers. see util/Output.h for the definition
  • it's possible to use output contexts with orxout(level, context). see util/Output.h for some common contexts. you can define more contexts
  • you must use 'endl' at the end of an output message, '\n' does not flush the message

Output levels:

  • instead of COUT(0) use orxout()
  • instead of COUT(1) use orxout(user_error) or orxout(internal_error)
  • instead of COUT(2) use orxout(user_warning) or orxout(internal_warning)
  • instead of COUT(3) use orxout(user_status/user_info) or orxout(internal_status/internal_info)
  • instead of COUT(4) use orxout(verbose)
  • instead of COUT(5) use orxout(verbose_more)
  • instead of COUT(6) use orxout(verbose_ultra)

Guidelines:

  • user_* levels are for the user, visible in the console and the log-file
  • internal_* levels are for developers, visible in the log-file
  • verbose_* levels are for debugging, only visible if the context of the output is activated

Usage in C++:

  • orxout() << "message" << endl;
  • orxout(level) << "message" << endl;
  • orxout(level, context) << "message" << endl;

Usage in Lua:

  • orxout("message")
  • orxout(orxonox.level.levelname, "message")
  • orxout(orxonox.level.levelname, "context", "message")

Usage in Tcl (and in the in-game-console):

  • orxout levelname message
  • orxout_context levelname context message
  • shortcuts: log message, error message, warning message, status message, info message, debug message
  • 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"
[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        {
[8858]231            orxout(user_error) << "Could not parse command line: " << ex.what() << endl;
232            orxout(user_error) << CommandLineParser::getUsageInformation() << 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
[8858]288        infoStr << endl;
289        infoStr << "Usage: orxonox [options]" << endl;
290        infoStr << "Available options:" << endl;
[3280]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();
[8858]307            infoStr << endl;
[3280]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.