Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Merged pch branch back to trunk.

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