Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/core/CommandLine.cc @ 4274

Last change on this file since 4274 was 3323, checked in by rgrieder, 15 years ago

ORXONOX_USE_WINMAIN should now work as expected.

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