LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapedatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 584 733 79.7 %
Date: 2025-05-31 00:00:17 Functions: 27 30 90.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRShapeDataSource class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999,  Les Technologies SoftMap Inc.
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "ogrshape.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <cstddef>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <memory>
      22             : #include <set>
      23             : #include <vector>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_error.h"
      27             : #include "cpl_string.h"
      28             : #include "cpl_vsi.h"
      29             : #include "cpl_vsi_error.h"
      30             : #include "gdal.h"
      31             : #include "gdal_priv.h"
      32             : #include "ogr_core.h"
      33             : #include "ogr_geometry.h"
      34             : #include "ogr_spatialref.h"
      35             : #include "ogrlayerpool.h"
      36             : #include "ogrsf_frmts.h"
      37             : #include "shapefil.h"
      38             : #include "shp_vsi.h"
      39             : 
      40             : // #define IMMEDIATE_OPENING 1
      41             : 
      42             : constexpr int knREFRESH_LOCK_FILE_DELAY_SEC = 10;
      43             : 
      44             : /************************************************************************/
      45             : /*                          DS_SHPOpen()                                */
      46             : /************************************************************************/
      47             : 
      48        3901 : SHPHandle OGRShapeDataSource::DS_SHPOpen(const char *pszShapeFile,
      49             :                                          const char *pszAccess)
      50             : {
      51             :     // Do lazy shx loading for /vsicurl/
      52        3901 :     if (STARTS_WITH(pszShapeFile, "/vsicurl/") && strcmp(pszAccess, "r") == 0)
      53           0 :         pszAccess = "rl";
      54             : 
      55             :     const bool bRestoreSHX =
      56        3901 :         CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
      57        3901 :     SHPHandle hSHP = SHPOpenLLEx(
      58             :         pszShapeFile, pszAccess,
      59        3901 :         const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)), bRestoreSHX);
      60             : 
      61        3901 :     if (hSHP != nullptr)
      62        3568 :         SHPSetFastModeReadObject(hSHP, TRUE);
      63        3901 :     return hSHP;
      64             : }
      65             : 
      66             : /************************************************************************/
      67             : /*                           DS_DBFOpen()                               */
      68             : /************************************************************************/
      69             : 
      70        3901 : DBFHandle OGRShapeDataSource::DS_DBFOpen(const char *pszDBFFile,
      71             :                                          const char *pszAccess)
      72             : {
      73             :     DBFHandle hDBF =
      74        3901 :         DBFOpenLL(pszDBFFile, pszAccess,
      75        3901 :                   const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
      76        3901 :     return hDBF;
      77             : }
      78             : 
      79             : /************************************************************************/
      80             : /*                         OGRShapeDataSource()                         */
      81             : /************************************************************************/
      82             : 
      83        3189 : OGRShapeDataSource::OGRShapeDataSource()
      84             :     : m_poPool(std::make_unique<OGRLayerPool>()),
      85        3189 :       m_b2GBLimit(CPLTestBool(CPLGetConfigOption("SHAPE_2GB_LIMIT", "FALSE")))
      86             : {
      87        3189 : }
      88             : 
      89             : /************************************************************************/
      90             : /*                             GetLayerNames()                          */
      91             : /************************************************************************/
      92             : 
      93           6 : std::vector<CPLString> OGRShapeDataSource::GetLayerNames() const
      94             : {
      95           6 :     std::vector<CPLString> res;
      96           6 :     const_cast<OGRShapeDataSource *>(this)->GetLayerCount();
      97          12 :     for (const auto &poLayer : m_apoLayers)
      98             :     {
      99           6 :         res.emplace_back(poLayer->GetName());
     100             :     }
     101           6 :     return res;
     102             : }
     103             : 
     104             : /************************************************************************/
     105             : /*                        ~OGRShapeDataSource()                         */
     106             : /************************************************************************/
     107             : 
     108        6364 : OGRShapeDataSource::~OGRShapeDataSource()
     109             : 
     110             : {
     111        6364 :     std::vector<CPLString> layerNames;
     112        3182 :     if (!m_osTemporaryUnzipDir.empty())
     113             :     {
     114           6 :         layerNames = GetLayerNames();
     115             :     }
     116        3182 :     m_apoLayers.clear();
     117        3182 :     m_poPool.reset();
     118             : 
     119        3182 :     RecompressIfNeeded(layerNames);
     120        3182 :     RemoveLockFile();
     121             : 
     122             :     // Free mutex & cond
     123        3182 :     if (m_poRefreshLockFileMutex)
     124             :     {
     125           0 :         CPLDestroyMutex(m_poRefreshLockFileMutex);
     126           0 :         m_poRefreshLockFileMutex = nullptr;
     127             :     }
     128        3182 :     if (m_poRefreshLockFileCond)
     129             :     {
     130           0 :         CPLDestroyCond(m_poRefreshLockFileCond);
     131           0 :         m_poRefreshLockFileCond = nullptr;
     132             :     }
     133        6364 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                              OpenZip()                               */
     137             : /************************************************************************/
     138             : 
     139           8 : bool OGRShapeDataSource::OpenZip(GDALOpenInfo *poOpenInfo,
     140             :                                  const char *pszOriFilename)
     141             : {
     142           8 :     if (!Open(poOpenInfo, true))
     143           0 :         return false;
     144             : 
     145           8 :     SetDescription(pszOriFilename);
     146             : 
     147           8 :     m_bIsZip = true;
     148           8 :     m_bSingleLayerZip =
     149           8 :         EQUAL(CPLGetExtensionSafe(pszOriFilename).c_str(), "shz");
     150             : 
     151           8 :     if (!m_bSingleLayerZip)
     152             :     {
     153           2 :         CPLString osLockFile(GetDescription());
     154           1 :         osLockFile += ".gdal.lock";
     155             :         VSIStatBufL sStat;
     156           1 :         if (VSIStatL(osLockFile, &sStat) == 0 &&
     157           0 :             sStat.st_mtime < time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC)
     158             :         {
     159           0 :             CPLDebug("Shape", "Deleting stalled %s", osLockFile.c_str());
     160           0 :             VSIUnlink(osLockFile);
     161             :         }
     162             :     }
     163             : 
     164           8 :     return true;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                            CreateZip()                               */
     169             : /************************************************************************/
     170             : 
     171           4 : bool OGRShapeDataSource::CreateZip(const char *pszOriFilename)
     172             : {
     173           4 :     CPLAssert(m_apoLayers.empty());
     174             : 
     175           4 :     void *hZIP = CPLCreateZip(pszOriFilename, nullptr);
     176           4 :     if (!hZIP)
     177           1 :         return false;
     178           3 :     if (CPLCloseZip(hZIP) != CE_None)
     179           0 :         return false;
     180           3 :     eAccess = GA_Update;
     181           3 :     m_bIsZip = true;
     182           3 :     m_bSingleLayerZip =
     183           3 :         EQUAL(CPLGetExtensionSafe(pszOriFilename).c_str(), "shz");
     184           3 :     return true;
     185             : }
     186             : 
     187             : /************************************************************************/
     188             : /*                                Open()                                */
     189             : /************************************************************************/
     190             : 
     191        3185 : bool OGRShapeDataSource::Open(GDALOpenInfo *poOpenInfo, bool bTestOpen,
     192             :                               bool bForceSingleFileDataSource)
     193             : 
     194             : {
     195        3185 :     CPLAssert(m_apoLayers.empty());
     196             : 
     197        3185 :     const char *pszNewName = poOpenInfo->pszFilename;
     198        3185 :     const bool bUpdate = poOpenInfo->eAccess == GA_Update;
     199        3185 :     CPLAssert(papszOpenOptions == nullptr);
     200        3185 :     papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
     201             : 
     202        3185 :     eAccess = poOpenInfo->eAccess;
     203             : 
     204        3185 :     m_bSingleFileDataSource = CPL_TO_BOOL(bForceSingleFileDataSource);
     205             : 
     206             :     /* -------------------------------------------------------------------- */
     207             :     /*      If m_bSingleFileDataSource is TRUE we don't try to do anything  */
     208             :     /*      else.                                                           */
     209             :     /*      This is only utilized when the OGRShapeDriver::Create()         */
     210             :     /*      method wants to create a stub OGRShapeDataSource for a          */
     211             :     /*      single shapefile.  The driver will take care of creating the    */
     212             :     /*      file by calling ICreateLayer().                                 */
     213             :     /* -------------------------------------------------------------------- */
     214        3185 :     if (m_bSingleFileDataSource)
     215         483 :         return true;
     216             : 
     217             :     /* -------------------------------------------------------------------- */
     218             :     /*      Is the given path a directory or a regular file?                */
     219             :     /* -------------------------------------------------------------------- */
     220        2702 :     if (!poOpenInfo->bStatOK)
     221             :     {
     222           0 :         if (!bTestOpen)
     223           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     224             :                      "%s is neither a file or directory, Shape access failed.",
     225             :                      pszNewName);
     226             : 
     227           0 :         return false;
     228             :     }
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      Build a list of filenames we figure are Shape files.            */
     232             :     /* -------------------------------------------------------------------- */
     233        2702 :     if (!poOpenInfo->bIsDirectory)
     234             :     {
     235        1545 :         if (!OpenFile(pszNewName, bUpdate))
     236             :         {
     237           1 :             if (!bTestOpen)
     238           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     239             :                          "Failed to open shapefile %s.  "
     240             :                          "It may be corrupt or read-only file accessed in "
     241             :                          "update mode.",
     242             :                          pszNewName);
     243             : 
     244           1 :             return false;
     245             :         }
     246             : 
     247        1544 :         m_bSingleFileDataSource = true;
     248             : 
     249        1544 :         return true;
     250             :     }
     251             :     else
     252             :     {
     253        2314 :         const CPLStringList aosCandidates(VSIReadDir(pszNewName));
     254        1157 :         const int nCandidateCount = aosCandidates.size();
     255        1157 :         bool bMightBeOldCoverage = false;
     256        1157 :         std::set<CPLString> osLayerNameSet;
     257             : 
     258       49798 :         for (int iCan = 0; iCan < nCandidateCount; iCan++)
     259             :         {
     260       48641 :             const char *pszCandidate = aosCandidates[iCan];
     261       48641 :             CPLString osLayerName(CPLGetBasenameSafe(pszCandidate));
     262             : #ifdef _WIN32
     263             :             // On Windows, as filenames are case insensitive, a shapefile layer
     264             :             // can be made of foo.shp and FOO.DBF, so to detect unique layer
     265             :             // names, put them upper case in the unique set used for detection.
     266             :             osLayerName.toupper();
     267             : #endif
     268             : 
     269       48641 :             if (EQUAL(pszCandidate, "ARC"))
     270           0 :                 bMightBeOldCoverage = true;
     271             : 
     272       48641 :             if (strlen(pszCandidate) < 4 ||
     273       42914 :                 !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".shp"))
     274       44914 :                 continue;
     275             : 
     276             :             std::string osFilename =
     277        3727 :                 CPLFormFilenameSafe(pszNewName, pszCandidate, nullptr);
     278             : 
     279        3727 :             osLayerNameSet.insert(std::move(osLayerName));
     280             : #ifdef IMMEDIATE_OPENING
     281             :             if (!OpenFile(osFilename.c_str(), bUpdate) && !bTestOpen)
     282             :             {
     283             :                 CPLError(CE_Failure, CPLE_OpenFailed,
     284             :                          "Failed to open shapefile %s.  "
     285             :                          "It may be corrupt or read-only file accessed in "
     286             :                          "update mode.",
     287             :                          osFilename.c_str());
     288             :                 return false;
     289             :             }
     290             : #else
     291        3727 :             m_oVectorLayerName.push_back(std::move(osFilename));
     292             : #endif
     293             :         }
     294             : 
     295             :         // Try and .dbf files without apparent associated shapefiles.
     296       49798 :         for (int iCan = 0; iCan < nCandidateCount; iCan++)
     297             :         {
     298       48641 :             const char *pszCandidate = aosCandidates[iCan];
     299       48641 :             const std::string osLayerNameOri = CPLGetBasenameSafe(pszCandidate);
     300       48641 :             CPLString osLayerName(osLayerNameOri);
     301             : #ifdef _WIN32
     302             :             osLayerName.toupper();
     303             : #endif
     304             : 
     305             :             // We don't consume .dbf files in a directory that looks like
     306             :             // an old style Arc/Info (for PC?) that unless we found at least
     307             :             // some shapefiles.  See Bug 493.
     308       48641 :             if (bMightBeOldCoverage && osLayerNameSet.empty())
     309           0 :                 continue;
     310             : 
     311       48641 :             if (strlen(pszCandidate) < 4 ||
     312       42914 :                 !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".dbf"))
     313       44356 :                 continue;
     314             : 
     315        4285 :             if (osLayerNameSet.find(osLayerName) != osLayerNameSet.end())
     316        3726 :                 continue;
     317             : 
     318             :             // We don't want to access .dbf files with an associated .tab
     319             :             // file, or it will never get recognised as a mapinfo dataset.
     320         559 :             bool bFoundTAB = false;
     321       36463 :             for (int iCan2 = 0; iCan2 < nCandidateCount; iCan2++)
     322             :             {
     323       35904 :                 const char *pszCandidate2 = aosCandidates[iCan2];
     324             : 
     325       35904 :                 if (EQUALN(pszCandidate2, osLayerNameOri.c_str(),
     326       37194 :                            osLayerNameOri.size()) &&
     327        1290 :                     EQUAL(pszCandidate2 + osLayerNameOri.size(), ".tab"))
     328           0 :                     bFoundTAB = true;
     329             :             }
     330             : 
     331         559 :             if (bFoundTAB)
     332           0 :                 continue;
     333             : 
     334             :             std::string osFilename =
     335         559 :                 CPLFormFilenameSafe(pszNewName, pszCandidate, nullptr);
     336             : 
     337         559 :             osLayerNameSet.insert(std::move(osLayerName));
     338             : 
     339             : #ifdef IMMEDIATE_OPENING
     340             :             if (!OpenFile(osFilename.c_str(), bUpdate) && !bTestOpen)
     341             :             {
     342             :                 CPLError(CE_Failure, CPLE_OpenFailed,
     343             :                          "Failed to open dbf file %s.  "
     344             :                          "It may be corrupt or read-only file accessed in "
     345             :                          "update mode.",
     346             :                          osFilename.c_str());
     347             :                 return false;
     348             :             }
     349             : #else
     350         559 :             m_oVectorLayerName.push_back(std::move(osFilename));
     351             : #endif
     352             :         }
     353             : 
     354             : #ifdef IMMEDIATE_OPENING
     355             :         const int nDirLayers = static_cast<int>(m_apoLayers.size());
     356             : #else
     357        1157 :         const int nDirLayers = static_cast<int>(m_oVectorLayerName.size());
     358             : #endif
     359             : 
     360        1157 :         CPLErrorReset();
     361             : 
     362        1157 :         return nDirLayers > 0 || !bTestOpen;
     363             :     }
     364             : }
     365             : 
     366             : /************************************************************************/
     367             : /*                              OpenFile()                              */
     368             : /************************************************************************/
     369             : 
     370        3867 : bool OGRShapeDataSource::OpenFile(const char *pszNewName, bool bUpdate)
     371             : 
     372             : {
     373        7734 :     const std::string osExtension = CPLGetExtensionSafe(pszNewName);
     374             : 
     375        3867 :     if (!EQUAL(osExtension.c_str(), "shp") &&
     376        4218 :         !EQUAL(osExtension.c_str(), "shx") &&
     377         351 :         !EQUAL(osExtension.c_str(), "dbf"))
     378           0 :         return false;
     379             : 
     380             :     /* -------------------------------------------------------------------- */
     381             :     /*      SHPOpen() should include better (CPL based) error reporting,    */
     382             :     /*      and we should be trying to distinguish at this point whether    */
     383             :     /*      failure is a result of trying to open a non-shapefile, or       */
     384             :     /*      whether it was a shapefile and we want to report the error      */
     385             :     /*      up.                                                             */
     386             :     /*                                                                      */
     387             :     /*      Care is taken to suppress the error and only reissue it if      */
     388             :     /*      we think it is appropriate.                                     */
     389             :     /* -------------------------------------------------------------------- */
     390             :     const bool bRealUpdateAccess =
     391        3867 :         bUpdate && (!IsZip() || !GetTemporaryUnzipDir().empty());
     392        3867 :     CPLErrorReset();
     393        3867 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     394        3867 :     SHPHandle hSHP = bRealUpdateAccess ? DS_SHPOpen(pszNewName, "r+")
     395        2213 :                                        : DS_SHPOpen(pszNewName, "r");
     396        3867 :     CPLPopErrorHandler();
     397             : 
     398             :     const bool bRestoreSHX =
     399        3867 :         CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
     400        3868 :     if (bRestoreSHX && EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf") &&
     401           1 :         CPLGetLastErrorMsg()[0] != '\0')
     402             :     {
     403           2 :         CPLString osMsg = CPLGetLastErrorMsg();
     404             : 
     405           1 :         CPLError(CE_Warning, CPLE_AppDefined, "%s", osMsg.c_str());
     406             :     }
     407             :     else
     408             :     {
     409        4198 :         if (hSHP == nullptr &&
     410        4198 :             (!EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf") ||
     411         331 :              strstr(CPLGetLastErrorMsg(), ".shp") == nullptr))
     412             :         {
     413           1 :             CPLString osMsg = CPLGetLastErrorMsg();
     414             : 
     415           1 :             CPLError(CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str());
     416             : 
     417           1 :             return false;
     418             :         }
     419        3865 :         CPLErrorReset();
     420             :     }
     421             : 
     422             :     /* -------------------------------------------------------------------- */
     423             :     /*      Open the .dbf file, if it exists.  To open a dbf file, the      */
     424             :     /*      filename has to either refer to a successfully opened shp       */
     425             :     /*      file or has to refer to the actual .dbf file.                   */
     426             :     /* -------------------------------------------------------------------- */
     427        3866 :     DBFHandle hDBF = nullptr;
     428        4198 :     if (hSHP != nullptr ||
     429        4198 :         EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf"))
     430             :     {
     431        3866 :         if (bRealUpdateAccess)
     432             :         {
     433        1654 :             hDBF = DS_DBFOpen(pszNewName, "r+");
     434        1654 :             if (hSHP != nullptr && hDBF == nullptr)
     435             :             {
     436          22 :                 for (int i = 0; i < 2; i++)
     437             :                 {
     438             :                     VSIStatBufL sStat;
     439             :                     const std::string osDBFName = CPLResetExtensionSafe(
     440          15 :                         pszNewName, (i == 0) ? "dbf" : "DBF");
     441          15 :                     VSILFILE *fp = nullptr;
     442          15 :                     if (VSIStatExL(osDBFName.c_str(), &sStat,
     443          15 :                                    VSI_STAT_EXISTS_FLAG) == 0)
     444             :                     {
     445           1 :                         fp = VSIFOpenL(osDBFName.c_str(), "r+");
     446           1 :                         if (fp == nullptr)
     447             :                         {
     448           1 :                             CPLError(CE_Failure, CPLE_OpenFailed,
     449             :                                      "%s exists, "
     450             :                                      "but cannot be opened in update mode",
     451             :                                      osDBFName.c_str());
     452           1 :                             SHPClose(hSHP);
     453           1 :                             return false;
     454             :                         }
     455           0 :                         VSIFCloseL(fp);
     456           0 :                         break;
     457             :                     }
     458             :                 }
     459             :             }
     460             :         }
     461             :         else
     462             :         {
     463        2212 :             hDBF = DS_DBFOpen(pszNewName, "r");
     464             :         }
     465             :     }
     466             :     else
     467             :     {
     468           0 :         hDBF = nullptr;
     469             :     }
     470             : 
     471        3865 :     if (hDBF == nullptr && hSHP == nullptr)
     472           0 :         return false;
     473             : 
     474             :     /* -------------------------------------------------------------------- */
     475             :     /*      Create the layer object.                                        */
     476             :     /* -------------------------------------------------------------------- */
     477             :     auto poLayer = std::make_unique<OGRShapeLayer>(
     478             :         this, pszNewName, hSHP, hDBF,
     479           0 :         /* poSRS = */ nullptr,
     480           0 :         /* bSRSSet = */ false,
     481        3865 :         /* osPrjFilename = */ std::string(), bUpdate, wkbNone);
     482        7730 :     poLayer->SetModificationDate(
     483        3865 :         CSLFetchNameValue(papszOpenOptions, "DBF_DATE_LAST_UPDATE"));
     484        3865 :     poLayer->SetAutoRepack(CPLFetchBool(papszOpenOptions, "AUTO_REPACK", true));
     485        3865 :     poLayer->SetWriteDBFEOFChar(
     486        3865 :         CPLFetchBool(papszOpenOptions, "DBF_EOF_CHAR", true));
     487             : 
     488             :     /* -------------------------------------------------------------------- */
     489             :     /*      Add layer to data source layer list.                            */
     490             :     /* -------------------------------------------------------------------- */
     491        3865 :     AddLayer(std::move(poLayer));
     492             : 
     493        3865 :     return true;
     494             : }
     495             : 
     496             : /************************************************************************/
     497             : /*                             AddLayer()                               */
     498             : /************************************************************************/
     499             : 
     500        5523 : void OGRShapeDataSource::AddLayer(std::unique_ptr<OGRShapeLayer> poLayer)
     501             : {
     502        5523 :     m_apoLayers.push_back(std::move(poLayer));
     503             : 
     504             :     // If we reach the limit, then register all the already opened layers
     505             :     // Technically this code would not be necessary if there was not the
     506             :     // following initial test in SetLastUsedLayer() :
     507             :     //      if (static_cast<int>(m_apoLayers.size()) < MAX_SIMULTANEOUSLY_OPENED_LAYERS)
     508             :     //         return;
     509        5523 :     if (static_cast<int>(m_apoLayers.size()) ==
     510        5528 :             m_poPool->GetMaxSimultaneouslyOpened() &&
     511           5 :         m_poPool->GetSize() == 0)
     512             :     {
     513         505 :         for (auto &poIterLayer : m_apoLayers)
     514         500 :             m_poPool->SetLastUsedLayer(poIterLayer.get());
     515             :     }
     516        5523 : }
     517             : 
     518             : /************************************************************************/
     519             : /*                        LaunderLayerName()                            */
     520             : /************************************************************************/
     521             : 
     522        1182 : static CPLString LaunderLayerName(const char *pszLayerName)
     523             : {
     524        2364 :     std::string osRet(CPLLaunderForFilenameSafe(pszLayerName, nullptr));
     525        1182 :     if (osRet != pszLayerName)
     526             :     {
     527           1 :         CPLError(CE_Warning, CPLE_AppDefined,
     528             :                  "Invalid layer name for a shapefile: %s. Laundered to %s.",
     529             :                  pszLayerName, osRet.c_str());
     530             :     }
     531        2364 :     return osRet;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                           ICreateLayer()                             */
     536             : /************************************************************************/
     537             : 
     538             : OGRLayer *
     539        1670 : OGRShapeDataSource::ICreateLayer(const char *pszLayerName,
     540             :                                  const OGRGeomFieldDefn *poGeomFieldDefn,
     541             :                                  CSLConstList papszOptions)
     542             : 
     543             : {
     544             :     // To ensure that existing layers are created.
     545        1670 :     GetLayerCount();
     546             : 
     547        1670 :     auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     548             :     const auto poSRS =
     549        1670 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     550             : 
     551             :     /* -------------------------------------------------------------------- */
     552             :     /*      Check that the layer doesn't already exist.                     */
     553             :     /* -------------------------------------------------------------------- */
     554        1670 :     if (GetLayerByName(pszLayerName) != nullptr)
     555             :     {
     556           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer '%s' already exists",
     557             :                  pszLayerName);
     558           2 :         return nullptr;
     559             :     }
     560             : 
     561             :     /* -------------------------------------------------------------------- */
     562             :     /*      Verify we are in update mode.                                   */
     563             :     /* -------------------------------------------------------------------- */
     564        1668 :     if (eAccess == GA_ReadOnly)
     565             :     {
     566           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     567             :                  "Data source %s opened read-only.  "
     568             :                  "New layer %s cannot be created.",
     569           0 :                  GetDescription(), pszLayerName);
     570             : 
     571           0 :         return nullptr;
     572             :     }
     573             : 
     574        1668 :     if (m_bIsZip && m_bSingleLayerZip && m_apoLayers.size() == 1)
     575             :     {
     576           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     577             :                  ".shz only supports one single layer");
     578           1 :         return nullptr;
     579             :     }
     580             : 
     581        1667 :     if (!UncompressIfNeeded())
     582           0 :         return nullptr;
     583             : 
     584             :     /* -------------------------------------------------------------------- */
     585             :     /*      Figure out what type of layer we need.                          */
     586             :     /* -------------------------------------------------------------------- */
     587        1667 :     int nShapeType = -1;
     588             : 
     589        1667 :     if (wkbFlatten(eType) == wkbUnknown || eType == wkbLineString)
     590        1292 :         nShapeType = SHPT_ARC;
     591         375 :     else if (eType == wkbPoint)
     592          42 :         nShapeType = SHPT_POINT;
     593         333 :     else if (eType == wkbPolygon || eType == wkbTriangle)
     594         147 :         nShapeType = SHPT_POLYGON;
     595         186 :     else if (eType == wkbMultiPoint)
     596           7 :         nShapeType = SHPT_MULTIPOINT;
     597         179 :     else if (eType == wkbPoint25D)
     598           6 :         nShapeType = SHPT_POINTZ;
     599         173 :     else if (eType == wkbPointM)
     600           2 :         nShapeType = SHPT_POINTM;
     601         171 :     else if (eType == wkbPointZM)
     602           2 :         nShapeType = SHPT_POINTZ;
     603         169 :     else if (eType == wkbLineString25D)
     604          12 :         nShapeType = SHPT_ARCZ;
     605         157 :     else if (eType == wkbLineStringM)
     606           2 :         nShapeType = SHPT_ARCM;
     607         155 :     else if (eType == wkbLineStringZM)
     608           2 :         nShapeType = SHPT_ARCZ;
     609         153 :     else if (eType == wkbMultiLineString)
     610          12 :         nShapeType = SHPT_ARC;
     611         141 :     else if (eType == wkbMultiLineString25D)
     612           5 :         nShapeType = SHPT_ARCZ;
     613         136 :     else if (eType == wkbMultiLineStringM)
     614           1 :         nShapeType = SHPT_ARCM;
     615         135 :     else if (eType == wkbMultiLineStringZM)
     616           1 :         nShapeType = SHPT_ARCZ;
     617         134 :     else if (eType == wkbPolygon25D || eType == wkbTriangleZ)
     618          12 :         nShapeType = SHPT_POLYGONZ;
     619         122 :     else if (eType == wkbPolygonM || eType == wkbTriangleM)
     620           2 :         nShapeType = SHPT_POLYGONM;
     621         120 :     else if (eType == wkbPolygonZM || eType == wkbTriangleZM)
     622           2 :         nShapeType = SHPT_POLYGONZ;
     623         118 :     else if (eType == wkbMultiPolygon)
     624          43 :         nShapeType = SHPT_POLYGON;
     625          75 :     else if (eType == wkbMultiPolygon25D)
     626           5 :         nShapeType = SHPT_POLYGONZ;
     627          70 :     else if (eType == wkbMultiPolygonM)
     628           1 :         nShapeType = SHPT_POLYGONM;
     629          69 :     else if (eType == wkbMultiPolygonZM)
     630           1 :         nShapeType = SHPT_POLYGONZ;
     631          68 :     else if (eType == wkbMultiPoint25D)
     632           6 :         nShapeType = SHPT_MULTIPOINTZ;
     633          62 :     else if (eType == wkbMultiPointM)
     634           2 :         nShapeType = SHPT_MULTIPOINTM;
     635          60 :     else if (eType == wkbMultiPointZM)
     636           2 :         nShapeType = SHPT_MULTIPOINTZ;
     637         112 :     else if (wkbFlatten(eType) == wkbTIN ||
     638          54 :              wkbFlatten(eType) == wkbPolyhedralSurface)
     639           4 :         nShapeType = SHPT_MULTIPATCH;
     640          54 :     else if (eType == wkbNone)
     641          50 :         nShapeType = SHPT_NULL;
     642             : 
     643             :     /* -------------------------------------------------------------------- */
     644             :     /*      Has the application overridden this with a special creation     */
     645             :     /*      option?                                                         */
     646             :     /* -------------------------------------------------------------------- */
     647        1667 :     const char *pszOverride = CSLFetchNameValue(papszOptions, "SHPT");
     648             : 
     649        1667 :     if (pszOverride == nullptr)
     650             :     {
     651             :         /* ignore */;
     652             :     }
     653          28 :     else if (EQUAL(pszOverride, "POINT"))
     654             :     {
     655           1 :         nShapeType = SHPT_POINT;
     656           1 :         eType = wkbPoint;
     657             :     }
     658          27 :     else if (EQUAL(pszOverride, "ARC"))
     659             :     {
     660           2 :         nShapeType = SHPT_ARC;
     661           2 :         eType = wkbLineString;
     662             :     }
     663          25 :     else if (EQUAL(pszOverride, "POLYGON"))
     664             :     {
     665           2 :         nShapeType = SHPT_POLYGON;
     666           2 :         eType = wkbPolygon;
     667             :     }
     668          23 :     else if (EQUAL(pszOverride, "MULTIPOINT"))
     669             :     {
     670           1 :         nShapeType = SHPT_MULTIPOINT;
     671           1 :         eType = wkbMultiPoint;
     672             :     }
     673          22 :     else if (EQUAL(pszOverride, "POINTZ"))
     674             :     {
     675           1 :         nShapeType = SHPT_POINTZ;
     676           1 :         eType = wkbPoint25D;
     677             :     }
     678          21 :     else if (EQUAL(pszOverride, "ARCZ"))
     679             :     {
     680           2 :         nShapeType = SHPT_ARCZ;
     681           2 :         eType = wkbLineString25D;
     682             :     }
     683          19 :     else if (EQUAL(pszOverride, "POLYGONZ"))
     684             :     {
     685           4 :         nShapeType = SHPT_POLYGONZ;
     686           4 :         eType = wkbPolygon25D;
     687             :     }
     688          15 :     else if (EQUAL(pszOverride, "MULTIPOINTZ"))
     689             :     {
     690           1 :         nShapeType = SHPT_MULTIPOINTZ;
     691           1 :         eType = wkbMultiPoint25D;
     692             :     }
     693          14 :     else if (EQUAL(pszOverride, "POINTM"))
     694             :     {
     695           1 :         nShapeType = SHPT_POINTM;
     696           1 :         eType = wkbPointM;
     697             :     }
     698          13 :     else if (EQUAL(pszOverride, "ARCM"))
     699             :     {
     700           2 :         nShapeType = SHPT_ARCM;
     701           2 :         eType = wkbLineStringM;
     702             :     }
     703          11 :     else if (EQUAL(pszOverride, "POLYGONM"))
     704             :     {
     705           2 :         nShapeType = SHPT_POLYGONM;
     706           2 :         eType = wkbPolygonM;
     707             :     }
     708           9 :     else if (EQUAL(pszOverride, "MULTIPOINTM"))
     709             :     {
     710           1 :         nShapeType = SHPT_MULTIPOINTM;
     711           1 :         eType = wkbMultiPointM;
     712             :     }
     713           8 :     else if (EQUAL(pszOverride, "POINTZM"))
     714             :     {
     715           1 :         nShapeType = SHPT_POINTZ;
     716           1 :         eType = wkbPointZM;
     717             :     }
     718           7 :     else if (EQUAL(pszOverride, "ARCZM"))
     719             :     {
     720           2 :         nShapeType = SHPT_ARCZ;
     721           2 :         eType = wkbLineStringZM;
     722             :     }
     723           5 :     else if (EQUAL(pszOverride, "POLYGONZM"))
     724             :     {
     725           2 :         nShapeType = SHPT_POLYGONZ;
     726           2 :         eType = wkbPolygonZM;
     727             :     }
     728           3 :     else if (EQUAL(pszOverride, "MULTIPOINTZM"))
     729             :     {
     730           1 :         nShapeType = SHPT_MULTIPOINTZ;
     731           1 :         eType = wkbMultiPointZM;
     732             :     }
     733           2 :     else if (EQUAL(pszOverride, "MULTIPATCH"))
     734             :     {
     735           2 :         nShapeType = SHPT_MULTIPATCH;
     736           2 :         eType = wkbUnknown;  // not ideal...
     737             :     }
     738           0 :     else if (EQUAL(pszOverride, "NONE") || EQUAL(pszOverride, "NULL"))
     739             :     {
     740           0 :         nShapeType = SHPT_NULL;
     741           0 :         eType = wkbNone;
     742             :     }
     743             :     else
     744             :     {
     745           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     746             :                  "Unknown SHPT value of `%s' passed to Shapefile layer"
     747             :                  "creation.  Creation aborted.",
     748             :                  pszOverride);
     749             : 
     750           0 :         return nullptr;
     751             :     }
     752             : 
     753        1667 :     if (nShapeType == -1)
     754             :     {
     755           4 :         CPLError(CE_Failure, CPLE_NotSupported,
     756             :                  "Geometry type of `%s' not supported in shapefiles.  "
     757             :                  "Type can be overridden with a layer creation option "
     758             :                  "of SHPT=POINT/ARC/POLYGON/MULTIPOINT/POINTZ/ARCZ/POLYGONZ/"
     759             :                  "MULTIPOINTZ/MULTIPATCH.",
     760             :                  OGRGeometryTypeToName(eType));
     761           4 :         return nullptr;
     762             :     }
     763             : 
     764             :     /* -------------------------------------------------------------------- */
     765             :     /*      What filename do we use, excluding the extension?               */
     766             :     /* -------------------------------------------------------------------- */
     767        3326 :     std::string osFilenameWithoutExt;
     768             : 
     769        1663 :     if (m_bSingleFileDataSource && m_apoLayers.empty())
     770             :     {
     771         962 :         const std::string osPath = CPLGetPathSafe(GetDescription());
     772         481 :         const std::string osFBasename = CPLGetBasenameSafe(GetDescription());
     773             : 
     774             :         osFilenameWithoutExt =
     775         481 :             CPLFormFilenameSafe(osPath.c_str(), osFBasename.c_str(), nullptr);
     776             :     }
     777        1182 :     else if (m_bSingleFileDataSource)
     778             :     {
     779             :         // This is a very weird use case : the user creates/open a datasource
     780             :         // made of a single shapefile 'foo.shp' and wants to add a new layer
     781             :         // to it, 'bar'. So we create a new shapefile 'bar.shp' in the same
     782             :         // directory as 'foo.shp'
     783             :         // So technically, we will not be any longer a single file
     784             :         // datasource ... Ahem ahem.
     785          16 :         const std::string osPath = CPLGetPathSafe(GetDescription());
     786          32 :         osFilenameWithoutExt = CPLFormFilenameSafe(
     787          48 :             osPath.c_str(), LaunderLayerName(pszLayerName).c_str(), nullptr);
     788             :     }
     789             :     else
     790             :     {
     791        1166 :         const std::string osDir(m_osTemporaryUnzipDir.empty()
     792        1163 :                                     ? std::string(GetDescription())
     793        1166 :                                     : m_osTemporaryUnzipDir);
     794        2332 :         osFilenameWithoutExt = CPLFormFilenameSafe(
     795        3498 :             osDir.c_str(), LaunderLayerName(pszLayerName).c_str(), nullptr);
     796             :     }
     797             : 
     798             :     /* -------------------------------------------------------------------- */
     799             :     /*      Create the shapefile.                                           */
     800             :     /* -------------------------------------------------------------------- */
     801             :     const bool l_b2GBLimit =
     802        1663 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "2GB_LIMIT", "FALSE"));
     803             : 
     804        1663 :     SHPHandle hSHP = nullptr;
     805             : 
     806        1663 :     if (nShapeType != SHPT_NULL)
     807             :     {
     808             :         const std::string osFilename =
     809        1613 :             CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
     810             : 
     811        1613 :         hSHP = SHPCreateLL(osFilename.c_str(), nShapeType,
     812             :                            const_cast<SAHooks *>(VSI_SHP_GetHook(l_b2GBLimit)));
     813             : 
     814        1613 :         if (hSHP == nullptr)
     815             :         {
     816           4 :             return nullptr;
     817             :         }
     818             : 
     819        1609 :         SHPSetFastModeReadObject(hSHP, TRUE);
     820             :     }
     821             : 
     822             :     /* -------------------------------------------------------------------- */
     823             :     /*      Has a specific LDID been specified by the caller?               */
     824             :     /* -------------------------------------------------------------------- */
     825        1659 :     const char *pszLDID = CSLFetchNameValue(papszOptions, "ENCODING");
     826             : 
     827             :     /* -------------------------------------------------------------------- */
     828             :     /*      Create a DBF file.                                              */
     829             :     /* -------------------------------------------------------------------- */
     830             :     const std::string osDBFFilename =
     831        3318 :         CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "dbf");
     832             : 
     833        1659 :     DBFHandle hDBF = DBFCreateLL(
     834             :         osDBFFilename.c_str(), (pszLDID != nullptr) ? pszLDID : "LDID/87",
     835        1659 :         const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
     836             : 
     837        1659 :     if (hDBF == nullptr)
     838             :     {
     839           1 :         CPLError(CE_Failure, CPLE_OpenFailed,
     840             :                  "Failed to create Shape DBF file `%s'.",
     841             :                  osDBFFilename.c_str());
     842           1 :         SHPClose(hSHP);
     843           1 :         return nullptr;
     844             :     }
     845             : 
     846             :     /* -------------------------------------------------------------------- */
     847             :     /*      Create the .prj file, if required.                              */
     848             :     /* -------------------------------------------------------------------- */
     849        3316 :     std::string osPrjFilename;
     850        1658 :     OGRSpatialReference *poSRSClone = nullptr;
     851        1658 :     if (poSRS != nullptr)
     852             :     {
     853             :         osPrjFilename =
     854         213 :             CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "prj");
     855         213 :         poSRSClone = poSRS->Clone();
     856             : 
     857         213 :         char *pszWKT = nullptr;
     858         213 :         VSILFILE *fp = nullptr;
     859         213 :         const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
     860         422 :         if (poSRSClone->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE &&
     861         209 :             (fp = VSIFOpenL(osPrjFilename.c_str(), "wt")) != nullptr)
     862             :         {
     863         209 :             VSIFWriteL(pszWKT, strlen(pszWKT), 1, fp);
     864         209 :             VSIFCloseL(fp);
     865             :         }
     866             : 
     867         213 :         CPLFree(pszWKT);
     868             :     }
     869             : 
     870             :     /* -------------------------------------------------------------------- */
     871             :     /*      Create the layer object.                                        */
     872             :     /* -------------------------------------------------------------------- */
     873             :     // OGRShapeLayer constructor expects a filename with an extension (that
     874             :     // could be random actually), otherwise this is going to cause problems with
     875             :     // layer names that have a dot (not speaking about the one before the shp)
     876             :     const std::string osSHPFilename =
     877        3316 :         CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
     878             : 
     879             :     auto poLayer = std::make_unique<OGRShapeLayer>(
     880           0 :         this, osSHPFilename.c_str(), hSHP, hDBF, poSRSClone,
     881           0 :         /* bSRSSet = */ true, osPrjFilename,
     882        3316 :         /* bUpdate = */ true, eType);
     883        1658 :     if (poSRSClone != nullptr)
     884             :     {
     885         213 :         poSRSClone->Release();
     886             :     }
     887             : 
     888        1658 :     poLayer->SetResizeAtClose(CPLFetchBool(papszOptions, "RESIZE", false));
     889        1658 :     poLayer->CreateSpatialIndexAtClose(
     890        1658 :         CPLFetchBool(papszOptions, "SPATIAL_INDEX", false));
     891        1658 :     poLayer->SetModificationDate(
     892             :         CSLFetchNameValue(papszOptions, "DBF_DATE_LAST_UPDATE"));
     893        1658 :     poLayer->SetAutoRepack(CPLFetchBool(papszOptions, "AUTO_REPACK", true));
     894        1658 :     poLayer->SetWriteDBFEOFChar(
     895        1658 :         CPLFetchBool(papszOptions, "DBF_EOF_CHAR", true));
     896             : 
     897             :     /* -------------------------------------------------------------------- */
     898             :     /*      Add layer to data source layer list.                            */
     899             :     /* -------------------------------------------------------------------- */
     900        1658 :     AddLayer(std::move(poLayer));
     901             : 
     902        1658 :     return m_apoLayers.back().get();
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                           TestCapability()                           */
     907             : /************************************************************************/
     908             : 
     909         734 : int OGRShapeDataSource::TestCapability(const char *pszCap)
     910             : 
     911             : {
     912         734 :     if (EQUAL(pszCap, ODsCCreateLayer))
     913         356 :         return eAccess == GA_Update &&
     914         356 :                !(m_bIsZip && m_bSingleLayerZip && m_apoLayers.size() == 1);
     915         556 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     916          18 :         return eAccess == GA_Update && !(m_bIsZip && m_bSingleLayerZip);
     917         538 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     918          14 :         return true;
     919         524 :     else if (EQUAL(pszCap, ODsCZGeometries))
     920          14 :         return true;
     921         510 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     922           6 :         return eAccess == GA_Update;
     923             : 
     924         504 :     return false;
     925             : }
     926             : 
     927             : /************************************************************************/
     928             : /*                            GetLayerCount()                           */
     929             : /************************************************************************/
     930             : 
     931     1094970 : int OGRShapeDataSource::GetLayerCount()
     932             : 
     933             : {
     934             : #ifndef IMMEDIATE_OPENING
     935     1094970 :     if (!m_oVectorLayerName.empty())
     936             :     {
     937        1601 :         for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
     938             :         {
     939        1495 :             const char *pszFilename = m_oVectorLayerName[i].c_str();
     940        1495 :             const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
     941             : 
     942        1495 :             bool bFound = false;
     943      253614 :             for (auto &poLayer : m_apoLayers)
     944             :             {
     945      252627 :                 if (poLayer->GetName() == osLayerName)
     946             :                 {
     947         508 :                     bFound = true;
     948         508 :                     break;
     949             :                 }
     950             :             }
     951        1495 :             if (bFound)
     952         508 :                 continue;
     953             : 
     954         987 :             if (!OpenFile(pszFilename, eAccess == GA_Update))
     955             :             {
     956           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
     957             :                          "Failed to open file %s."
     958             :                          "It may be corrupt or read-only file accessed in "
     959             :                          "update mode.",
     960             :                          pszFilename);
     961             :             }
     962             :         }
     963         106 :         m_oVectorLayerName.resize(0);
     964             :     }
     965             : #endif
     966             : 
     967     1094970 :     return static_cast<int>(m_apoLayers.size());
     968             : }
     969             : 
     970             : /************************************************************************/
     971             : /*                              GetLayer()                              */
     972             : /************************************************************************/
     973             : 
     974      544790 : OGRLayer *OGRShapeDataSource::GetLayer(int iLayer)
     975             : 
     976             : {
     977             :     // To ensure that existing layers are created.
     978      544790 :     GetLayerCount();
     979             : 
     980      544790 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
     981          14 :         return nullptr;
     982             : 
     983      544776 :     return m_apoLayers[iLayer].get();
     984             : }
     985             : 
     986             : /************************************************************************/
     987             : /*                           GetLayerByName()                           */
     988             : /************************************************************************/
     989             : 
     990        5142 : OGRLayer *OGRShapeDataSource::GetLayerByName(const char *pszLayerNameIn)
     991             : {
     992             : #ifndef IMMEDIATE_OPENING
     993        5142 :     if (!m_oVectorLayerName.empty())
     994             :     {
     995      257268 :         for (auto &poLayer : m_apoLayers)
     996             :         {
     997      255928 :             if (strcmp(poLayer->GetName(), pszLayerNameIn) == 0)
     998             :             {
     999        1081 :                 return poLayer.get();
    1000             :             }
    1001             :         }
    1002             : 
    1003        1440 :         for (int j = 0; j < 2; j++)
    1004             :         {
    1005      252166 :             for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
    1006             :             {
    1007      252066 :                 const char *pszFilename = m_oVectorLayerName[i].c_str();
    1008      252066 :                 const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
    1009             : 
    1010      252066 :                 if (j == 0)
    1011             :                 {
    1012      251956 :                     if (osLayerName != pszLayerNameIn)
    1013      250711 :                         continue;
    1014             :                 }
    1015             :                 else
    1016             :                 {
    1017         110 :                     if (!EQUAL(osLayerName.c_str(), pszLayerNameIn))
    1018          20 :                         continue;
    1019             :                 }
    1020             : 
    1021        1335 :                 if (!OpenFile(pszFilename, eAccess == GA_Update))
    1022             :                 {
    1023           1 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1024             :                              "Failed to open file %s.  "
    1025             :                              "It may be corrupt or read-only file accessed in "
    1026             :                              "update mode.",
    1027             :                              pszFilename);
    1028           1 :                     return nullptr;
    1029             :                 }
    1030             : 
    1031        1334 :                 return m_apoLayers.back().get();
    1032             :             }
    1033             :         }
    1034             : 
    1035           5 :         return nullptr;
    1036             :     }
    1037             : #endif
    1038             : 
    1039        2721 :     return GDALDataset::GetLayerByName(pszLayerNameIn);
    1040             : }
    1041             : 
    1042             : /************************************************************************/
    1043             : /*                             ExecuteSQL()                             */
    1044             : /*                                                                      */
    1045             : /*      We override this to provide special handling of CREATE          */
    1046             : /*      SPATIAL INDEX commands.  Support forms are:                     */
    1047             : /*                                                                      */
    1048             : /*        CREATE SPATIAL INDEX ON layer_name [DEPTH n]                  */
    1049             : /*        DROP SPATIAL INDEX ON layer_name                              */
    1050             : /*        REPACK layer_name                                             */
    1051             : /*        RECOMPUTE EXTENT ON layer_name                                */
    1052             : /************************************************************************/
    1053             : 
    1054        1001 : OGRLayer *OGRShapeDataSource::ExecuteSQL(const char *pszStatement,
    1055             :                                          OGRGeometry *poSpatialFilter,
    1056             :                                          const char *pszDialect)
    1057             : 
    1058             : {
    1059        1001 :     if (EQUAL(pszStatement, "UNCOMPRESS"))
    1060             :     {
    1061           0 :         CPL_IGNORE_RET_VAL(UncompressIfNeeded());
    1062           0 :         return nullptr;
    1063             :     }
    1064             : 
    1065        1001 :     if (EQUAL(pszStatement, "RECOMPRESS"))
    1066             :     {
    1067           0 :         RecompressIfNeeded(GetLayerNames());
    1068           0 :         return nullptr;
    1069             :     }
    1070             :     /* ==================================================================== */
    1071             :     /*      Handle command to drop a spatial index.                         */
    1072             :     /* ==================================================================== */
    1073        1001 :     if (STARTS_WITH_CI(pszStatement, "REPACK "))
    1074             :     {
    1075             :         OGRShapeLayer *poLayer =
    1076          17 :             cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
    1077             : 
    1078          17 :         if (poLayer != nullptr)
    1079             :         {
    1080          17 :             if (poLayer->Repack() != OGRERR_NONE)
    1081             :             {
    1082           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1083             :                          "REPACK of layer '%s' failed.", pszStatement + 7);
    1084             :             }
    1085             :         }
    1086             :         else
    1087             :         {
    1088           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1089             :                      "No such layer as '%s' in REPACK.", pszStatement + 7);
    1090             :         }
    1091          17 :         return nullptr;
    1092             :     }
    1093             : 
    1094             :     /* ==================================================================== */
    1095             :     /*      Handle command to shrink columns to their minimum size.         */
    1096             :     /* ==================================================================== */
    1097         984 :     if (STARTS_WITH_CI(pszStatement, "RESIZE "))
    1098             :     {
    1099             :         OGRShapeLayer *poLayer =
    1100           1 :             cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
    1101             : 
    1102           1 :         if (poLayer != nullptr)
    1103             :         {
    1104           1 :             poLayer->ResizeDBF();
    1105             :         }
    1106             :         else
    1107             :         {
    1108           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1109             :                      "No such layer as '%s' in RESIZE.", pszStatement + 7);
    1110             :         }
    1111           1 :         return nullptr;
    1112             :     }
    1113             : 
    1114             :     /* ==================================================================== */
    1115             :     /*      Handle command to recompute extent                             */
    1116             :     /* ==================================================================== */
    1117         983 :     if (STARTS_WITH_CI(pszStatement, "RECOMPUTE EXTENT ON "))
    1118             :     {
    1119             :         OGRShapeLayer *poLayer =
    1120           6 :             cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 20));
    1121             : 
    1122           6 :         if (poLayer != nullptr)
    1123             :         {
    1124           5 :             poLayer->RecomputeExtent();
    1125             :         }
    1126             :         else
    1127             :         {
    1128           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1129             :                      "No such layer as '%s' in RECOMPUTE EXTENT.",
    1130             :                      pszStatement + 20);
    1131             :         }
    1132           6 :         return nullptr;
    1133             :     }
    1134             : 
    1135             :     /* ==================================================================== */
    1136             :     /*      Handle command to drop a spatial index.                         */
    1137             :     /* ==================================================================== */
    1138         977 :     if (STARTS_WITH_CI(pszStatement, "DROP SPATIAL INDEX ON "))
    1139             :     {
    1140             :         OGRShapeLayer *poLayer =
    1141           3 :             cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 22));
    1142             : 
    1143           3 :         if (poLayer != nullptr)
    1144             :         {
    1145           3 :             poLayer->DropSpatialIndex();
    1146             :         }
    1147             :         else
    1148             :         {
    1149           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1150             :                      "No such layer as '%s' in DROP SPATIAL INDEX.",
    1151             :                      pszStatement + 22);
    1152             :         }
    1153           3 :         return nullptr;
    1154             :     }
    1155             : 
    1156             :     /* ==================================================================== */
    1157             :     /*      Handle all commands except spatial index creation generically.  */
    1158             :     /* ==================================================================== */
    1159         974 :     if (!STARTS_WITH_CI(pszStatement, "CREATE SPATIAL INDEX ON "))
    1160             :     {
    1161         958 :         char **papszTokens = CSLTokenizeString(pszStatement);
    1162         958 :         if (CSLCount(papszTokens) >= 4 &&
    1163         454 :             (EQUAL(papszTokens[0], "CREATE") ||
    1164         426 :              EQUAL(papszTokens[0], "DROP")) &&
    1165        1412 :             EQUAL(papszTokens[1], "INDEX") && EQUAL(papszTokens[2], "ON"))
    1166             :         {
    1167             :             OGRShapeLayer *poLayer =
    1168          38 :                 cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[3]));
    1169          38 :             if (poLayer != nullptr)
    1170          38 :                 poLayer->InitializeIndexSupport(poLayer->GetFullName());
    1171             :         }
    1172         958 :         CSLDestroy(papszTokens);
    1173             : 
    1174         958 :         return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter,
    1175         958 :                                        pszDialect);
    1176             :     }
    1177             : 
    1178             :     /* -------------------------------------------------------------------- */
    1179             :     /*      Parse into keywords.                                            */
    1180             :     /* -------------------------------------------------------------------- */
    1181          16 :     char **papszTokens = CSLTokenizeString(pszStatement);
    1182             : 
    1183          32 :     if (CSLCount(papszTokens) < 5 || !EQUAL(papszTokens[0], "CREATE") ||
    1184          16 :         !EQUAL(papszTokens[1], "SPATIAL") || !EQUAL(papszTokens[2], "INDEX") ||
    1185          48 :         !EQUAL(papszTokens[3], "ON") || CSLCount(papszTokens) > 7 ||
    1186          16 :         (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5], "DEPTH")))
    1187             :     {
    1188           0 :         CSLDestroy(papszTokens);
    1189           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1190             :                  "Syntax error in CREATE SPATIAL INDEX command.\n"
    1191             :                  "Was '%s'\n"
    1192             :                  "Should be of form 'CREATE SPATIAL INDEX ON <table> "
    1193             :                  "[DEPTH <n>]'",
    1194             :                  pszStatement);
    1195           0 :         return nullptr;
    1196             :     }
    1197             : 
    1198             :     /* -------------------------------------------------------------------- */
    1199             :     /*      Get depth if provided.                                          */
    1200             :     /* -------------------------------------------------------------------- */
    1201          16 :     const int nDepth = CSLCount(papszTokens) == 7 ? atoi(papszTokens[6]) : 0;
    1202             : 
    1203             :     /* -------------------------------------------------------------------- */
    1204             :     /*      What layer are we operating on.                                 */
    1205             :     /* -------------------------------------------------------------------- */
    1206             :     OGRShapeLayer *poLayer =
    1207          16 :         cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[4]));
    1208             : 
    1209          16 :     if (poLayer == nullptr)
    1210             :     {
    1211           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer %s not recognised.",
    1212           0 :                  papszTokens[4]);
    1213           0 :         CSLDestroy(papszTokens);
    1214           0 :         return nullptr;
    1215             :     }
    1216             : 
    1217          16 :     CSLDestroy(papszTokens);
    1218             : 
    1219          16 :     poLayer->CreateSpatialIndex(nDepth);
    1220          16 :     return nullptr;
    1221             : }
    1222             : 
    1223             : /************************************************************************/
    1224             : /*                     GetExtensionsForDeletion()                       */
    1225             : /************************************************************************/
    1226             : 
    1227         694 : const char *const *OGRShapeDataSource::GetExtensionsForDeletion()
    1228             : {
    1229             :     static const char *const apszExtensions[] = {
    1230             :         "shp",  "shx", "dbf", "sbn", "sbx", "prj", "idm", "ind", "qix", "cpg",
    1231             :         "qpj",  // QGIS projection file
    1232             :         nullptr};
    1233         694 :     return apszExtensions;
    1234             : }
    1235             : 
    1236             : /************************************************************************/
    1237             : /*                            DeleteLayer()                             */
    1238             : /************************************************************************/
    1239             : 
    1240         548 : OGRErr OGRShapeDataSource::DeleteLayer(int iLayer)
    1241             : 
    1242             : {
    1243             :     /* -------------------------------------------------------------------- */
    1244             :     /*      Verify we are in update mode.                                   */
    1245             :     /* -------------------------------------------------------------------- */
    1246         548 :     if (eAccess != GA_Update)
    1247             :     {
    1248           1 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
    1249             :                  "Data source %s opened read-only.  "
    1250             :                  "Layer %d cannot be deleted.",
    1251           1 :                  GetDescription(), iLayer);
    1252             : 
    1253           1 :         return OGRERR_FAILURE;
    1254             :     }
    1255             : 
    1256             :     // To ensure that existing layers are created.
    1257         547 :     GetLayerCount();
    1258             : 
    1259         547 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
    1260             :     {
    1261           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1262             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
    1263           2 :                  static_cast<int>(m_apoLayers.size()) - 1);
    1264           2 :         return OGRERR_FAILURE;
    1265             :     }
    1266             : 
    1267         545 :     if (m_bIsZip && m_bSingleLayerZip)
    1268             :     {
    1269           3 :         CPLError(CE_Failure, CPLE_NotSupported,
    1270             :                  ".shz does not support layer deletion");
    1271           3 :         return OGRERR_FAILURE;
    1272             :     }
    1273             : 
    1274         542 :     if (!UncompressIfNeeded())
    1275           0 :         return OGRERR_FAILURE;
    1276             : 
    1277         542 :     const std::string osLayerFilename = m_apoLayers[iLayer]->GetFullName();
    1278             : 
    1279         542 :     m_apoLayers.erase(m_apoLayers.begin() + iLayer);
    1280             : 
    1281             :     const char *const *papszExtensions =
    1282         542 :         OGRShapeDataSource::GetExtensionsForDeletion();
    1283        6504 :     for (int iExt = 0; papszExtensions[iExt] != nullptr; iExt++)
    1284             :     {
    1285             :         const std::string osFile = CPLResetExtensionSafe(
    1286       11924 :             osLayerFilename.c_str(), papszExtensions[iExt]);
    1287             :         VSIStatBufL sStatBuf;
    1288        5962 :         if (VSIStatL(osFile.c_str(), &sStatBuf) == 0)
    1289        1621 :             VSIUnlink(osFile.c_str());
    1290             :     }
    1291             : 
    1292         542 :     return OGRERR_NONE;
    1293             : }
    1294             : 
    1295             : /************************************************************************/
    1296             : /*                          SetLastUsedLayer()                          */
    1297             : /************************************************************************/
    1298             : 
    1299      225189 : void OGRShapeDataSource::SetLastUsedLayer(OGRShapeLayer *poLayer)
    1300             : {
    1301             :     // We could remove that check and things would still work in
    1302             :     // 99.99% cases.
    1303             :     // The only rationale for that test is to avoid breaking applications that
    1304             :     // would deal with layers of the same datasource in different threads. In
    1305             :     // GDAL < 1.9.0, this would work in most cases I can imagine as shapefile
    1306             :     // layers are pretty much independent from each others (although it has
    1307             :     // never been guaranteed to be a valid use case, and the shape driver is
    1308             :     // likely more the exception than the rule in permitting accessing layers
    1309             :     // from different threads !)  Anyway the LRU list mechanism leaves the door
    1310             :     // open to concurrent accesses to it so when the datasource has not many
    1311             :     // layers, we don't try to build the LRU list to avoid concurrency issues. I
    1312             :     // haven't bothered making the analysis of how a mutex could be used to
    1313             :     // protect that (my intuition is that it would need to be placed at the
    1314             :     // beginning of OGRShapeLayer::TouchLayer() ).
    1315      450378 :     if (static_cast<int>(m_apoLayers.size()) <
    1316      225189 :         m_poPool->GetMaxSimultaneouslyOpened())
    1317      216347 :         return;
    1318             : 
    1319        8842 :     m_poPool->SetLastUsedLayer(poLayer);
    1320             : }
    1321             : 
    1322             : /************************************************************************/
    1323             : //                            GetFileList()                             */
    1324             : /************************************************************************/
    1325             : 
    1326          29 : char **OGRShapeDataSource::GetFileList()
    1327             : {
    1328          29 :     if (m_bIsZip)
    1329             :     {
    1330           1 :         return CSLAddString(nullptr, GetDescription());
    1331             :     }
    1332          56 :     CPLStringList oFileList;
    1333          28 :     GetLayerCount();
    1334         163 :     for (auto &poLayer : m_apoLayers)
    1335             :     {
    1336         135 :         poLayer->AddToFileList(oFileList);
    1337             :     }
    1338          28 :     return oFileList.StealList();
    1339             : }
    1340             : 
    1341             : /************************************************************************/
    1342             : //                          RefreshLockFile()                            */
    1343             : /************************************************************************/
    1344             : 
    1345           0 : void OGRShapeDataSource::RefreshLockFile(void *_self)
    1346             : {
    1347           0 :     OGRShapeDataSource *self = static_cast<OGRShapeDataSource *>(_self);
    1348           0 :     CPLAssert(self->m_psLockFile);
    1349           0 :     CPLAcquireMutex(self->m_poRefreshLockFileMutex, 1000);
    1350           0 :     self->m_bRefreshLockFileThreadStarted = true;
    1351           0 :     CPLCondSignal(self->m_poRefreshLockFileCond);
    1352           0 :     unsigned int nInc = 0;
    1353           0 :     while (!(self->m_bExitRefreshLockFileThread))
    1354             :     {
    1355           0 :         auto ret = CPLCondTimedWait(self->m_poRefreshLockFileCond,
    1356             :                                     self->m_poRefreshLockFileMutex,
    1357             :                                     self->m_dfRefreshLockDelay);
    1358           0 :         if (ret == COND_TIMED_WAIT_TIME_OUT)
    1359             :         {
    1360           0 :             CPLAssert(self->m_psLockFile);
    1361           0 :             VSIFSeekL(self->m_psLockFile, 0, SEEK_SET);
    1362           0 :             CPLString osTime;
    1363           0 :             nInc++;
    1364             :             osTime.Printf(CPL_FRMT_GUIB ", %u\n",
    1365           0 :                           static_cast<GUIntBig>(time(nullptr)), nInc);
    1366           0 :             VSIFWriteL(osTime.data(), 1, osTime.size(), self->m_psLockFile);
    1367           0 :             VSIFFlushL(self->m_psLockFile);
    1368             :         }
    1369             :     }
    1370           0 :     CPLReleaseMutex(self->m_poRefreshLockFileMutex);
    1371           0 : }
    1372             : 
    1373             : /************************************************************************/
    1374             : //                            RemoveLockFile()                          */
    1375             : /************************************************************************/
    1376             : 
    1377        3188 : void OGRShapeDataSource::RemoveLockFile()
    1378             : {
    1379        3188 :     if (!m_psLockFile)
    1380        3188 :         return;
    1381             : 
    1382             :     // Ask the thread to terminate
    1383           0 :     CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
    1384           0 :     m_bExitRefreshLockFileThread = true;
    1385           0 :     CPLCondSignal(m_poRefreshLockFileCond);
    1386           0 :     CPLReleaseMutex(m_poRefreshLockFileMutex);
    1387           0 :     CPLJoinThread(m_hRefreshLockFileThread);
    1388           0 :     m_hRefreshLockFileThread = nullptr;
    1389             : 
    1390             :     // Close and remove lock file
    1391           0 :     VSIFCloseL(m_psLockFile);
    1392           0 :     m_psLockFile = nullptr;
    1393           0 :     CPLString osLockFile(GetDescription());
    1394           0 :     osLockFile += ".gdal.lock";
    1395           0 :     VSIUnlink(osLockFile);
    1396             : }
    1397             : 
    1398             : /************************************************************************/
    1399             : //                         UncompressIfNeeded()                         */
    1400             : /************************************************************************/
    1401             : 
    1402       72390 : bool OGRShapeDataSource::UncompressIfNeeded()
    1403             : {
    1404       72390 :     if (eAccess != GA_Update || !m_bIsZip || !m_osTemporaryUnzipDir.empty())
    1405       72384 :         return true;
    1406             : 
    1407           6 :     GetLayerCount();
    1408             : 
    1409           0 :     auto returnError = [this]()
    1410             :     {
    1411           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot uncompress %s",
    1412           0 :                  GetDescription());
    1413           0 :         return false;
    1414           6 :     };
    1415             : 
    1416           6 :     if (m_apoLayers.size() > 1)
    1417             :     {
    1418           0 :         CPLString osLockFile(GetDescription());
    1419           0 :         osLockFile += ".gdal.lock";
    1420             :         VSIStatBufL sStat;
    1421           0 :         if (VSIStatL(osLockFile, &sStat) == 0 &&
    1422           0 :             sStat.st_mtime > time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC)
    1423             :         {
    1424           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1425             :                      "Cannot edit %s. Another task is editing it",
    1426           0 :                      GetDescription());
    1427           0 :             return false;
    1428             :         }
    1429           0 :         if (!m_poRefreshLockFileMutex)
    1430             :         {
    1431           0 :             m_poRefreshLockFileMutex = CPLCreateMutex();
    1432           0 :             if (!m_poRefreshLockFileMutex)
    1433           0 :                 return false;
    1434           0 :             CPLReleaseMutex(m_poRefreshLockFileMutex);
    1435             :         }
    1436           0 :         if (!m_poRefreshLockFileCond)
    1437             :         {
    1438           0 :             m_poRefreshLockFileCond = CPLCreateCond();
    1439           0 :             if (!m_poRefreshLockFileCond)
    1440           0 :                 return false;
    1441             :         }
    1442           0 :         auto f = VSIFOpenL(osLockFile, "wb");
    1443           0 :         if (!f)
    1444             :         {
    1445           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot create lock file");
    1446           0 :             return false;
    1447             :         }
    1448           0 :         m_psLockFile = f;
    1449           0 :         CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
    1450           0 :         m_bExitRefreshLockFileThread = false;
    1451           0 :         m_bRefreshLockFileThreadStarted = false;
    1452           0 :         CPLReleaseMutex(m_poRefreshLockFileMutex);
    1453             :         // Config option mostly for testing purposes
    1454             :         // coverity[tainted_data]
    1455           0 :         m_dfRefreshLockDelay = CPLAtof(CPLGetConfigOption(
    1456             :             "OGR_SHAPE_LOCK_DELAY",
    1457             :             CPLSPrintf("%d", knREFRESH_LOCK_FILE_DELAY_SEC)));
    1458           0 :         m_hRefreshLockFileThread =
    1459           0 :             CPLCreateJoinableThread(OGRShapeDataSource::RefreshLockFile, this);
    1460           0 :         if (!m_hRefreshLockFileThread)
    1461             :         {
    1462           0 :             VSIFCloseL(m_psLockFile);
    1463           0 :             m_psLockFile = nullptr;
    1464           0 :             VSIUnlink(osLockFile);
    1465             :         }
    1466             :         else
    1467             :         {
    1468           0 :             CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
    1469           0 :             while (!m_bRefreshLockFileThreadStarted)
    1470             :             {
    1471           0 :                 CPLCondWait(m_poRefreshLockFileCond, m_poRefreshLockFileMutex);
    1472             :             }
    1473           0 :             CPLReleaseMutex(m_poRefreshLockFileMutex);
    1474             :         }
    1475             :     }
    1476             : 
    1477          12 :     CPLString osVSIZipDirname(GetVSIZipPrefixeDir());
    1478           6 :     vsi_l_offset nTotalUncompressedSize = 0;
    1479          12 :     CPLStringList aosFiles(VSIReadDir(osVSIZipDirname));
    1480          20 :     for (int i = 0; i < aosFiles.size(); i++)
    1481             :     {
    1482          14 :         const char *pszFilename = aosFiles[i];
    1483          14 :         if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
    1484             :         {
    1485             :             const CPLString osSrcFile(
    1486          28 :                 CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
    1487             :             VSIStatBufL sStat;
    1488          14 :             if (VSIStatL(osSrcFile, &sStat) == 0)
    1489             :             {
    1490          14 :                 nTotalUncompressedSize += sStat.st_size;
    1491             :             }
    1492             :         }
    1493             :     }
    1494             : 
    1495          12 :     CPLString osTemporaryDir(GetDescription());
    1496           6 :     osTemporaryDir += "_tmp_uncompressed";
    1497             : 
    1498             :     const char *pszUseVsimem =
    1499           6 :         CPLGetConfigOption("OGR_SHAPE_USE_VSIMEM_FOR_TEMP", "AUTO");
    1500          12 :     if (EQUAL(pszUseVsimem, "YES") ||
    1501           6 :         (EQUAL(pszUseVsimem, "AUTO") && nTotalUncompressedSize > 0 &&
    1502             :          nTotalUncompressedSize <
    1503           3 :              static_cast<GUIntBig>(CPLGetUsablePhysicalRAM() / 10)))
    1504             :     {
    1505           3 :         osTemporaryDir = VSIMemGenerateHiddenFilename("shapedriver");
    1506             :     }
    1507           6 :     CPLDebug("Shape", "Uncompressing to %s", osTemporaryDir.c_str());
    1508             : 
    1509           6 :     VSIRmdirRecursive(osTemporaryDir);
    1510           6 :     if (VSIMkdir(osTemporaryDir, 0755) != 0)
    1511           0 :         return returnError();
    1512          20 :     for (int i = 0; i < aosFiles.size(); i++)
    1513             :     {
    1514          14 :         const char *pszFilename = aosFiles[i];
    1515          14 :         if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
    1516             :         {
    1517             :             const CPLString osSrcFile(
    1518          14 :                 CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
    1519             :             const CPLString osDestFile(
    1520          14 :                 CPLFormFilenameSafe(osTemporaryDir, pszFilename, nullptr));
    1521          14 :             if (CPLCopyFile(osDestFile, osSrcFile) != 0)
    1522             :             {
    1523           0 :                 VSIRmdirRecursive(osTemporaryDir);
    1524           0 :                 return returnError();
    1525             :             }
    1526             :         }
    1527             :     }
    1528             : 
    1529           6 :     m_osTemporaryUnzipDir = std::move(osTemporaryDir);
    1530             : 
    1531           9 :     for (auto &poLayer : m_apoLayers)
    1532             :     {
    1533           3 :         poLayer->UpdateFollowingDeOrRecompression();
    1534             :     }
    1535             : 
    1536           6 :     return true;
    1537             : }
    1538             : 
    1539             : /************************************************************************/
    1540             : //                         RecompressIfNeeded()                         */
    1541             : /************************************************************************/
    1542             : 
    1543        3182 : bool OGRShapeDataSource::RecompressIfNeeded(
    1544             :     const std::vector<CPLString> &layerNames)
    1545             : {
    1546        3182 :     if (eAccess != GA_Update || !m_bIsZip || m_osTemporaryUnzipDir.empty())
    1547        3176 :         return true;
    1548             : 
    1549           0 :     auto returnError = [this]()
    1550             :     {
    1551           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot recompress %s",
    1552           0 :                  GetDescription());
    1553           0 :         RemoveLockFile();
    1554           0 :         return false;
    1555           6 :     };
    1556             : 
    1557          12 :     CPLStringList aosFiles(VSIReadDir(m_osTemporaryUnzipDir));
    1558          12 :     CPLString osTmpZip(m_osTemporaryUnzipDir + ".zip");
    1559           6 :     VSIUnlink(osTmpZip);
    1560          18 :     CPLString osTmpZipWithVSIZip("/vsizip/{" + osTmpZip + '}');
    1561             : 
    1562          12 :     std::map<CPLString, int> oMapLayerOrder;
    1563          12 :     for (size_t i = 0; i < layerNames.size(); i++)
    1564           6 :         oMapLayerOrder[layerNames[i]] = static_cast<int>(i);
    1565             : 
    1566          12 :     std::vector<CPLString> sortedFiles;
    1567           6 :     vsi_l_offset nTotalUncompressedSize = 0;
    1568          36 :     for (int i = 0; i < aosFiles.size(); i++)
    1569             :     {
    1570          30 :         sortedFiles.emplace_back(aosFiles[i]);
    1571             :         const CPLString osSrcFile(
    1572          60 :             CPLFormFilenameSafe(m_osTemporaryUnzipDir, aosFiles[i], nullptr));
    1573             :         VSIStatBufL sStat;
    1574          30 :         if (VSIStatL(osSrcFile, &sStat) == 0)
    1575             :         {
    1576          30 :             nTotalUncompressedSize += sStat.st_size;
    1577             :         }
    1578             :     }
    1579             : 
    1580             :     // Sort files by their layer orders, and then for files of the same layer,
    1581             :     // make shp appear first, and then by filename order
    1582           6 :     std::sort(sortedFiles.begin(), sortedFiles.end(),
    1583         220 :               [&oMapLayerOrder](const CPLString &a, const CPLString &b)
    1584             :               {
    1585          55 :                   int iA = INT_MAX;
    1586             :                   auto oIterA =
    1587          55 :                       oMapLayerOrder.find(CPLGetBasenameSafe(a).c_str());
    1588          55 :                   if (oIterA != oMapLayerOrder.end())
    1589          47 :                       iA = oIterA->second;
    1590          55 :                   int iB = INT_MAX;
    1591             :                   auto oIterB =
    1592          55 :                       oMapLayerOrder.find(CPLGetBasenameSafe(b).c_str());
    1593          55 :                   if (oIterB != oMapLayerOrder.end())
    1594          40 :                       iB = oIterB->second;
    1595          55 :                   if (iA < iB)
    1596          13 :                       return true;
    1597          42 :                   if (iA > iB)
    1598           6 :                       return false;
    1599          36 :                   if (iA != INT_MAX)
    1600             :                   {
    1601          34 :                       if (EQUAL(CPLGetExtensionSafe(a).c_str(), "shp"))
    1602           4 :                           return true;
    1603          30 :                       if (EQUAL(CPLGetExtensionSafe(b).c_str(), "shp"))
    1604          13 :                           return false;
    1605             :                   }
    1606          19 :                   return a < b;
    1607             :               });
    1608             : 
    1609             :     CPLConfigOptionSetter oZIP64Setter(
    1610             :         "CPL_CREATE_ZIP64",
    1611          12 :         nTotalUncompressedSize < 4000U * 1000 * 1000 ? "NO" : "YES", true);
    1612             : 
    1613             :     /* Maintain a handle on the ZIP opened */
    1614           6 :     VSILFILE *fpZIP = VSIFOpenExL(osTmpZipWithVSIZip, "wb", true);
    1615           6 :     if (fpZIP == nullptr)
    1616             :     {
    1617           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
    1618             :                  osTmpZipWithVSIZip.c_str(), VSIGetLastErrorMsg());
    1619           0 :         return returnError();
    1620             :     }
    1621             : 
    1622          36 :     for (const auto &osFilename : sortedFiles)
    1623             :     {
    1624          30 :         const char *pszFilename = osFilename.c_str();
    1625          30 :         if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
    1626             :         {
    1627          26 :             const CPLString osSrcFile(CPLFormFilenameSafe(
    1628          26 :                 m_osTemporaryUnzipDir, pszFilename, nullptr));
    1629             :             const CPLString osDestFile(
    1630          26 :                 CPLFormFilenameSafe(osTmpZipWithVSIZip, pszFilename, nullptr));
    1631          26 :             if (CPLCopyFile(osDestFile, osSrcFile) != 0)
    1632             :             {
    1633           0 :                 VSIFCloseL(fpZIP);
    1634           0 :                 return returnError();
    1635             :             }
    1636             :         }
    1637             :     }
    1638             : 
    1639           6 :     VSIFCloseL(fpZIP);
    1640             : 
    1641             :     const bool bOverwrite =
    1642           6 :         CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
    1643             : #ifdef _WIN32
    1644             :                                        "YES"
    1645             : #else
    1646             :                                        "NO"
    1647             : #endif
    1648             :                                        ));
    1649           6 :     if (bOverwrite)
    1650             :     {
    1651           0 :         VSILFILE *fpTarget = nullptr;
    1652           0 :         for (int i = 0; i < 10; i++)
    1653             :         {
    1654           0 :             fpTarget = VSIFOpenL(GetDescription(), "rb+");
    1655           0 :             if (fpTarget)
    1656           0 :                 break;
    1657           0 :             CPLSleep(0.1);
    1658             :         }
    1659           0 :         if (!fpTarget)
    1660           0 :             return returnError();
    1661           0 :         bool bCopyOK = CopyInPlace(fpTarget, osTmpZip);
    1662           0 :         VSIFCloseL(fpTarget);
    1663           0 :         VSIUnlink(osTmpZip);
    1664           0 :         if (!bCopyOK)
    1665             :         {
    1666           0 :             return returnError();
    1667             :         }
    1668             :     }
    1669             :     else
    1670             :     {
    1671          12 :         if (VSIUnlink(GetDescription()) != 0 ||
    1672           6 :             CPLMoveFile(GetDescription(), osTmpZip) != 0)
    1673             :         {
    1674           0 :             return returnError();
    1675             :         }
    1676             :     }
    1677             : 
    1678           6 :     VSIRmdirRecursive(m_osTemporaryUnzipDir);
    1679           6 :     m_osTemporaryUnzipDir.clear();
    1680             : 
    1681           6 :     for (auto &poLayer : m_apoLayers)
    1682             :     {
    1683           0 :         poLayer->UpdateFollowingDeOrRecompression();
    1684             :     }
    1685             : 
    1686           6 :     RemoveLockFile();
    1687             : 
    1688           6 :     return true;
    1689             : }
    1690             : 
    1691             : /************************************************************************/
    1692             : /*                            CopyInPlace()                             */
    1693             : /************************************************************************/
    1694             : 
    1695           3 : bool OGRShapeDataSource::CopyInPlace(VSILFILE *fpTarget,
    1696             :                                      const CPLString &osSourceFilename)
    1697             : {
    1698           3 :     return CPL_TO_BOOL(VSIOverwriteFile(fpTarget, osSourceFilename.c_str()));
    1699             : }

Generated by: LCOV version 1.14