LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapedatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 607 753 80.6 %
Date: 2024-11-21 22:18:42 Functions: 27 30 90.0 %

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

Generated by: LCOV version 1.14