Line data Source code
1 : /****************************************************************************/ 2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo 3 : // Copyright (C) 2001-2024 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 : #ifdef HAVE_ZLIB 42 : #include <foreign/zstr/zstr.hpp> 43 : #endif 44 : #include <utils/xml/IStreamInputSource.h> 45 : 46 : 47 : // =========================================================================== 48 : // static member definitions 49 : // =========================================================================== 50 : std::vector<std::string> OptionsIO::myArgs; 51 : std::chrono::time_point<std::chrono::system_clock> OptionsIO::myLoadTime; 52 : 53 : 54 : // =========================================================================== 55 : // method definitions 56 : // =========================================================================== 57 : void 58 47389 : OptionsIO::setArgs(int argc, char** argv) { 59 : myArgs.clear(); 60 681585 : for (int i = 0; i < argc; i++) { 61 1525572 : myArgs.push_back(StringUtils::transcodeFromLocal(argv[i])); 62 : } 63 47389 : } 64 : 65 : 66 : void 67 1034 : OptionsIO::setArgs(const std::vector<std::string>& args) { 68 1034 : myArgs.resize(1); // will insert an empty string if no first element is there 69 1034 : myArgs.insert(myArgs.end(), args.begin(), args.end()); 70 1034 : } 71 : 72 : 73 : void 74 48423 : OptionsIO::getOptions(const bool commandLineOnly) { 75 48423 : myLoadTime = std::chrono::system_clock::now(); 76 48423 : if (myArgs.size() == 2 && myArgs[1][0] != '-') { 77 : // special case only one parameter, check who can handle it 78 104 : if (OptionsCont::getOptions().setByRootElement(getRoot(myArgs[1]), myArgs[1])) { 79 52 : if (!commandLineOnly) { 80 52 : loadConfiguration(); 81 : } 82 52 : return; 83 : } 84 : } 85 : // preparse the options 86 : // (maybe another configuration file was chosen) 87 48371 : if (!OptionsParser::parse(myArgs, true)) { 88 142 : throw ProcessError(TL("Could not parse commandline options.")); 89 : } 90 55545 : if (!commandLineOnly || OptionsCont::getOptions().isSet("save-configuration", false)) { 91 : // read the configuration when everything's ok 92 41055 : loadConfiguration(); 93 : } 94 : } 95 : 96 : 97 : void 98 48351 : OptionsIO::loadConfiguration() { 99 48351 : OptionsCont& oc = OptionsCont::getOptions(); 100 183051 : if (oc.exists("configuration-file") && oc.isSet("configuration-file")) { 101 20706 : const std::string path = oc.getString("configuration-file"); 102 20706 : if (!FileHelpers::isReadable(path)) { 103 0 : throw ProcessError(TLF("Could not access configuration '%'.", oc.getString("configuration-file"))); 104 : } 105 20857 : const bool verbose = !oc.exists("verbose") || oc.getBool("verbose"); 106 10353 : if (verbose) { 107 309 : PROGRESS_BEGIN_MESSAGE(TL("Loading configuration")); 108 : } 109 10353 : oc.resetWritable(); 110 : // build parser 111 10353 : XERCES_CPP_NAMESPACE::SAXParser parser; 112 10353 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never); 113 10353 : parser.setDisableDefaultEntityResolution(true); 114 : // start the parsing 115 10353 : OptionsLoader handler(OptionsCont::getOptions()); 116 : try { 117 10353 : parser.setDocumentHandler(&handler); 118 10353 : parser.setErrorHandler(&handler); 119 10353 : parser.parse(StringUtils::transcodeToLocal(path).c_str()); 120 10353 : if (handler.errorOccurred()) { 121 28 : throw ProcessError(TLF("Could not load configuration '%'.", path)); 122 : } 123 7 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) { 124 0 : throw ProcessError("Could not load configuration '" + path + "':\n " + StringUtils::transcode(e.getMessage())); 125 0 : } 126 10346 : oc.relocateFiles(path); 127 10346 : if (verbose) { 128 151 : PROGRESS_DONE_MESSAGE(); 129 : } 130 10360 : } 131 48344 : if (myArgs.size() > 2) { 132 : // reparse the options (overwrite the settings from the configuration file) 133 48028 : oc.resetWritable(); 134 48028 : if (!OptionsParser::parse(myArgs)) { 135 16 : throw ProcessError(TL("Could not parse commandline options.")); 136 : } 137 : } 138 48336 : } 139 : 140 : 141 : std::string 142 52 : OptionsIO::getRoot(const std::string& filename) { 143 : // build parser 144 52 : XERCES_CPP_NAMESPACE::SAXParser parser; 145 52 : parser.setValidationScheme(XERCES_CPP_NAMESPACE::SAXParser::Val_Never); 146 52 : parser.setDisableDefaultEntityResolution(true); 147 : // start the parsing 148 52 : OptionsLoader handler(OptionsCont::getOptions()); 149 : try { 150 52 : parser.setDocumentHandler(&handler); 151 52 : parser.setErrorHandler(&handler); 152 : XERCES_CPP_NAMESPACE::XMLPScanToken token; 153 177 : if (!FileHelpers::isReadable(filename) || FileHelpers::isDirectory(filename)) { 154 0 : throw ProcessError(TLF("Could not open '%'.", filename)); 155 : } 156 : #ifdef HAVE_ZLIB 157 125 : zstr::ifstream istream(StringUtils::transcodeToLocal(filename).c_str(), std::fstream::in | std::fstream::binary); 158 : IStreamInputSource inputStream(istream); 159 52 : const bool result = parser.parseFirst(inputStream, token); 160 : #else 161 : const bool result = parser.parseFirst(StringUtils::transcodeToLocal(filename).c_str(), token); 162 : #endif 163 52 : if (!result) { 164 0 : throw ProcessError(TLF("Can not read XML-file '%'.", filename)); 165 : } 166 104 : while (parser.parseNext(token) && handler.getItem() == ""); 167 52 : if (handler.errorOccurred()) { 168 0 : throw ProcessError(TLF("Could not load '%'.", filename)); 169 : } 170 52 : } catch (const XERCES_CPP_NAMESPACE::XMLException& e) { 171 0 : throw ProcessError("Could not load '" + filename + "':\n " + StringUtils::transcode(e.getMessage())); 172 0 : } 173 52 : return handler.getItem(); 174 52 : } 175 : 176 : 177 : /****************************************************************************/