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
Line 
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
29#include "CommandLineParser.h"
30
31#include <algorithm>
32#include <sstream>
33
34#include "util/Debug.h"
35#include "util/Exception.h"
36#include "util/StringUtils.h"
37#include "util/SubString.h"
38#include "PathConfig.h"
39
40namespace orxonox
41{
42    SetCommandLineOnlyArgument(optionsFile, "start.ini").shortcut("o");
43
44    /**
45    @brief
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    */
51    void CommandLineArgument::parse(const std::string& value, bool bParsingFile)
52    {
53        if (bParsingFile && this->bCommandLineOnly_)
54            ThrowException(Argument, "Command line argument '" + getName() + "' is not allowed in files.");
55        if (value_.type() == QVariant::Bool)
56        {
57            // Command line switch activated
58            this->bHasDefaultValue_ = false;
59            this->value_ = true;
60        }
61        else
62        {
63            QVariant temp(QString::fromStdString(value));
64            if (!temp.convert(value_.type()))
65                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
66            else
67            {
68                this->bHasDefaultValue_ = false;
69                this->value_ = temp;
70            }
71        }
72    }
73
74
75    /**
76    @brief
77        Destructor destroys all CommandLineArguments with it.
78    */
79    CommandLineParser::~CommandLineParser()
80    {
81        CommandLineParser::destroyAllArguments();
82    }
83
84    /**
85    @brief
86        Returns a unique instance (Meyers Singleton).
87    */
88    CommandLineParser& CommandLineParser::_getInstance()
89    {
90        static CommandLineParser instance;
91        return instance;
92    }
93
94    /**
95    @brief
96        Destroys all command line arguments. This should be called at the end
97        of main. Do not use before that.
98    */
99    void CommandLineParser::destroyAllArguments()
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
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.
117    @param bParsingFile
118        Parsing a file or the command line itself
119    */
120    void CommandLineParser::_parse(const std::vector<std::string>& arguments, bool bParsingFile)
121    {
122        try
123        {
124            // why this? See bFirstTimeParse_ declaration.
125            if (bFirstTimeParse_)
126            {
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.");
133                    if (!it->second->getShortcut().empty())
134                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
135                }
136                bFirstTimeParse_ = false;
137            }
138
139            std::string name;
140            std::string shortcut;
141            std::string value;
142            for (unsigned int i = 0; i < arguments.size(); ++i)
143            {
144                if (arguments[i].size() != 0)
145                {
146                    // sure not ""
147                    if (arguments[i][0] == '-')
148                    {
149                        // start with "-"
150                        if (arguments[i].size() == 1)
151                        {
152                            // argument[i] is "-", probably a minus sign
153                            value += "- ";
154                        }
155                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
156                        {
157                            // negative number as a value
158                            value += arguments[i] + ' ';
159                        }
160                        else
161                        {
162                            // can be shortcut or full name argument
163
164                            // save old data first
165                            value = removeTrailingWhitespaces(value);
166                            if (!name.empty())
167                            {
168                                checkFullArgument(name, value, bParsingFile);
169                                name.clear();
170                                assert(shortcut.empty());
171                            }
172                            else if (!shortcut.empty())
173                            {
174                                checkShortcut(shortcut, value, bParsingFile);
175                                shortcut.clear();
176                                assert(name.empty());
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
191                            value.clear();
192                        }
193                    }
194                    else
195                    {
196                        // value string
197
198                        if (name.empty() && shortcut.empty())
199                        {
200                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
201                        }
202
203                        // Concatenate strings as long as there's no new argument by "-" or "--"
204                        value += arguments[i] + ' ';
205                    }
206                }
207            }
208
209            // parse last argument
210            value = removeTrailingWhitespaces(value);
211            if (!name.empty())
212            {
213                checkFullArgument(name, value, bParsingFile);
214                assert(shortcut.empty());
215            }
216            else if (!shortcut.empty())
217            {
218                checkShortcut(shortcut, value, bParsingFile);
219                assert(name.empty());
220            }
221        }
222        catch (const ArgumentException& ex)
223        {
224            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
225            COUT(0) << CommandLineParser::getUsageInformation() << std::endl;
226            throw GeneralException("");
227        }
228    }
229
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
237    @param bParsingFile
238        Parsing a file or the command line itself
239    */
240    void CommandLineParser::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
241    {
242        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
243        if (it == cmdLineArgs_.end())
244            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
245
246        it->second->parse(value, bParsingFile);
247    }
248
249    /**
250    @brief
251        Parses an argument based on its shortcut.
252    @param shortcut
253        Shortcut to the argument
254    @param value
255        String containing the value
256    @param bParsingFile
257        Parsing a file or the command line itself
258    */
259    void CommandLineParser::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
260    {
261        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
262        if (it == cmdLineArgsShortcut_.end())
263            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
264
265        it->second->parse(value, bParsingFile);
266    }
267
268    std::string CommandLineParser::getUsageInformation()
269    {
270        CommandLineParser& inst = _getInstance();
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)
277        {
278            maxNameSize = std::max(it->second->getName().size(), maxNameSize);
279        }
280
281        infoStr << std::endl;
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        {
288            if (!it->second->getShortcut().empty())
289                infoStr << " [-" << it->second->getShortcut() << "] ";
290            else
291                infoStr << "      ";
292            infoStr << "--" << it->second->getName() << ' ';
293            if (it->second->getValue().type() != QVariant::Bool)
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();
303    }
304
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
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
319        You should of course not call this method before the command line has been parsed.
320    */
321    const CommandLineArgument* CommandLineParser::getArgument(const std::string& name)
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    }
333
334    /**
335    @brief
336        Parses only the command line for CommandLineArguments.
337    */
338    void CommandLineParser::_parseCommandLine(const std::string& cmdLine)
339    {
340        std::vector<std::string> args;
341        SubString tokens(cmdLine, " ", " ", false, '\\', true, '"', true, '\0', '\0', false);
342        for (unsigned i = 0; i < tokens.size(); ++i)
343            args.push_back(tokens[i]);
344        this->_parse(args, false);
345    }
346
347    /**
348    @brief
349        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
350    */
351    void CommandLineParser::_parseFile()
352    {
353        const std::string& filename = CommandLineParser::getValue("optionsFile").toString().toStdString();
354
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;
358        file.open((PathConfig::getConfigPathString() + filename).c_str());
359        std::vector<std::string> args;
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                //{
369                SubString tokens(line, " ", " ", false, '\\', true, '"', true, '\0', '\0', false, '#');
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
379        _parse(args, true);
380    }
381}
Note: See TracBrowser for help on using the repository browser.