LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpsbabel - ogrgpsbabeldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 147 184 79.9 %
Date: 2024-11-21 22:18:42 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRGPSBabelDataSource class.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_conv.h"
      14             : #include "cpl_string.h"
      15             : #include "cpl_error.h"
      16             : #include "cpl_spawn.h"
      17             : #include "ogr_gpsbabel.h"
      18             : 
      19             : #include <cstring>
      20             : #include <algorithm>
      21             : 
      22             : /************************************************************************/
      23             : /*                      OGRGPSBabelDataSource()                         */
      24             : /************************************************************************/
      25             : 
      26          10 : OGRGPSBabelDataSource::OGRGPSBabelDataSource()
      27             : {
      28          10 : }
      29             : 
      30             : /************************************************************************/
      31             : /*                     ~OGRGPSBabelDataSource()                         */
      32             : /************************************************************************/
      33             : 
      34          20 : OGRGPSBabelDataSource::~OGRGPSBabelDataSource()
      35             : 
      36             : {
      37          10 :     CPLFree(pszGPSBabelDriverName);
      38          10 :     CPLFree(pszFilename);
      39             : 
      40          10 :     OGRGPSBabelDataSource::CloseDependentDatasets();
      41             : 
      42          10 :     if (!osTmpFileName.empty())
      43           6 :         VSIUnlink(osTmpFileName.c_str());
      44          20 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                     CloseDependentDatasets()                         */
      48             : /************************************************************************/
      49             : 
      50          10 : int OGRGPSBabelDataSource::CloseDependentDatasets()
      51             : {
      52          10 :     if (poGPXDS == nullptr)
      53           5 :         return FALSE;
      54             : 
      55           5 :     GDALClose(poGPXDS);
      56           5 :     poGPXDS = nullptr;
      57           5 :     return TRUE;
      58             : }
      59             : 
      60             : /************************************************************************/
      61             : /*                             GetArgv()                                */
      62             : /************************************************************************/
      63             : 
      64           5 : static char **GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes,
      65             :                       int bTracks, const char *pszGPSBabelDriverName,
      66             :                       const char *pszFilename)
      67             : {
      68           5 :     char **argv = CSLAddString(nullptr, "gpsbabel");
      69           5 :     if (bExplicitFeatures)
      70             :     {
      71           3 :         if (bWaypoints)
      72           1 :             argv = CSLAddString(argv, "-w");
      73           3 :         if (bRoutes)
      74           1 :             argv = CSLAddString(argv, "-r");
      75           3 :         if (bTracks)
      76           1 :             argv = CSLAddString(argv, "-t");
      77             :     }
      78           5 :     argv = CSLAddString(argv, "-i");
      79           5 :     argv = CSLAddString(argv, pszGPSBabelDriverName);
      80           5 :     argv = CSLAddString(argv, "-f");
      81           5 :     argv = CSLAddString(argv, pszFilename);
      82           5 :     argv = CSLAddString(argv, "-o");
      83           5 :     argv = CSLAddString(argv, "gpx,gpxver=1.1");
      84           5 :     argv = CSLAddString(argv, "-F");
      85           5 :     argv = CSLAddString(argv, "-");
      86             : 
      87           5 :     return argv;
      88             : }
      89             : 
      90             : /************************************************************************/
      91             : /*                         IsSpecialFile()                              */
      92             : /************************************************************************/
      93             : 
      94           7 : bool OGRGPSBabelDataSource::IsSpecialFile(const char *pszFilename)
      95             : {
      96          14 :     return STARTS_WITH(pszFilename, "/dev/") ||
      97          14 :            STARTS_WITH(pszFilename, "usb:") ||
      98          14 :            (STARTS_WITH(pszFilename, "COM") && atoi(pszFilename + 3) > 0);
      99             : }
     100             : 
     101             : /************************************************************************/
     102             : /*                       IsValidDriverName()                            */
     103             : /************************************************************************/
     104             : 
     105           8 : bool OGRGPSBabelDataSource::IsValidDriverName(const char *pszGPSBabelDriverName)
     106             : {
     107          34 :     for (int i = 0; pszGPSBabelDriverName[i] != '\0'; i++)
     108             :     {
     109          26 :         char ch = pszGPSBabelDriverName[i];
     110          26 :         if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
     111           0 :               (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' ||
     112             :               ch == ','))
     113             :         {
     114           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     115             :                      "Invalid GPSBabel driver name");
     116           0 :             return false;
     117             :         }
     118             :     }
     119           8 :     return true;
     120             : }
     121             : 
     122             : /************************************************************************/
     123             : /*                                Open()                                */
     124             : /************************************************************************/
     125             : 
     126          10 : int OGRGPSBabelDataSource::Open(const char *pszDatasourceName,
     127             :                                 const char *pszGPSBabelDriverNameIn,
     128             :                                 char **papszOpenOptionsIn)
     129             : 
     130             : {
     131          10 :     constexpr const char *GPSBABEL_PREFIX = "GPSBABEL:";
     132          10 :     if (!STARTS_WITH_CI(pszDatasourceName, GPSBABEL_PREFIX))
     133             :     {
     134           1 :         CPLAssert(pszGPSBabelDriverNameIn);
     135           1 :         pszGPSBabelDriverName = CPLStrdup(pszGPSBabelDriverNameIn);
     136           1 :         pszFilename = CPLStrdup(pszDatasourceName);
     137             :     }
     138             :     else
     139             :     {
     140           9 :         if (CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"))
     141           0 :             pszFilename =
     142           0 :                 CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"));
     143             : 
     144           9 :         if (CSLFetchNameValue(papszOpenOptionsIn, "GPSBABEL_DRIVER"))
     145             :         {
     146           0 :             if (pszFilename == nullptr)
     147             :             {
     148           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Missing FILENAME");
     149           0 :                 return FALSE;
     150             :             }
     151             : 
     152           0 :             pszGPSBabelDriverName =
     153           0 :                 CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "DRIVER"));
     154             : 
     155             :             /* A bit of validation to avoid command line injection */
     156           0 :             if (!IsValidDriverName(pszGPSBabelDriverName))
     157           0 :                 return FALSE;
     158             :         }
     159             :     }
     160             : 
     161          10 :     bool bExplicitFeatures = false;
     162          10 :     bool bWaypoints = true;
     163          10 :     bool bTracks = true;
     164          10 :     bool bRoutes = true;
     165             : 
     166          10 :     if (pszGPSBabelDriverName == nullptr)
     167             :     {
     168           9 :         const char *pszDatasourceNameAfterPrefix =
     169             :             pszDatasourceName + strlen(GPSBABEL_PREFIX);
     170           9 :         const char *pszSep = strchr(pszDatasourceNameAfterPrefix, ':');
     171           9 :         if (pszSep == nullptr)
     172             :         {
     173           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     174             :                      "Wrong syntax. Expected GPSBabel:driver_name:file_name");
     175           2 :             return FALSE;
     176             :         }
     177             : 
     178           7 :         pszGPSBabelDriverName = CPLStrdup(pszDatasourceNameAfterPrefix);
     179           7 :         pszGPSBabelDriverName[pszSep - pszDatasourceNameAfterPrefix] = '\0';
     180             : 
     181             :         /* A bit of validation to avoid command line injection */
     182           7 :         if (!IsValidDriverName(pszGPSBabelDriverName))
     183           0 :             return FALSE;
     184             : 
     185             :         /* Parse optional features= option */
     186           7 :         const char *pszAfterSep = pszSep + 1;
     187           7 :         constexpr const char *FEATURES_EQUAL = "features=";
     188           7 :         if (STARTS_WITH_CI(pszAfterSep, FEATURES_EQUAL))
     189             :         {
     190           5 :             const char *pszAfterFeaturesEqual =
     191             :                 pszAfterSep + strlen(FEATURES_EQUAL);
     192           5 :             const char *pszNextSep = strchr(pszAfterFeaturesEqual, ':');
     193           5 :             if (pszNextSep == nullptr)
     194             :             {
     195           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     196             :                          "Wrong syntax. Expected "
     197             :                          "GPSBabel:driver_name[,options]*:["
     198             :                          "features=waypoints,tracks,routes:]file_name");
     199           1 :                 return FALSE;
     200             :             }
     201             : 
     202           4 :             char *pszFeatures = CPLStrdup(pszAfterFeaturesEqual);
     203           4 :             pszFeatures[pszNextSep - pszAfterFeaturesEqual] = 0;
     204           4 :             char **papszTokens = CSLTokenizeString(pszFeatures);
     205           4 :             char **papszIter = papszTokens;
     206           4 :             bool bErr = false;
     207           4 :             bExplicitFeatures = true;
     208           4 :             bWaypoints = false;
     209           4 :             bTracks = false;
     210           4 :             bRoutes = false;
     211           8 :             while (papszIter && *papszIter)
     212             :             {
     213           4 :                 if (EQUAL(*papszIter, "waypoints"))
     214           1 :                     bWaypoints = true;
     215           3 :                 else if (EQUAL(*papszIter, "tracks"))
     216           1 :                     bTracks = true;
     217           2 :                 else if (EQUAL(*papszIter, "routes"))
     218           1 :                     bRoutes = true;
     219             :                 else
     220             :                 {
     221           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     222             :                              "Wrong value for 'features' options");
     223           1 :                     bErr = true;
     224             :                 }
     225           4 :                 papszIter++;
     226             :             }
     227           4 :             CSLDestroy(papszTokens);
     228           4 :             CPLFree(pszFeatures);
     229             : 
     230           4 :             if (bErr)
     231           1 :                 return FALSE;
     232             : 
     233           3 :             pszAfterSep = pszNextSep + 1;
     234             :         }
     235             : 
     236           5 :         if (pszFilename == nullptr)
     237           5 :             pszFilename = CPLStrdup(pszAfterSep);
     238             :     }
     239             : 
     240             :     const char *pszOptionUseTempFile =
     241           6 :         CPLGetConfigOption("USE_TEMPFILE", nullptr);
     242           6 :     if (pszOptionUseTempFile && CPLTestBool(pszOptionUseTempFile))
     243           0 :         osTmpFileName = CPLGenerateTempFilename(nullptr);
     244             :     else
     245           6 :         osTmpFileName = VSIMemGenerateHiddenFilename("gpsbabel");
     246             : 
     247           6 :     bool bRet = false;
     248           6 :     if (IsSpecialFile(pszFilename))
     249             :     {
     250             :         /* Special file : don't try to open it */
     251           0 :         char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     252           0 :                               pszGPSBabelDriverName, pszFilename);
     253           0 :         VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     254           0 :         bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
     255           0 :         VSIFCloseL(tmpfp);
     256           0 :         tmpfp = nullptr;
     257           0 :         CSLDestroy(argv);
     258           0 :         argv = nullptr;
     259             :     }
     260             :     else
     261             :     {
     262           6 :         VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
     263           6 :         if (fp == nullptr)
     264             :         {
     265           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot open file %s",
     266             :                      pszFilename);
     267           1 :             return FALSE;
     268             :         }
     269             : 
     270          10 :         char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     271           5 :                               pszGPSBabelDriverName, "-");
     272             : 
     273           5 :         VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     274             : 
     275           5 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     276           5 :         bRet = (CPLSpawn(argv, fp, tmpfp, TRUE) == 0);
     277           5 :         CPLPopErrorHandler();
     278             : 
     279           5 :         CSLDestroy(argv);
     280           5 :         argv = nullptr;
     281             : 
     282           5 :         CPLErr nLastErrorType = CPLGetLastErrorType();
     283           5 :         CPLErrorNum nLastErrorNo = CPLGetLastErrorNo();
     284           5 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     285             : 
     286           5 :         VSIFCloseL(tmpfp);
     287           5 :         tmpfp = nullptr;
     288             : 
     289           5 :         VSIFCloseL(fp);
     290           5 :         fp = nullptr;
     291             : 
     292           5 :         if (!bRet)
     293             :         {
     294           0 :             if (strstr(osLastErrorMsg.c_str(),
     295           0 :                        "This format cannot be used in piped commands") ==
     296             :                 nullptr)
     297             :             {
     298           0 :                 CPLError(nLastErrorType, nLastErrorNo, "%s",
     299             :                          osLastErrorMsg.c_str());
     300             :             }
     301             :             else
     302             :             {
     303             :                 VSIStatBuf sStatBuf;
     304           0 :                 if (VSIStat(pszFilename, &sStatBuf) != 0)
     305             :                 {
     306           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     307             :                              "Driver %s only supports real (non virtual) "
     308             :                              "files",
     309             :                              pszGPSBabelDriverName);
     310           0 :                     return FALSE;
     311             :                 }
     312             : 
     313             :                 /* Try without piping in */
     314           0 :                 argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     315           0 :                                pszGPSBabelDriverName, pszFilename);
     316           0 :                 tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     317           0 :                 bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
     318           0 :                 VSIFCloseL(tmpfp);
     319           0 :                 tmpfp = nullptr;
     320             : 
     321           0 :                 CSLDestroy(argv);
     322           0 :                 argv = nullptr;
     323             :             }
     324             :         }
     325             :     }
     326             : 
     327           5 :     if (bRet)
     328             :     {
     329           5 :         poGPXDS = static_cast<GDALDataset *>(GDALOpenEx(
     330             :             osTmpFileName.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
     331           5 :         if (poGPXDS)
     332             :         {
     333           5 :             if (bWaypoints)
     334             :             {
     335           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("waypoints");
     336           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     337           3 :                     apoLayers[nLayers++] = poLayer;
     338             :             }
     339             : 
     340           5 :             if (bRoutes)
     341             :             {
     342           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("routes");
     343           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     344           1 :                     apoLayers[nLayers++] = poLayer;
     345           3 :                 poLayer = poGPXDS->GetLayerByName("route_points");
     346           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     347           1 :                     apoLayers[nLayers++] = poLayer;
     348             :             }
     349             : 
     350           5 :             if (bTracks)
     351             :             {
     352           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("tracks");
     353           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     354           3 :                     apoLayers[nLayers++] = poLayer;
     355           3 :                 poLayer = poGPXDS->GetLayerByName("track_points");
     356           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     357           1 :                     apoLayers[nLayers++] = poLayer;
     358             :             }
     359             :         }
     360             :     }
     361             : 
     362           5 :     return nLayers > 0;
     363             : }
     364             : 
     365             : /************************************************************************/
     366             : /*                              GetLayer()                              */
     367             : /************************************************************************/
     368             : 
     369           3 : OGRLayer *OGRGPSBabelDataSource::GetLayer(int iLayer)
     370             : 
     371             : {
     372           3 :     if (iLayer < 0 || iLayer >= nLayers)
     373           0 :         return nullptr;
     374             : 
     375           3 :     return apoLayers[iLayer];
     376             : }

Generated by: LCOV version 1.14