Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file OptionsIO.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Michael Behrisch
17 : /// @date Mon, 17 Dec 2001
18 : ///
19 : // Helper for parsing command line arguments and reading configuration files
20 : /****************************************************************************/
21 : #include <config.h>
22 :
23 : #include <string>
24 : #include <iostream>
25 : #include <cstdlib>
26 : #include <cstring>
27 : #include <xercesc/framework/XMLPScanToken.hpp>
28 : #include <xercesc/parsers/SAXParser.hpp>
29 : #include <xercesc/sax/HandlerBase.hpp>
30 : #include <xercesc/sax/AttributeList.hpp>
31 : #include <xercesc/sax/SAXParseException.hpp>
32 : #include <xercesc/sax/SAXException.hpp>
33 : #include <xercesc/util/PlatformUtils.hpp>
34 : #include "OptionsIO.h"
35 : #include "OptionsCont.h"
36 : #include "OptionsLoader.h"
37 : #include "OptionsParser.h"
38 : #include <utils/common/FileHelpers.h>
39 : #include <utils/common/MsgHandler.h>
40 : #include <utils/common/StringUtils.h>
41 : #include <utils/xml/XMLSubSys.h>
42 : #ifdef HAVE_ZLIB
43 : #include <foreign/zstr/zstr.hpp>
44 : #endif
45 : #include <utils/xml/IStreamInputSource.h>
46 :
47 :
48 : // ===========================================================================
49 : // static member definitions
50 : // ===========================================================================
51 : std::vector<std::string> OptionsIO::myArgs;
52 : std::chrono::time_point<std::chrono::system_clock> OptionsIO::myLoadTime;
53 :
54 :
55 : // ===========================================================================
56 : // method definitions
57 : // ===========================================================================
58 : void
59 53587 : OptionsIO::setArgs(int argc, char** argv) {
60 : myArgs.clear();
61 909128 : for (int i = 0; i < argc; i++) {
62 1711082 : myArgs.push_back(XMLSubSys::transcodeFromLocal(argv[i]));
63 : }
64 53587 : }
65 :
66 :
67 : void
68 1161 : OptionsIO::setArgs(const std::vector<std::string>& args) {
69 1161 : myArgs.resize(1); // will insert an empty string if no first element is there
70 1161 : myArgs.insert(myArgs.end(), args.begin(), args.end());
71 1161 : }
72 :
73 :
74 : void
75 54748 : OptionsIO::getOptions(const bool commandLineOnly) {
76 54748 : myLoadTime = std::chrono::system_clock::now();
77 54748 : if (myArgs.size() == 2 && myArgs[1][0] != '-') {
78 : // special case only one parameter, check who can handle it
79 34 : if (OptionsCont::getOptions().setByRootElement(getRoot(myArgs[1]), myArgs[1])) {
80 17 : if (!commandLineOnly) {
81 17 : loadConfiguration();
82 : }
83 17 : return;
84 : }
85 : }
86 : // preparse the options
87 : // (maybe another configuration file was chosen)
88 54731 : if (!OptionsParser::parse(myArgs, true)) {
89 132 : throw ProcessError(TL("Could not parse commandline options."));
90 : }
91 63486 : if (!commandLineOnly || OptionsCont::getOptions().isSet("save-configuration", false)) {
92 : // read the configuration when everything's ok
93 45844 : loadConfiguration();
94 : }
95 : }
96 :
97 :
98 : void
99 54682 : OptionsIO::loadConfiguration() {
100 54682 : OptionsCont& oc = OptionsCont::getOptions();
101 109364 : if (oc.exists("configuration-file") && oc.isSet("configuration-file")) {
102 27892 : const std::string path = oc.getString("configuration-file");
103 27892 : if (!FileHelpers::isReadable(path)) {
104 717 : throw ProcessError(TLF("Could not access configuration '%'.", oc.getString("configuration-file")));
105 : }
106 27414 : const bool verbose = !oc.exists("verbose") || oc.getBool("verbose");
107 13707 : if (verbose) {
108 666 : PROGRESS_BEGIN_MESSAGE(TL("Loading configuration"));
109 : }
110 13707 : oc.resetWritable();
111 : // build parser
112 13707 : XERCES_CPP_NAMESPACE::SAXParser parser;
113 13707 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
114 13707 : parser.setDisableDefaultEntityResolution(true);
115 : // start the parsing
116 13707 : OptionsLoader handler(OptionsCont::getOptions());
117 : try {
118 13707 : parser.setDocumentHandler(&handler);
119 13707 : parser.setErrorHandler(&handler);
120 13707 : parser.parse(XMLSubSys::transcodeToLocal(path).c_str());
121 13707 : if (handler.errorOccurred()) {
122 21 : throw ProcessError(TLF("Could not load configuration '%'.", path));
123 : }
124 7 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
125 0 : throw ProcessError("Could not load configuration '" + path + "':\n " + XMLSubSys::transcode(e.getMessage()));
126 0 : }
127 13700 : oc.relocateFiles(path);
128 13700 : if (verbose) {
129 140 : PROGRESS_DONE_MESSAGE();
130 : }
131 13714 : }
132 54436 : if (myArgs.size() > 2) {
133 : // reparse the options (overwrite the settings from the configuration file)
134 54269 : oc.resetWritable();
135 54269 : if (!OptionsParser::parse(myArgs)) {
136 8 : throw ProcessError(TL("Could not parse commandline options."));
137 : }
138 : }
139 54432 : }
140 :
141 :
142 : std::string
143 17 : OptionsIO::getRoot(const std::string& filename) {
144 : // build parser
145 17 : XERCES_CPP_NAMESPACE::SAXParser parser;
146 17 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never);
147 17 : parser.setDisableDefaultEntityResolution(true);
148 : // start the parsing
149 17 : OptionsLoader handler(OptionsCont::getOptions());
150 : try {
151 17 : parser.setDocumentHandler(&handler);
152 17 : parser.setErrorHandler(&handler);
153 : XERCES_CPP_NAMESPACE::XMLPScanToken token;
154 51 : if (!FileHelpers::isReadable(filename) || FileHelpers::isDirectory(filename)) {
155 0 : throw ProcessError(TLF("Could not open '%'.", filename));
156 : }
157 : #ifdef HAVE_ZLIB
158 34 : zstr::ifstream istream(XMLSubSys::transcodeToLocal(filename).c_str(), std::fstream::in | std::fstream::binary);
159 : IStreamInputSource inputStream(istream);
160 17 : const bool result = parser.parseFirst(inputStream, token);
161 : #else
162 : const bool result = parser.parseFirst(XMLSubSys::transcodeToLocal(filename).c_str(), token);
163 : #endif
164 17 : if (!result) {
165 0 : throw ProcessError(TLF("Can not read XML-file '%'.", filename));
166 : }
167 17 : while (parser.parseNext(token) && handler.getItem() == "");
168 17 : if (handler.errorOccurred()) {
169 0 : throw ProcessError(TLF("Could not load '%'.", filename));
170 : }
171 17 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) {
172 0 : throw ProcessError("Could not load '" + filename + "':\n " + XMLSubSys::transcode(e.getMessage()));
173 0 : }
174 17 : return handler.getItem();
175 17 : }
176 :
177 :
178 : /****************************************************************************/
|