Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/core4/src/core/CommandLine.cc @ 3260

Last change on this file since 3260 was 3257, checked in by rgrieder, 15 years ago

Unified enumeration layout according to the style guide (which I have edited recently ;)).
There is one exception though: XMLPort::Mode. Since that would involve 182 changed files, I have decided not to rename it for now. Moreover its syntax is not too bad ;)

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