LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapedatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 608 745 81.6 %
Date: 2024-05-04 12:52:34 Functions: 27 30 90.0 %

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

Generated by: LCOV version 1.14