Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: sandbox_qt/src/libraries/core/CommandLineParser.cc @ 7428

Last change on this file since 7428 was 7428, checked in by rgrieder, 14 years ago

Removed conversion functions. Use QVariant instead.

  • Property svn:eol-style set to native
File size: 13.4 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/Debug.h"
35#include "util/Exception.h"
[3280]36#include "util/StringUtils.h"
[2103]37#include "util/SubString.h"
[5929]38#include "PathConfig.h"
[1690]39
[1663]40namespace orxonox
41{
[3280]42    SetCommandLineOnlyArgument(optionsFile, "start.ini").shortcut("o");
[2103]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    */
[3280]51    void CommandLineArgument::parse(const std::string& value, bool bParsingFile)
[2087]52    {
[3280]53        if (bParsingFile && this->bCommandLineOnly_)
54            ThrowException(Argument, "Command line argument '" + getName() + "' is not allowed in files.");
[7424]55        if (value_.type() == QVariant::Bool)
[2087]56        {
[7424]57            // Command line switch activated
58            this->bHasDefaultValue_ = false;
59            this->value_ = true;
[2087]60        }
61        else
62        {
[7424]63            QVariant temp(QString::fromStdString(value));
64            if (!temp.convert(value_.type()))
[2087]65                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
66            else
[7424]67            {
[2087]68                this->bHasDefaultValue_ = false;
[7424]69                this->value_ = temp;
70            }
[2087]71        }
72    }
73
74
75    /**
76    @brief
[1664]77        Destructor destroys all CommandLineArguments with it.
78    */
[6021]79    CommandLineParser::~CommandLineParser()
[1663]80    {
[6021]81        CommandLineParser::destroyAllArguments();
[1663]82    }
[1664]83
84    /**
85    @brief
86        Returns a unique instance (Meyers Singleton).
87    */
[6021]88    CommandLineParser& CommandLineParser::_getInstance()
[1663]89    {
[6021]90        static CommandLineParser instance;
[1663]91        return instance;
92    }
93
[1664]94    /**
95    @brief
[2662]96        Destroys all command line arguments. This should be called at the end
97        of main. Do not use before that.
98    */
[6021]99    void CommandLineParser::destroyAllArguments()
[2662]100    {
101        for (std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.begin();
102            it != _getInstance().cmdLineArgs_.end(); ++it)
103            delete it->second;
104        _getInstance().cmdLineArgs_.clear();
105    }
106
107    /**
108    @brief
[1664]109        Reads the command line parses the values of each argument.
110        It is then stored in the corresponding CommandLineArgument.
111    @note
112        The reason that you have to provide the string to be parsed as
113        space separted list is because of argc and argv. If you only have
114        a whole string, simply use getAllStrings() of SubString.
115    @param arguments
116        Vector of space separated strings.
[7401]117    @param bParsingFile
118        Parsing a file or the command line itself
[1664]119    */
[6021]120    void CommandLineParser::_parse(const std::vector<std::string>& arguments, bool bParsingFile)
[1663]121    {
[3280]122        try
[1663]123        {
[3280]124            // why this? See bFirstTimeParse_ declaration.
125            if (bFirstTimeParse_)
[1663]126            {
[3280]127                // first shove all the shortcuts in a map
128                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
129                    it != cmdLineArgs_.end(); ++it)
130                {
131                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
132                        "Cannot have two command line shortcut with the same name.");
[6417]133                    if (!it->second->getShortcut().empty())
[3280]134                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
135                }
136                bFirstTimeParse_ = false;
[1663]137            }
138
[3280]139            std::string name;
140            std::string shortcut;
141            std::string value;
142            for (unsigned int i = 0; i < arguments.size(); ++i)
[1663]143            {
[3280]144                if (arguments[i].size() != 0)
[1663]145                {
[3280]146                    // sure not ""
147                    if (arguments[i][0] == '-')
[1663]148                    {
[3280]149                        // start with "-"
150                        if (arguments[i].size() == 1)
[1664]151                        {
[3280]152                            // argument[i] is "-", probably a minus sign
153                            value += "- ";
[1664]154                        }
[3280]155                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
[1664]156                        {
[3280]157                            // negative number as a value
[6417]158                            value += arguments[i] + ' ';
[1664]159                        }
[3280]160                        else
161                        {
162                            // can be shortcut or full name argument
[1664]163
[3280]164                            // save old data first
165                            value = removeTrailingWhitespaces(value);
[6417]166                            if (!name.empty())
[3280]167                            {
168                                checkFullArgument(name, value, bParsingFile);
[6417]169                                name.clear();
170                                assert(shortcut.empty());
[3280]171                            }
[6417]172                            else if (!shortcut.empty())
[3280]173                            {
174                                checkShortcut(shortcut, value, bParsingFile);
[6417]175                                shortcut.clear();
176                                assert(name.empty());
[3280]177                            }
178
179                            if (arguments[i][1] == '-')
180                            {
181                                // full name argument with "--name"
182                                name = arguments[i].substr(2);
183                            }
184                            else
185                            {
186                                // shortcut with "-s"
187                                shortcut = arguments[i].substr(1);
188                            }
189
190                            // reset value string
[6417]191                            value.clear();
[1664]192                        }
[3280]193                    }
194                    else
195                    {
196                        // value string
197
[6417]198                        if (name.empty() && shortcut.empty())
[1664]199                        {
[3280]200                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
[1664]201                        }
202
[3280]203                        // Concatenate strings as long as there's no new argument by "-" or "--"
204                        value += arguments[i] + ' ';
[1663]205                    }
206                }
[3280]207            }
[1664]208
[3280]209            // parse last argument
210            value = removeTrailingWhitespaces(value);
[6417]211            if (!name.empty())
[3280]212            {
213                checkFullArgument(name, value, bParsingFile);
[6417]214                assert(shortcut.empty());
[1663]215            }
[6417]216            else if (!shortcut.empty())
[3280]217            {
218                checkShortcut(shortcut, value, bParsingFile);
[6417]219                assert(name.empty());
[3280]220            }
[1663]221        }
[3280]222        catch (const ArgumentException& ex)
[1664]223        {
[3280]224            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
[6021]225            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
[3280]226            throw GeneralException("");
[1664]227        }
[1663]228    }
229
[1664]230    /**
231    @brief
232        Parses an argument based on its full name.
233    @param name
234        Full name of the argument
235    @param value
236        String containing the value
[7401]237    @param bParsingFile
238        Parsing a file or the command line itself
[1664]239    */
[6021]240    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
[1663]241    {
[2087]242        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
[1663]243        if (it == cmdLineArgs_.end())
244            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
245
[3280]246        it->second->parse(value, bParsingFile);
[1663]247    }
248
[1664]249    /**
250    @brief
251        Parses an argument based on its shortcut.
252    @param shortcut
[7401]253        Shortcut to the argument
[1664]254    @param value
255        String containing the value
[7401]256    @param bParsingFile
257        Parsing a file or the command line itself
[1664]258    */
[6021]259    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
[1663]260    {
[2087]261        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
[1663]262        if (it == cmdLineArgsShortcut_.end())
263            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
264
[3280]265        it->second->parse(value, bParsingFile);
[1663]266    }
[1664]267
[6021]268    std::string CommandLineParser::getUsageInformation()
[1664]269    {
[6021]270        CommandLineParser& inst = _getInstance();
[3280]271        std::ostringstream infoStr;
272
273        // determine maximum name size
274        size_t maxNameSize = 0;
275        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
276            it != inst.cmdLineArgs_.end(); ++it)
[1664]277        {
[3280]278            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
[1664]279        }
[3280]280
[5929]281        infoStr << std::endl;
[3280]282        infoStr << "Usage: orxonox [options]" << std::endl;
283        infoStr << "Available options:" << std::endl;
284
285        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst.cmdLineArgs_.begin();
286            it != inst.cmdLineArgs_.end(); ++it)
287        {
[6417]288            if (!it->second->getShortcut().empty())
[3280]289                infoStr << " [-" << it->second->getShortcut() << "] ";
290            else
291                infoStr << "      ";
[6417]292            infoStr << "--" << it->second->getName() << ' ';
[7424]293            if (it->second->getValue().type() != QVariant::Bool)
[3280]294                infoStr << "ARG ";
295            else
296                infoStr << "    ";
297            // fill with the necessary amount of blanks
298            infoStr << std::string(maxNameSize - it->second->getName().size(), ' ');
299            infoStr << ": " << it->second->getInformation();
300            infoStr << std::endl;
301        }
302        return infoStr.str();
[1664]303    }
304
[7401]305    void CommandLineParser::generateDoc(std::ofstream& file)
306    {
307        file << "/** @page cmdargspage Command Line Arguments Reference" << endl;
308        file << "    @verbatim"; /*no endl*/
309        file << getUsageInformation(); /*no endl*/
310        file << "    @endverbatim" << endl;
311        file << "*/" << endl;
312    }
313
[2087]314    /**
315    @brief
316        Retrieves a CommandLineArgument.
317        The method throws an exception if 'name' was not found or the value could not be converted.
318    @note
[7401]319        You should of course not call this method before the command line has been parsed.
[2087]320    */
[6021]321    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
[2087]322    {
323        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
324        if (it == _getInstance().cmdLineArgs_.end())
325        {
326            ThrowException(Argument, "Could find command line argument '" + name + "'.");
327        }
328        else
329        {
330            return it->second;
331        }
332    }
[2103]333
334    /**
335    @brief
[3280]336        Parses only the command line for CommandLineArguments.
[2103]337    */
[6021]338    void CommandLineParser::_parseCommandLine(const std::string& cmdLine)
[2103]339    {
340        std::vector<std::string> args;
[7284]341        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
[3323]342        for (unsigned i = 0; i < tokens.size(); ++i)
343            args.push_back(tokens[i]);
[3280]344        this->_parse(args, false);
345    }
[2103]346
[3280]347    /**
348    @brief
349        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
350    */
[6021]351    void CommandLineParser::_parseFile()
[3280]352    {
[7424]353        const std::string& filename = CommandLineParser::getValue("optionsFile").toString().toStdString();
[2710]354
[2103]355        // look for additional arguments in given file or start.ini as default
356        // They will not overwrite the arguments given directly
357        std::ifstream file;
[5929]358        file.open((PathConfig::getConfigPathString() + filename).c_str());
[3280]359        std::vector<std::string> args;
[2103]360        if (file)
361        {
362            while (!file.eof())
363            {
364                std::string line;
365                std::getline(file, line);
366                line = removeTrailingWhitespaces(line);
367                //if (!(line[0] == '#' || line[0] == '%'))
368                //{
[7284]369                SubString tokens(line, " ", " ", false, '\\', true, '"', true, '\0', '\0', false, '#');
[2103]370                for (unsigned i = 0; i < tokens.size(); ++i)
371                    if (tokens[i][0] != '#')
372                        args.push_back(tokens[i]);
373                //args.insert(args.end(), tokens.getAllStrings().begin(), tokens.getAllStrings().end());
374                //}
375            }
376            file.close();
377        }
378
[3280]379        _parse(args, true);
[2103]380    }
[1663]381}
Note: See TracBrowser for help on using the repository browser.