Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

Added new feature to CommandLine: arguments that cannot be specified in a file.
—optionsFile and —writingPathSuffix are now such arguments (because of obvious dependency problems).

  • Property svn:eol-style set to native
File size: 12.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 <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    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_.getType() == MT_bool)
56        {
57            // simulate command line switch
58            bool temp;
59            if (convertValue(&temp, value))
60            {
61                this->bHasDefaultValue_ = false;
62                this->value_ = temp;
63            }
64            else if (value == "")
65            {
66                this->bHasDefaultValue_ = false;
67                this->value_ = true;
68            }
69            else
70                ThrowException(Argument, "Could not read command line argument '" + getName() + "'.");
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, bool bParsingFile)
129    {
130        try
131        {
132            // why this? See bFirstTimeParse_ declaration.
133            if (bFirstTimeParse_)
134            {
135                // first shove all the shortcuts in a map
136                for (std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.begin();
137                    it != cmdLineArgs_.end(); ++it)
138                {
139                    OrxAssert(cmdLineArgsShortcut_.find(it->second->getShortcut()) == cmdLineArgsShortcut_.end(),
140                        "Cannot have two command line shortcut with the same name.");
141                    if (it->second->getShortcut() != "")
142                        cmdLineArgsShortcut_[it->second->getShortcut()] = it->second;
143                }
144                bFirstTimeParse_ = false;
145            }
146
147            std::string name;
148            std::string shortcut;
149            std::string value;
150            for (unsigned int i = 0; i < arguments.size(); ++i)
151            {
152                if (arguments[i].size() != 0)
153                {
154                    // sure not ""
155                    if (arguments[i][0] == '-')
156                    {
157                        // start with "-"
158                        if (arguments[i].size() == 1)
159                        {
160                            // argument[i] is "-", probably a minus sign
161                            value += "- ";
162                        }
163                        else if (arguments[i][1] <= 57 && arguments[i][1] >= 48)
164                        {
165                            // negative number as a value
166                            value += arguments[i] + " ";
167                        }
168                        else
169                        {
170                            // can be shortcut or full name argument
171
172                            // save old data first
173                            value = removeTrailingWhitespaces(value);
174                            if (name != "")
175                            {
176                                checkFullArgument(name, value, bParsingFile);
177                                name = "";
178                                assert(shortcut == "");
179                            }
180                            else if (shortcut != "")
181                            {
182                                checkShortcut(shortcut, value, bParsingFile);
183                                shortcut = "";
184                                assert(name == "");
185                            }
186
187                            if (arguments[i][1] == '-')
188                            {
189                                // full name argument with "--name"
190                                name = arguments[i].substr(2);
191                            }
192                            else
193                            {
194                                // shortcut with "-s"
195                                shortcut = arguments[i].substr(1);
196                            }
197
198                            // reset value string
199                            value = "";
200                        }
201                    }
202                    else
203                    {
204                        // value string
205
206                        if (name == "" && shortcut == "")
207                        {
208                            ThrowException(Argument, "Expected \"-\" or \"-\" in command line arguments.\n");
209                        }
210
211                        // Concatenate strings as long as there's no new argument by "-" or "--"
212                        value += arguments[i] + ' ';
213                    }
214                }
215            }
216
217            // parse last argument
218            value = removeTrailingWhitespaces(value);
219            if (name != "")
220            {
221                checkFullArgument(name, value, bParsingFile);
222                assert(shortcut == "");
223            }
224            else if (shortcut != "")
225            {
226                checkShortcut(shortcut, value, bParsingFile);
227                assert(name == "");
228            }
229        }
230        catch (const ArgumentException& ex)
231        {
232            COUT(0) << "Could not parse command line (including additional files): " << ex.what() << std::endl;
233            COUT(0) << "Usage:" << std::endl << "orxonox " << CommandLine::getUsageInformation() << std::endl;
234            throw GeneralException("");
235        }
236    }
237
238    /**
239    @brief
240        Parses an argument based on its full name.
241    @param name
242        Full name of the argument
243    @param value
244        String containing the value
245    */
246    void CommandLine::checkFullArgument(const std::string& name, const std::string& value, bool bParsingFile)
247    {
248        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgs_.find(name);
249        if (it == cmdLineArgs_.end())
250            ThrowException(Argument, "Command line argument '" + name + "' does not exist.");
251
252        it->second->parse(value, bParsingFile);
253    }
254
255    /**
256    @brief
257        Parses an argument based on its shortcut.
258    @param shortcut
259        Shotcut to the argument
260    @param value
261        String containing the value
262    */
263    void CommandLine::checkShortcut(const std::string& shortcut, const std::string& value, bool bParsingFile)
264    {
265        std::map<std::string, CommandLineArgument*>::const_iterator it = cmdLineArgsShortcut_.find(shortcut);
266        if (it == cmdLineArgsShortcut_.end())
267            ThrowException(Argument, "Command line shortcut '" + shortcut + "' does not exist.");
268
269        it->second->parse(value, bParsingFile);
270    }
271
272    std::string CommandLine::getUsageInformation()
273    {
274        CommandLine* inst = &_getInstance();
275        std::string infoStr;
276        for (std::map<std::string, CommandLineArgument*>::const_iterator it = inst->cmdLineArgs_.begin();
277            it != inst->cmdLineArgs_.end(); ++it)
278        {
279            infoStr += "[--" + it->second->getName() + " " + it->second->getInformation() + "] ";
280        }
281        return infoStr;
282    }
283
284    /**
285    @brief
286        Retrieves a CommandLineArgument.
287        The method throws an exception if 'name' was not found or the value could not be converted.
288    @note
289        You shold of course not call this method before the command line has been parsed.
290    */
291    const CommandLineArgument* CommandLine::getArgument(const std::string& name)
292    {
293        std::map<std::string, CommandLineArgument*>::const_iterator it = _getInstance().cmdLineArgs_.find(name);
294        if (it == _getInstance().cmdLineArgs_.end())
295        {
296            ThrowException(Argument, "Could find command line argument '" + name + "'.");
297        }
298        else
299        {
300            return it->second;
301        }
302    }
303
304    /**
305    @brief
306        Parses only the command line for CommandLineArguments.
307    */
308    void CommandLine::_parseCommandLine(int argc, char** argv)
309    {
310        std::vector<std::string> args;
311        for (int i = 1; i < argc; ++i)
312            args.push_back(argv[i]);
313        this->_parse(args, false);
314    }
315
316    /**
317    @brief
318        Parses start.ini (or the file specified with --optionsFile) for CommandLineArguments.
319    */
320    void CommandLine::_parseFile()
321    {
322        std::string filename = CommandLine::getValue("optionsFile").getString();
323        boost::filesystem::path filepath(Core::getConfigPath() / filename);
324
325        // look for additional arguments in given file or start.ini as default
326        // They will not overwrite the arguments given directly
327        std::ifstream file;
328        file.open(filepath.string().c_str());
329        std::vector<std::string> args;
330        if (file)
331        {
332            while (!file.eof())
333            {
334                std::string line;
335                std::getline(file, line);
336                line = removeTrailingWhitespaces(line);
337                //if (!(line[0] == '#' || line[0] == '%'))
338                //{
339                SubString tokens(line, " ", " ", false, 92, false, 34, false, 40, 41, false, '#');
340                for (unsigned i = 0; i < tokens.size(); ++i)
341                    if (tokens[i][0] != '#')
342                        args.push_back(tokens[i]);
343                //args.insert(args.end(), tokens.getAllStrings().begin(), tokens.getAllStrings().end());
344                //}
345            }
346            file.close();
347        }
348
349        _parse(args, true);
350    }
351}
Note: See TracBrowser for help on using the repository browser.