LCOV - code coverage report
Current view: top level - src/polyconvert - polyconvert_main.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.5 % 222 201
Test Date: 2025-11-14 15:59:05 Functions: 100.0 % 2 2

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2005-2025 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    polyconvert_main.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Christoph Sommer
      18              : /// @author  Michael Behrisch
      19              : /// @author  Melanie Knocke
      20              : /// @date    Mon, 05 Dec 2005
      21              : ///
      22              : // Main for POLYCONVERT
      23              : /****************************************************************************/
      24              : #include <config.h>
      25              : 
      26              : #ifdef HAVE_VERSION_H
      27              : #include <version.h>
      28              : #endif
      29              : 
      30              : #include <iostream>
      31              : #include <string>
      32              : #include <utils/options/OptionsIO.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/common/UtilExceptions.h>
      35              : #include <utils/common/StringTokenizer.h>
      36              : #include <utils/common/SystemFrame.h>
      37              : #include <utils/common/MsgHandler.h>
      38              : #include <utils/common/StringUtils.h>
      39              : #include <utils/common/ToString.h>
      40              : #include <utils/iodevices/OutputDevice.h>
      41              : #include <utils/importio/LineReader.h>
      42              : #include <utils/geom/GeoConvHelper.h>
      43              : #include <utils/geom/GeomConvHelper.h>
      44              : #include <utils/geom/Boundary.h>
      45              : #include <utils/xml/SUMOSAXReader.h>
      46              : #include <utils/xml/XMLSubSys.h>
      47              : #include <polyconvert/PCLoaderVisum.h>
      48              : #include <polyconvert/PCLoaderDlrNavteq.h>
      49              : #include <polyconvert/PCLoaderXML.h>
      50              : #include <polyconvert/PCLoaderOSM.h>
      51              : #include <polyconvert/PCLoaderArcView.h>
      52              : #include <polyconvert/PCTypeMap.h>
      53              : #include <polyconvert/PCTypeDefHandler.h>
      54              : #include <polyconvert/PCNetProjectionLoader.h>
      55              : #include <polyconvert/pc_typemap.h>
      56              : 
      57              : 
      58              : // ===========================================================================
      59              : // method definitions
      60              : // ===========================================================================
      61              : void
      62           52 : fillOptions() {
      63           52 :     OptionsCont& oc = OptionsCont::getOptions();
      64          104 :     oc.addCallExample("-c <CONFIGURATION>", TL("run with configuration options set in file"));
      65              : 
      66              :     // insert options sub-topics
      67           52 :     SystemFrame::addConfigurationOptions(oc); // fill this subtopic, too
      68           52 :     oc.addOptionSubTopic("Input");
      69           52 :     oc.addOptionSubTopic("Output");
      70           52 :     GeoConvHelper::addProjectionOptions(oc);
      71           52 :     oc.addOptionSubTopic("Pruning");
      72           52 :     oc.addOptionSubTopic("Processing");
      73           52 :     oc.addOptionSubTopic("Building Defaults");
      74              : 
      75              : 
      76              :     // register options
      77              :     // add i/o options
      78              :     // original network
      79           52 :     oc.doRegister("net-file", 'n', new Option_FileName());
      80          104 :     oc.addSynonyme("net-file", "net");
      81          104 :     oc.addDescription("net-file", "Input", TL("Loads SUMO-network FILE as reference to offset and projection"));
      82              : 
      83              :     // dlrnavteq import
      84           52 :     oc.doRegister("dlr-navteq-poly-files", new Option_FileName());
      85          104 :     oc.addDescription("dlr-navteq-poly-files", "Input", TL("Reads polygons from FILE assuming they're coded in DLR-Navteq (Elmar)-format"));
      86           52 :     oc.doRegister("dlr-navteq-poi-files", new Option_FileName());
      87          104 :     oc.addDescription("dlr-navteq-poi-files", "Input", TL("Reads pois from FILE assuming they're coded in DLR-Navteq (Elmar)-format"));
      88              : 
      89              :     // visum import
      90           52 :     oc.doRegister("visum-files", new Option_FileName());
      91          104 :     oc.addSynonyme("visum-files", "visum");
      92          104 :     oc.addDescription("visum-files", "Input", TL("Reads polygons from FILE assuming it's a Visum-net"));
      93              : 
      94           52 :     oc.doRegister("visum.language-file", new Option_FileName());
      95          104 :     oc.addDescription("visum.language-file", "Input", TL("Load language mappings from FILE"));
      96              : 
      97              :     // xml import
      98           52 :     oc.doRegister("xml-files", new Option_FileName());
      99          104 :     oc.addSynonyme("xml-files", "xml");
     100          104 :     oc.addDescription("xml-files", "Input", TL("Reads pois and shapes from FILE assuming they're coded in XML"));
     101              : 
     102              :     // osm import
     103           52 :     oc.doRegister("osm-files", new Option_FileName());
     104          104 :     oc.addSynonyme("osm-files", "osm");
     105          104 :     oc.addDescription("osm-files", "Input", TL("Reads pois from FILE assuming they're coded in OSM"));
     106           52 :     oc.doRegister("osm.keep-full-type", new Option_Bool(false));
     107          104 :     oc.addDescription("osm.keep-full-type", "Input", TL("The type will be made of the key-value - pair"));
     108           52 :     oc.doRegister("osm.use-name", new Option_Bool(false));
     109          104 :     oc.addDescription("osm.use-name", "Input", TL("The id will be set from the given 'name' attribute"));
     110           52 :     oc.doRegister("osm.merge-relations", new Option_Float(-1));
     111          104 :     oc.addDescription("osm.merge-relations", "Input", TL("If FLOAT >= 0, assemble one polygon from all ways of a relation if they all connect with gaps below FLOAT"));
     112              : 
     113              :     // geojson import
     114           52 :     oc.doRegister("geojson-files", new Option_FileName());
     115          104 :     oc.addDescription("geojson-files", "Input", TL("Reads shapes from geojson FILE"));
     116              : 
     117              :     // arcview import
     118           52 :     oc.doRegister("shapefile-prefixes", new Option_FileName());
     119          104 :     oc.addSynonyme("shapefile-prefixes", "shapefile-prefix");
     120          104 :     oc.addSynonyme("shapefile-prefixes", "shapefile");
     121          104 :     oc.addSynonyme("shapefile-prefixes", "shape-files", true);
     122          104 :     oc.addDescription("shapefile-prefixes", "Input", TL("Reads shapes from shapefiles FILE"));
     123              : 
     124           52 :     oc.doRegister("shapefile.guess-projection", new Option_Bool(false));
     125          104 :     oc.addSynonyme("shapefile.guess-projection", "arcview.guess-projection", true);
     126          104 :     oc.addDescription("shapefile.guess-projection", "Input", TL("Guesses the shapefile's projection"));
     127              : 
     128           52 :     oc.doRegister("shapefile.traditional-axis-mapping", new Option_Bool(false));
     129          104 :     oc.addDescription("shapefile.traditional-axis-mapping", "Input", TL("Use traditional axis order (lon, lat)"));
     130              : 
     131           52 :     oc.doRegister("shapefile.id-column", new Option_String());
     132          104 :     oc.addSynonyme("shapefile.id-column", "shapefile.id-name", true);
     133          104 :     oc.addSynonyme("shapefile.id-column", "shape-files.id-name", true);
     134          104 :     oc.addDescription("shapefile.id-column", "Input", TL("Defines in which column the id can be found"));
     135              : 
     136           52 :     oc.doRegister("shapefile.type-columns", new Option_StringVector());
     137          104 :     oc.addSynonyme("shapefile.type-columns", "shapefile.type-column");
     138          104 :     oc.addDescription("shapefile.type-columns", "Input", TL("Defines which columns form the type id (comma separated list)"));
     139              : 
     140           52 :     oc.doRegister("shapefile.use-running-id", new Option_Bool(false));
     141          104 :     oc.addDescription("shapefile.use-running-id", "Input", TL("A running number will be used as id"));
     142              : 
     143           52 :     oc.doRegister("shapefile.add-param", new Option_Bool(false));
     144          104 :     oc.addDescription("shapefile.add-param", "Input", TL("Extract all additional columns as params"));
     145              : 
     146           52 :     oc.doRegister("shapefile.fill", new Option_String());
     147          104 :     oc.addDescription("shapefile.fill", "Input", TL("[auto|true|false]. Forces the 'fill' status to the given value. Default 'auto' tries to determine it from the data type"));
     148              : 
     149              :     // typemap reading
     150           52 :     oc.doRegister("type-file", new Option_FileName());
     151          104 :     oc.addSynonyme("type-file", "typemap", true);
     152          104 :     oc.addDescription("type-file", "Input", TL("Reads types from FILE"));
     153              : 
     154              :     // need to do this here to be able to check for network and route input options
     155           52 :     SystemFrame::addReportOptions(oc);
     156              : 
     157              :     // output
     158           52 :     oc.doRegister("output-file", 'o', new Option_FileName());
     159          104 :     oc.addSynonyme("output-file", "output");
     160          104 :     oc.addDescription("output-file", "Output", TL("Write generated polygons/pois to FILE"));
     161              : 
     162           52 :     oc.doRegister("dlr-tdp-output", new Option_FileName());
     163          104 :     oc.addDescription("dlr-tdp-output", "Output", TL("Write generated polygons/pois to a dlr-tdp file with the given prefix"));
     164              : 
     165              : 
     166              :     // pruning options
     167           52 :     oc.doRegister("prune.in-net", new Option_Bool(false));
     168          104 :     oc.addSynonyme("prune.in-net", "prune.on-net", true);
     169          104 :     oc.addDescription("prune.in-net", TL("Pruning"), TL("Enables pruning on net boundaries"));
     170              : 
     171          104 :     oc.doRegister("prune.in-net.offsets", new Option_String("0,0,0,0"));
     172          104 :     oc.addSynonyme("prune.in-net.offsets", "prune.on-net.offsets", true);
     173          104 :     oc.addDescription("prune.in-net.offsets", TL("Pruning"), TL("Uses FLOAT,FLOAT,FLOAT,FLOAT as offset definition added to the net boundary. Positive values grow the boundary on all sides while negative values shrink it."));
     174              : 
     175           52 :     oc.doRegister("prune.boundary", new Option_String());
     176          104 :     oc.addDescription("prune.boundary", TL("Pruning"), TL("Uses STR as pruning boundary"));
     177              : 
     178           52 :     oc.doRegister("prune.keep-list", new Option_String());
     179          104 :     oc.addSynonyme("prune.keep-list", "prune.keep");
     180          104 :     oc.addSynonyme("prune.keep-list", "prune.ignore", true);
     181          104 :     oc.addDescription("prune.keep-list", TL("Pruning"), TL("Items in STR will be kept though out of boundary"));
     182              : 
     183          208 :     oc.doRegister("prune.explicit", new Option_StringVector(StringVector({ "" })));
     184          104 :     oc.addSynonyme("prune.explicit", "remove");
     185          104 :     oc.addDescription("prune.explicit", TL("Pruning"), TL("Items with names in STR[] will be removed"));
     186              : 
     187              : 
     188           52 :     oc.doRegister("offset.x", new Option_Float(0));
     189          104 :     oc.addSynonyme("offset.x", "x-offset-to-apply", true);
     190          104 :     oc.addDescription("offset.x", "Processing", TL("Adds FLOAT to net x-positions"));
     191              : 
     192           52 :     oc.doRegister("offset.y", new Option_Float(0));
     193          104 :     oc.addSynonyme("offset.y", "y-offset-to-apply", true);
     194          104 :     oc.addDescription("offset.y", "Processing", TL("Adds FLOAT to net y-positions"));
     195              : 
     196           52 :     oc.doRegister("offset.z", new Option_Float(0));
     197          104 :     oc.addDescription("offset.z", "Processing", TL("Adds FLOAT to net z-positions"));
     198              : 
     199           52 :     oc.doRegister("all-attributes", new Option_Bool(false));
     200          104 :     oc.addDescription("all-attributes", "Processing", TL("Imports all attributes as key/value pairs"));
     201              : 
     202           52 :     oc.doRegister("ignore-errors", new Option_Bool(false));
     203          104 :     oc.addDescription("ignore-errors", "Processing", TL("Continue on broken input"));
     204              : 
     205           52 :     oc.doRegister("poi-layer-offset", new Option_Float(0));
     206          104 :     oc.addDescription("poi-layer-offset", "Processing", TL("Adds FLOAT to the layer value for each poi (i.e. to raise it above polygons)"));
     207              : 
     208           52 :     oc.doRegister("flatten", new Option_Bool(false));
     209          104 :     oc.addDescription("flatten", "Processing", TL("Remove all z-data"));
     210              : 
     211              :     // building defaults options
     212          104 :     oc.doRegister("color", new Option_String("0.2,0.5,1."));
     213          104 :     oc.addDescription("color", "Building Defaults", TL("Sets STR as default color"));
     214              : 
     215          104 :     oc.doRegister("prefix", new Option_String(""));
     216          104 :     oc.addDescription("prefix", "Building Defaults", TL("Sets STR as default prefix"));
     217              : 
     218          104 :     oc.doRegister("type", new Option_String("unknown"));
     219          104 :     oc.addDescription("type", "Building Defaults", TL("Sets STR as default type"));
     220              : 
     221           52 :     oc.doRegister("fill", new Option_Bool(true));
     222          104 :     oc.addDescription("fill", "Building Defaults", TL("Fills polygons by default"));
     223              : 
     224          104 :     oc.doRegister("icon", new Option_String(SUMOXMLDefinitions::POIIcons.getString(POIIcon::NONE)));
     225          104 :     oc.addDescription("icon", "Building Defaults", TL("Sets STR as default icon"));
     226              : 
     227           52 :     oc.doRegister("layer", new Option_Float(-1));
     228          104 :     oc.addDescription("layer", "Building Defaults", TL("Sets FLOAT as default layer"));
     229              : 
     230           52 :     oc.doRegister("discard", new Option_Bool(false));
     231          104 :     oc.addDescription("discard", "Building Defaults", TL("Sets default action to discard"));
     232              : 
     233              :     // projection
     234           52 :     oc.doRegister("proj.plain-geo", new Option_Bool(false));
     235          104 :     oc.addDescription("proj.plain-geo", "Projection", TL("Write geo coordinates in output"));
     236          156 : }
     237              : 
     238              : 
     239              : int
     240           52 : main(int argc, char** argv) {
     241           52 :     OptionsCont& oc = OptionsCont::getOptions();
     242           52 :     oc.setApplicationDescription(TL("Importer of polygons and POIs for the microscopic, multi-modal traffic simulation SUMO."));
     243          104 :     oc.setApplicationName("polyconvert", "Eclipse SUMO polyconvert " VERSION_STRING);
     244              :     int ret = 0;
     245              :     try {
     246              :         // initialise subsystems
     247           52 :         XMLSubSys::init();
     248           52 :         fillOptions();
     249           52 :         OptionsIO::setArgs(argc, argv);
     250           52 :         OptionsIO::getOptions();
     251           52 :         if (oc.processMetaOptions(argc < 2)) {
     252            6 :             SystemFrame::close();
     253            6 :             return 0;
     254              :         }
     255           46 :         SystemFrame::checkOptions(oc);
     256          138 :         XMLSubSys::setValidation(oc.getString("xml-validation"), oc.getString("xml-validation.net"), "never");
     257           92 :         if (oc.isDefault("aggregate-warnings")) {
     258           92 :             oc.setDefault("aggregate-warnings", "5");
     259              :         }
     260           46 :         MsgHandler::initOutputOptions();
     261              :         // build the projection
     262           46 :         double scale = 1.0;
     263           94 :         if ((oc.isSet("dlr-navteq-poly-files") || oc.isSet("dlr-navteq-poi-files")) && oc.isDefault("proj.scale")) {
     264            6 :             scale = 1e-5;
     265              :         }
     266           92 :         if (!oc.isSet("net")) {
     267              :             // from the given options
     268              : #ifdef PROJ_API_FILE
     269           70 :             const int numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + (oc.getString("proj").length() > 1);
     270           70 :             if ((oc.isSet("osm-files")
     271           54 :                     || oc.isSet("dlr-navteq-poly-files")
     272           52 :                     || oc.isSet("dlr-navteq-poi-files")
     273           51 :                     || oc.isSet("geojson-files")
     274           50 :                     || oc.isSet("shapefile-prefixes"))
     275           43 :                     && numProjections == 0) {
     276              :                 // input is lon,lat and projecting it to UTM ensures accurate handling of geometry
     277           48 :                 oc.set("proj.utm", "true");
     278           48 :                 if (oc.isDefault("proj.plain-geo")) {
     279              :                     // without reference to a network, raw UTM isn't helpful so we better write the data out as lon,lat
     280           46 :                     oc.set("proj.plain-geo", "true");
     281              :                 }
     282              :             }
     283           70 :             if (oc.isDefault("proj.scale")) {
     284           70 :                 oc.set("proj.scale", toString(scale, 5));
     285              :             }
     286              : #endif
     287           35 :             if (!GeoConvHelper::init(oc)) {
     288            0 :                 throw ProcessError(TL("Could not build projection!"));
     289              :             }
     290              :         } else {
     291              :             // from the supplied network
     292              :             // @todo warn about given options being ignored
     293           22 :             PCNetProjectionLoader::load(oc.getString("net"), scale);
     294              :         }
     295           46 :         Boundary pruningBoundary = GeoConvHelper::getFinal().getConvBoundary();
     296              :         // check whether the input shall be pruned
     297              :         bool prune = false;
     298           92 :         if (oc.getBool("prune.in-net")) {
     299            2 :             if (!oc.isSet("net")) {
     300            0 :                 throw ProcessError(TL("In order to prune the input on the net, you have to supply a network."));
     301              :             }
     302            1 :             bool ok = true;
     303              :             // !!! no proper error handling
     304              :             Boundary offsets = GeomConvHelper::parseBoundaryReporting(
     305            2 :                                    oc.getString("prune.in-net.offsets"), "--prune.on-net.offsets", nullptr, ok, true, true);
     306            1 :             pruningBoundary.setOffsets(
     307            1 :                 pruningBoundary.xmin() - offsets.xmin(),
     308            1 :                 pruningBoundary.ymin() - offsets.ymin(),
     309            1 :                 pruningBoundary.xmax() + offsets.xmax(),
     310            1 :                 pruningBoundary.ymax() + offsets.ymax());
     311              :             prune = true;
     312              :         }
     313           92 :         if (oc.isSet("prune.boundary")) {
     314            0 :             bool ok = true;
     315              :             // !!! no proper error handling
     316            0 :             pruningBoundary = GeomConvHelper::parseBoundaryReporting(oc.getString("prune.boundary"), "--prune.boundary", nullptr, ok);
     317              :             prune = true;
     318              :         }
     319           69 :         if (oc.isSet("osm-files") && oc.isDefault("poi-layer-offset")) {
     320           46 :             oc.setDefault("poi-layer-offset", "5"); // sufficient when using the default typemap
     321              :         }
     322              : 
     323           46 :         PCPolyContainer toFill(prune, pruningBoundary, oc.getStringVector("remove"));
     324              : 
     325              :         // read in the type defaults
     326           92 :         if (!oc.isSet("type-file")) {
     327           41 :             const char* sumoPath = std::getenv("SUMO_HOME");
     328           41 :             if (sumoPath == nullptr) {
     329            0 :                 WRITE_WARNING(TL("Environment variable SUMO_HOME is not set, using built in type maps."));
     330              :             } else {
     331           41 :                 const std::string path = sumoPath + std::string("/data/typemap/");
     332           82 :                 if (oc.isSet("dlr-navteq-poly-files")) {
     333            8 :                     oc.setDefault("type-file", path + "navteqPolyconvert.typ.xml");
     334              :                 }
     335           82 :                 if (oc.isSet("osm-files")) {
     336           36 :                     oc.setDefault("type-file", path + "osmPolyconvert.typ.xml");
     337              :                 }
     338           82 :                 if (oc.isSet("visum-files")) {
     339            0 :                     oc.setDefault("type-file", path + "visumPolyconvert.typ.xml");
     340              :                 }
     341              :             }
     342              :         }
     343           46 :         PCTypeMap tm(oc);
     344           46 :         PCTypeDefHandler handler(oc, tm);
     345           92 :         if (oc.isSet("type-file")) {
     346           54 :             if (!XMLSubSys::runParser(handler, oc.getString("type-file"))) {
     347              :                 // something failed
     348            0 :                 throw ProcessError();
     349              :             }
     350              :         } else {
     351           19 :             handler.setFileName("built in type map");
     352           19 :             SUMOSAXReader* reader = XMLSubSys::getSAXReader(handler);
     353           38 :             if (oc.isSet("dlr-navteq-poly-files")) {
     354            0 :                 reader->parseString(navteqTypemap);
     355              :             }
     356           38 :             if (oc.isSet("osm-files")) {
     357            0 :                 reader->parseString(osmTypemap);
     358              :             }
     359           38 :             if (oc.isSet("visum-files")) {
     360            0 :                 reader->parseString(visumTypemap);
     361              :             }
     362           19 :             delete reader;
     363              :         }
     364              :         // read in the data
     365           46 :         PCLoaderXML::loadIfSet(oc, toFill, tm); // SUMO-XML
     366           45 :         PCLoaderOSM::loadIfSet(oc, toFill, tm); // OSM-XML
     367           44 :         PCLoaderDlrNavteq::loadIfSet(oc, toFill, tm); // Elmar-files
     368           44 :         PCLoaderVisum::loadIfSet(oc, toFill, tm); // VISUM
     369           44 :         PCLoaderArcView::loadIfSet(oc, toFill, tm); // shape-files
     370           44 :         GeoConvHelper::computeFinal();
     371              :         // error processing
     372           45 :         if (MsgHandler::getErrorInstance()->wasInformed() && !oc.getBool("ignore-errors")) {
     373            1 :             throw ProcessError();
     374              :         }
     375              :         // output
     376           45 :         if (!oc.isSet("output-file") && !oc.isSet("dlr-tdp-output")) {
     377            0 :             std::string out = "polygons.xml";
     378            0 :             if (oc.isSet("configuration-file")) {
     379            0 :                 out = FileHelpers::getConfigurationRelative(oc.getString("configuration-file"), out);
     380              :             }
     381            0 :             oc.setDefault("output-file", out);
     382              :         }
     383           86 :         if (oc.isSet("output-file")) {
     384           84 :             toFill.save(oc.getString("output-file"), oc.getBool("proj.plain-geo"));
     385              :         }
     386           86 :         if (oc.isSet("dlr-tdp-output")) {
     387            2 :             toFill.saveDlrTDP(oc.getString("dlr-tdp-output"));
     388              :         }
     389              : 
     390           55 :     } catch (const ProcessError& e) {
     391            3 :         if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
     392            0 :             WRITE_ERROR(e.what());
     393              :         }
     394            3 :         MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
     395              :         ret = 1;
     396              : #ifndef _DEBUG
     397            3 :     } catch (const std::exception& e) {
     398            0 :         if (std::string(e.what()) != std::string("")) {
     399            0 :             WRITE_ERROR(e.what());
     400              :         }
     401            0 :         MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
     402              :         ret = 1;
     403            0 :     } catch (...) {
     404            0 :         MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
     405              :         ret = 1;
     406              : #endif
     407            0 :     }
     408           46 :     SystemFrame::close();
     409              :     // report about ending
     410              :     if (ret == 0) {
     411              :         std::cout << "Success." << std::endl;
     412              :     }
     413              :     return ret;
     414              : }
     415              : 
     416              : 
     417              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1