LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gpsbabel - ogrgpsbabeldatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 149 188 79.3 %
Date: 2024-04-29 01:40:10 Functions: 9 10 90.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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_spawn.h"
      33             : #include "ogr_gpsbabel.h"
      34             : 
      35             : #include <cstring>
      36             : #include <algorithm>
      37             : 
      38             : /************************************************************************/
      39             : /*                      OGRGPSBabelDataSource()                         */
      40             : /************************************************************************/
      41             : 
      42          10 : OGRGPSBabelDataSource::OGRGPSBabelDataSource()
      43             : {
      44          10 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                     ~OGRGPSBabelDataSource()                         */
      48             : /************************************************************************/
      49             : 
      50          20 : OGRGPSBabelDataSource::~OGRGPSBabelDataSource()
      51             : 
      52             : {
      53          10 :     CPLFree(pszName);
      54          10 :     CPLFree(pszGPSBabelDriverName);
      55          10 :     CPLFree(pszFilename);
      56             : 
      57          10 :     OGRGPSBabelDataSource::CloseDependentDatasets();
      58             : 
      59          10 :     if (!osTmpFileName.empty())
      60           6 :         VSIUnlink(osTmpFileName.c_str());
      61          20 : }
      62             : 
      63             : /************************************************************************/
      64             : /*                     CloseDependentDatasets()                         */
      65             : /************************************************************************/
      66             : 
      67          10 : int OGRGPSBabelDataSource::CloseDependentDatasets()
      68             : {
      69          10 :     if (poGPXDS == nullptr)
      70           5 :         return FALSE;
      71             : 
      72           5 :     GDALClose(poGPXDS);
      73           5 :     poGPXDS = nullptr;
      74           5 :     return TRUE;
      75             : }
      76             : 
      77             : /************************************************************************/
      78             : /*                             GetArgv()                                */
      79             : /************************************************************************/
      80             : 
      81           5 : static char **GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes,
      82             :                       int bTracks, const char *pszGPSBabelDriverName,
      83             :                       const char *pszFilename)
      84             : {
      85           5 :     char **argv = CSLAddString(nullptr, "gpsbabel");
      86           5 :     if (bExplicitFeatures)
      87             :     {
      88           3 :         if (bWaypoints)
      89           1 :             argv = CSLAddString(argv, "-w");
      90           3 :         if (bRoutes)
      91           1 :             argv = CSLAddString(argv, "-r");
      92           3 :         if (bTracks)
      93           1 :             argv = CSLAddString(argv, "-t");
      94             :     }
      95           5 :     argv = CSLAddString(argv, "-i");
      96           5 :     argv = CSLAddString(argv, pszGPSBabelDriverName);
      97           5 :     argv = CSLAddString(argv, "-f");
      98           5 :     argv = CSLAddString(argv, pszFilename);
      99           5 :     argv = CSLAddString(argv, "-o");
     100           5 :     argv = CSLAddString(argv, "gpx,gpxver=1.1");
     101           5 :     argv = CSLAddString(argv, "-F");
     102           5 :     argv = CSLAddString(argv, "-");
     103             : 
     104           5 :     return argv;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                         IsSpecialFile()                              */
     109             : /************************************************************************/
     110             : 
     111           7 : bool OGRGPSBabelDataSource::IsSpecialFile(const char *pszFilename)
     112             : {
     113          14 :     return STARTS_WITH(pszFilename, "/dev/") ||
     114          14 :            STARTS_WITH(pszFilename, "usb:") ||
     115          14 :            (STARTS_WITH(pszFilename, "COM") && atoi(pszFilename + 3) > 0);
     116             : }
     117             : 
     118             : /************************************************************************/
     119             : /*                       IsValidDriverName()                            */
     120             : /************************************************************************/
     121             : 
     122           8 : bool OGRGPSBabelDataSource::IsValidDriverName(const char *pszGPSBabelDriverName)
     123             : {
     124          34 :     for (int i = 0; pszGPSBabelDriverName[i] != '\0'; i++)
     125             :     {
     126          26 :         char ch = pszGPSBabelDriverName[i];
     127          26 :         if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
     128           0 :               (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' ||
     129             :               ch == ','))
     130             :         {
     131           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     132             :                      "Invalid GPSBabel driver name");
     133           0 :             return false;
     134             :         }
     135             :     }
     136           8 :     return true;
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*                                Open()                                */
     141             : /************************************************************************/
     142             : 
     143          10 : int OGRGPSBabelDataSource::Open(const char *pszDatasourceName,
     144             :                                 const char *pszGPSBabelDriverNameIn,
     145             :                                 char **papszOpenOptionsIn)
     146             : 
     147             : {
     148          10 :     constexpr const char *GPSBABEL_PREFIX = "GPSBABEL:";
     149          10 :     if (!STARTS_WITH_CI(pszDatasourceName, GPSBABEL_PREFIX))
     150             :     {
     151           1 :         CPLAssert(pszGPSBabelDriverNameIn);
     152           1 :         pszGPSBabelDriverName = CPLStrdup(pszGPSBabelDriverNameIn);
     153           1 :         pszFilename = CPLStrdup(pszDatasourceName);
     154             :     }
     155             :     else
     156             :     {
     157           9 :         if (CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"))
     158           0 :             pszFilename =
     159           0 :                 CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "FILENAME"));
     160             : 
     161           9 :         if (CSLFetchNameValue(papszOpenOptionsIn, "GPSBABEL_DRIVER"))
     162             :         {
     163           0 :             if (pszFilename == nullptr)
     164             :             {
     165           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Missing FILENAME");
     166           0 :                 return FALSE;
     167             :             }
     168             : 
     169           0 :             pszGPSBabelDriverName =
     170           0 :                 CPLStrdup(CSLFetchNameValue(papszOpenOptionsIn, "DRIVER"));
     171             : 
     172             :             /* A bit of validation to avoid command line injection */
     173           0 :             if (!IsValidDriverName(pszGPSBabelDriverName))
     174           0 :                 return FALSE;
     175             :         }
     176             :     }
     177             : 
     178          10 :     pszName = CPLStrdup(pszDatasourceName);
     179             : 
     180          10 :     bool bExplicitFeatures = false;
     181          10 :     bool bWaypoints = true;
     182          10 :     bool bTracks = true;
     183          10 :     bool bRoutes = true;
     184             : 
     185          10 :     if (pszGPSBabelDriverName == nullptr)
     186             :     {
     187           9 :         const char *pszDatasourceNameAfterPrefix =
     188             :             pszDatasourceName + strlen(GPSBABEL_PREFIX);
     189           9 :         const char *pszSep = strchr(pszDatasourceNameAfterPrefix, ':');
     190           9 :         if (pszSep == nullptr)
     191             :         {
     192           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     193             :                      "Wrong syntax. Expected GPSBabel:driver_name:file_name");
     194           2 :             return FALSE;
     195             :         }
     196             : 
     197           7 :         pszGPSBabelDriverName = CPLStrdup(pszDatasourceNameAfterPrefix);
     198           7 :         pszGPSBabelDriverName[pszSep - pszDatasourceNameAfterPrefix] = '\0';
     199             : 
     200             :         /* A bit of validation to avoid command line injection */
     201           7 :         if (!IsValidDriverName(pszGPSBabelDriverName))
     202           0 :             return FALSE;
     203             : 
     204             :         /* Parse optional features= option */
     205           7 :         const char *pszAfterSep = pszSep + 1;
     206           7 :         constexpr const char *FEATURES_EQUAL = "features=";
     207           7 :         if (STARTS_WITH_CI(pszAfterSep, FEATURES_EQUAL))
     208             :         {
     209           5 :             const char *pszAfterFeaturesEqual =
     210             :                 pszAfterSep + strlen(FEATURES_EQUAL);
     211           5 :             const char *pszNextSep = strchr(pszAfterFeaturesEqual, ':');
     212           5 :             if (pszNextSep == nullptr)
     213             :             {
     214           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     215             :                          "Wrong syntax. Expected "
     216             :                          "GPSBabel:driver_name[,options]*:["
     217             :                          "features=waypoints,tracks,routes:]file_name");
     218           1 :                 return FALSE;
     219             :             }
     220             : 
     221           4 :             char *pszFeatures = CPLStrdup(pszAfterFeaturesEqual);
     222           4 :             pszFeatures[pszNextSep - pszAfterFeaturesEqual] = 0;
     223           4 :             char **papszTokens = CSLTokenizeString(pszFeatures);
     224           4 :             char **papszIter = papszTokens;
     225           4 :             bool bErr = false;
     226           4 :             bExplicitFeatures = true;
     227           4 :             bWaypoints = false;
     228           4 :             bTracks = false;
     229           4 :             bRoutes = false;
     230           8 :             while (papszIter && *papszIter)
     231             :             {
     232           4 :                 if (EQUAL(*papszIter, "waypoints"))
     233           1 :                     bWaypoints = true;
     234           3 :                 else if (EQUAL(*papszIter, "tracks"))
     235           1 :                     bTracks = true;
     236           2 :                 else if (EQUAL(*papszIter, "routes"))
     237           1 :                     bRoutes = true;
     238             :                 else
     239             :                 {
     240           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
     241             :                              "Wrong value for 'features' options");
     242           1 :                     bErr = true;
     243             :                 }
     244           4 :                 papszIter++;
     245             :             }
     246           4 :             CSLDestroy(papszTokens);
     247           4 :             CPLFree(pszFeatures);
     248             : 
     249           4 :             if (bErr)
     250           1 :                 return FALSE;
     251             : 
     252           3 :             pszAfterSep = pszNextSep + 1;
     253             :         }
     254             : 
     255           5 :         if (pszFilename == nullptr)
     256           5 :             pszFilename = CPLStrdup(pszAfterSep);
     257             :     }
     258             : 
     259             :     const char *pszOptionUseTempFile =
     260           6 :         CPLGetConfigOption("USE_TEMPFILE", nullptr);
     261           6 :     if (pszOptionUseTempFile && CPLTestBool(pszOptionUseTempFile))
     262           0 :         osTmpFileName = CPLGenerateTempFilename(nullptr);
     263             :     else
     264           6 :         osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this);
     265             : 
     266           6 :     bool bRet = false;
     267           6 :     if (IsSpecialFile(pszFilename))
     268             :     {
     269             :         /* Special file : don't try to open it */
     270           0 :         char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     271           0 :                               pszGPSBabelDriverName, pszFilename);
     272           0 :         VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     273           0 :         bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
     274           0 :         VSIFCloseL(tmpfp);
     275           0 :         tmpfp = nullptr;
     276           0 :         CSLDestroy(argv);
     277           0 :         argv = nullptr;
     278             :     }
     279             :     else
     280             :     {
     281           6 :         VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
     282           6 :         if (fp == nullptr)
     283             :         {
     284           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot open file %s",
     285             :                      pszFilename);
     286           1 :             return FALSE;
     287             :         }
     288             : 
     289          10 :         char **argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     290           5 :                               pszGPSBabelDriverName, "-");
     291             : 
     292           5 :         VSILFILE *tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     293             : 
     294           5 :         CPLPushErrorHandler(CPLQuietErrorHandler);
     295           5 :         bRet = (CPLSpawn(argv, fp, tmpfp, TRUE) == 0);
     296           5 :         CPLPopErrorHandler();
     297             : 
     298           5 :         CSLDestroy(argv);
     299           5 :         argv = nullptr;
     300             : 
     301           5 :         CPLErr nLastErrorType = CPLGetLastErrorType();
     302           5 :         CPLErrorNum nLastErrorNo = CPLGetLastErrorNo();
     303           5 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
     304             : 
     305           5 :         VSIFCloseL(tmpfp);
     306           5 :         tmpfp = nullptr;
     307             : 
     308           5 :         VSIFCloseL(fp);
     309           5 :         fp = nullptr;
     310             : 
     311           5 :         if (!bRet)
     312             :         {
     313           0 :             if (strstr(osLastErrorMsg.c_str(),
     314           0 :                        "This format cannot be used in piped commands") ==
     315             :                 nullptr)
     316             :             {
     317           0 :                 CPLError(nLastErrorType, nLastErrorNo, "%s",
     318             :                          osLastErrorMsg.c_str());
     319             :             }
     320             :             else
     321             :             {
     322             :                 VSIStatBuf sStatBuf;
     323           0 :                 if (VSIStat(pszFilename, &sStatBuf) != 0)
     324             :                 {
     325           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     326             :                              "Driver %s only supports real (non virtual) "
     327             :                              "files",
     328             :                              pszGPSBabelDriverName);
     329           0 :                     return FALSE;
     330             :                 }
     331             : 
     332             :                 /* Try without piping in */
     333           0 :                 argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes, bTracks,
     334           0 :                                pszGPSBabelDriverName, pszFilename);
     335           0 :                 tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
     336           0 :                 bRet = (CPLSpawn(argv, nullptr, tmpfp, TRUE) == 0);
     337           0 :                 VSIFCloseL(tmpfp);
     338           0 :                 tmpfp = nullptr;
     339             : 
     340           0 :                 CSLDestroy(argv);
     341           0 :                 argv = nullptr;
     342             :             }
     343             :         }
     344             :     }
     345             : 
     346           5 :     if (bRet)
     347             :     {
     348           5 :         poGPXDS = static_cast<GDALDataset *>(GDALOpenEx(
     349             :             osTmpFileName.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
     350           5 :         if (poGPXDS)
     351             :         {
     352           5 :             if (bWaypoints)
     353             :             {
     354           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("waypoints");
     355           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     356           3 :                     apoLayers[nLayers++] = poLayer;
     357             :             }
     358             : 
     359           5 :             if (bRoutes)
     360             :             {
     361           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("routes");
     362           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     363           1 :                     apoLayers[nLayers++] = poLayer;
     364           3 :                 poLayer = poGPXDS->GetLayerByName("route_points");
     365           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     366           1 :                     apoLayers[nLayers++] = poLayer;
     367             :             }
     368             : 
     369           5 :             if (bTracks)
     370             :             {
     371           3 :                 OGRLayer *poLayer = poGPXDS->GetLayerByName("tracks");
     372           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     373           3 :                     apoLayers[nLayers++] = poLayer;
     374           3 :                 poLayer = poGPXDS->GetLayerByName("track_points");
     375           3 :                 if (poLayer != nullptr && poLayer->GetFeatureCount() != 0)
     376           1 :                     apoLayers[nLayers++] = poLayer;
     377             :             }
     378             :         }
     379             :     }
     380             : 
     381           5 :     return nLayers > 0;
     382             : }
     383             : 
     384             : /************************************************************************/
     385             : /*                           TestCapability()                           */
     386             : /************************************************************************/
     387             : 
     388           0 : int OGRGPSBabelDataSource::TestCapability(const char * /* pszCap */)
     389             : {
     390           0 :     return FALSE;
     391             : }
     392             : 
     393             : /************************************************************************/
     394             : /*                              GetLayer()                              */
     395             : /************************************************************************/
     396             : 
     397           3 : OGRLayer *OGRGPSBabelDataSource::GetLayer(int iLayer)
     398             : 
     399             : {
     400           3 :     if (iLayer < 0 || iLayer >= nLayers)
     401           0 :         return nullptr;
     402             : 
     403           3 :     return apoLayers[iLayer];
     404             : }

Generated by: LCOV version 1.14