LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapedatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 605 755 80.1 %
Date: 2026-09-02 18:57:48 Functions: 28 31 90.3 %

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

Generated by: LCOV version 1.14