LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1488 1920 77.5 %
Date: 2025-02-18 14:19:29 Functions: 54 54 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements OGRShapeLayer class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999,  Les Technologies SoftMap Inc.
       9             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "ogrshape.h"
      15             : 
      16             : #include <cerrno>
      17             : #include <limits>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <ctime>
      24             : #include <algorithm>
      25             : #include <string>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_multiproc.h"
      30             : #include "cpl_port.h"
      31             : #include "cpl_string.h"
      32             : #include "cpl_time.h"
      33             : #include "cpl_vsi.h"
      34             : #include "ogr_core.h"
      35             : #include "ogr_feature.h"
      36             : #include "ogr_geometry.h"
      37             : #include "ogr_p.h"
      38             : #include "ogr_spatialref.h"
      39             : #include "ogr_srs_api.h"
      40             : #include "ogrlayerpool.h"
      41             : #include "ograrrowarrayhelper.h"
      42             : #include "ogrsf_frmts.h"
      43             : #include "shapefil.h"
      44             : #include "shp_vsi.h"
      45             : 
      46             : /************************************************************************/
      47             : /*                           OGRShapeLayer()                            */
      48             : /************************************************************************/
      49             : 
      50        5182 : OGRShapeLayer::OGRShapeLayer(OGRShapeDataSource *poDSIn,
      51             :                              const char *pszFullNameIn, SHPHandle hSHPIn,
      52             :                              DBFHandle hDBFIn,
      53             :                              const OGRSpatialReference *poSRSIn, bool bSRSSetIn,
      54             :                              const std::string &osPrjFilename, bool bUpdate,
      55             :                              OGRwkbGeometryType eReqType,
      56        5182 :                              char **papszCreateOptions)
      57             :     : OGRAbstractProxiedLayer(poDSIn->GetPool()), poDS(poDSIn),
      58             :       poFeatureDefn(nullptr), iNextShapeId(0), nTotalShapeCount(0),
      59       10364 :       pszFullName(CPLStrdup(pszFullNameIn)), hSHP(hSHPIn), hDBF(hDBFIn),
      60             :       bUpdateAccess(bUpdate), eRequestedGeomType(eReqType),
      61             :       panMatchingFIDs(nullptr), iMatchingFID(0),
      62             :       m_poFilterGeomLastValid(nullptr), nSpatialFIDCount(0),
      63             :       panSpatialFIDs(nullptr), bHeaderDirty(false), bSHPNeedsRepack(false),
      64             :       bCheckedForQIX(false), hQIX(nullptr), bCheckedForSBN(false),
      65             :       hSBN(nullptr), bSbnSbxDeleted(false), bTruncationWarningEmitted(false),
      66        5182 :       bHSHPWasNonNULL(hSHPIn != nullptr), bHDBFWasNonNULL(hDBFIn != nullptr),
      67             :       eFileDescriptorsState(FD_OPENED), bResizeAtClose(false),
      68             :       bCreateSpatialIndexAtClose(false), bRewindOnWrite(false),
      69        5182 :       m_bAutoRepack(false), m_eNeedRepack(MAYBE)
      70             : {
      71        5182 :     if (hSHP != nullptr)
      72             :     {
      73        4805 :         nTotalShapeCount = hSHP->nRecords;
      74        4805 :         if (hDBF != nullptr && hDBF->nRecords != nTotalShapeCount)
      75             :         {
      76           0 :             CPLDebug("Shape",
      77             :                      "Inconsistent record number in .shp (%d) and in .dbf (%d)",
      78           0 :                      hSHP->nRecords, hDBF->nRecords);
      79             :         }
      80             :     }
      81         377 :     else if (hDBF != nullptr)
      82             :     {
      83         377 :         nTotalShapeCount = hDBF->nRecords;
      84             :     }
      85             : #ifdef DEBUG
      86             :     else
      87             :     {
      88           0 :         CPLError(CE_Fatal, CPLE_AssertionFailed,
      89             :                  "Should not happen: Both hSHP and hDBF are nullptrs");
      90             :     }
      91             : #endif
      92             : 
      93        5182 :     if (!TouchLayer())
      94             :     {
      95           0 :         CPLDebug("Shape", "TouchLayer in shape ctor failed. ");
      96             :     }
      97             : 
      98        5182 :     if (hDBF != nullptr && hDBF->pszCodePage != nullptr)
      99             :     {
     100        4361 :         CPLDebug("Shape", "DBF Codepage = %s for %s", hDBF->pszCodePage,
     101             :                  pszFullName);
     102             : 
     103             :         // Not too sure about this, but it seems like better than nothing.
     104        4361 :         osEncoding = ConvertCodePage(hDBF->pszCodePage);
     105             :     }
     106             : 
     107        5182 :     if (hDBF != nullptr)
     108             :     {
     109        5141 :         if (!(hDBF->nUpdateYearSince1900 == 95 && hDBF->nUpdateMonth == 7 &&
     110        1789 :               hDBF->nUpdateDay == 26))
     111             :         {
     112        3352 :             SetMetadataItem("DBF_DATE_LAST_UPDATE",
     113             :                             CPLSPrintf("%04d-%02d-%02d",
     114        3352 :                                        hDBF->nUpdateYearSince1900 + 1900,
     115        3352 :                                        hDBF->nUpdateMonth, hDBF->nUpdateDay));
     116             :         }
     117             :         struct tm tm;
     118        5141 :         CPLUnixTimeToYMDHMS(time(nullptr), &tm);
     119        5141 :         DBFSetLastModifiedDate(hDBF, tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
     120             :     }
     121             : 
     122             :     const char *pszShapeEncoding =
     123        5182 :         CSLFetchNameValue(poDS->GetOpenOptions(), "ENCODING");
     124        5182 :     if (pszShapeEncoding == nullptr && osEncoding == "")
     125         821 :         pszShapeEncoding = CSLFetchNameValue(papszCreateOptions, "ENCODING");
     126        5182 :     if (pszShapeEncoding == nullptr)
     127        5181 :         pszShapeEncoding = CPLGetConfigOption("SHAPE_ENCODING", nullptr);
     128        5182 :     if (pszShapeEncoding != nullptr)
     129           1 :         osEncoding = pszShapeEncoding;
     130             : 
     131        5182 :     if (osEncoding != "")
     132             :     {
     133        4360 :         CPLDebug("Shape", "Treating as encoding '%s'.", osEncoding.c_str());
     134             : 
     135        4360 :         if (!OGRShapeLayer::TestCapability(OLCStringsAsUTF8))
     136             :         {
     137           1 :             CPLDebug("Shape", "Cannot recode from '%s'. Disabling recoding",
     138             :                      osEncoding.c_str());
     139           1 :             osEncoding = "";
     140             :         }
     141             :     }
     142        5182 :     SetMetadataItem("SOURCE_ENCODING", osEncoding, "SHAPEFILE");
     143             : 
     144        5182 :     poFeatureDefn = SHPReadOGRFeatureDefn(
     145       10364 :         CPLGetBasenameSafe(pszFullName).c_str(), hSHP, hDBF, osEncoding,
     146        5182 :         CPLFetchBool(poDS->GetOpenOptions(), "ADJUST_TYPE", false));
     147             : 
     148             :     // To make sure that
     149             :     //  GetLayerDefn()->GetGeomFieldDefn(0)->GetSpatialRef() == GetSpatialRef()
     150        5182 :     OGRwkbGeometryType eGeomType = poFeatureDefn->GetGeomType();
     151        5182 :     if (eGeomType != wkbNone)
     152             :     {
     153        4805 :         OGRwkbGeometryType eType = wkbUnknown;
     154             : 
     155        4805 :         if (eRequestedGeomType == wkbNone)
     156             :         {
     157        3267 :             eType = eGeomType;
     158             : 
     159        3267 :             const char *pszAdjustGeomType = CSLFetchNameValueDef(
     160        3267 :                 poDS->GetOpenOptions(), "ADJUST_GEOM_TYPE", "FIRST_SHAPE");
     161        3267 :             const bool bFirstShape = EQUAL(pszAdjustGeomType, "FIRST_SHAPE");
     162        3267 :             const bool bAllShapes = EQUAL(pszAdjustGeomType, "ALL_SHAPES");
     163        3269 :             if ((hSHP != nullptr) && (hSHP->nRecords > 0) && wkbHasM(eType) &&
     164           2 :                 (bFirstShape || bAllShapes))
     165             :             {
     166         236 :                 bool bMIsUsed = false;
     167         237 :                 for (int iShape = 0; iShape < hSHP->nRecords; iShape++)
     168             :                 {
     169         237 :                     SHPObject *psShape = SHPReadObject(hSHP, iShape);
     170         237 :                     if (psShape)
     171             :                     {
     172         237 :                         if (psShape->bMeasureIsUsed && psShape->nVertices > 0 &&
     173          45 :                             psShape->padfM != nullptr)
     174             :                         {
     175          48 :                             for (int i = 0; i < psShape->nVertices; i++)
     176             :                             {
     177             :                                 // Per the spec, if the M value is smaller than
     178             :                                 // -1e38, it is a nodata value.
     179          45 :                                 if (psShape->padfM[i] > -1e38)
     180             :                                 {
     181          42 :                                     bMIsUsed = true;
     182          42 :                                     break;
     183             :                                 }
     184             :                             }
     185             :                         }
     186             : 
     187         237 :                         SHPDestroyObject(psShape);
     188             :                     }
     189         237 :                     if (bFirstShape || bMIsUsed)
     190             :                         break;
     191             :                 }
     192         236 :                 if (!bMIsUsed)
     193         194 :                     eType = OGR_GT_SetModifier(eType, wkbHasZ(eType), FALSE);
     194             :             }
     195             :         }
     196             :         else
     197             :         {
     198        1538 :             eType = eRequestedGeomType;
     199             :         }
     200             : 
     201        4805 :         OGRSpatialReference *poSRSClone = poSRSIn ? poSRSIn->Clone() : nullptr;
     202        4805 :         if (poSRSClone)
     203             :         {
     204         170 :             poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     205             :         }
     206             :         auto poGeomFieldDefn = std::make_unique<OGRShapeGeomFieldDefn>(
     207        4805 :             pszFullName, eType, bSRSSetIn, poSRSClone);
     208        4805 :         if (!osPrjFilename.empty())
     209         170 :             poGeomFieldDefn->SetPrjFilename(osPrjFilename);
     210        4805 :         if (poSRSClone)
     211         170 :             poSRSClone->Release();
     212        4805 :         poFeatureDefn->SetGeomType(wkbNone);
     213        4805 :         poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     214             :     }
     215             : 
     216        5182 :     SetDescription(poFeatureDefn->GetName());
     217        5182 :     bRewindOnWrite = CPLTestBool(CPLGetConfigOption(
     218             :         "SHAPE_REWIND_ON_WRITE",
     219        5182 :         hSHP != nullptr && hSHP->nShapeType != SHPT_MULTIPATCH ? "NO" : "YES"));
     220             : 
     221        5182 :     poFeatureDefn->Seal(/* bSealFields = */ true);
     222        5182 : }
     223             : 
     224             : /************************************************************************/
     225             : /*                           ~OGRShapeLayer()                           */
     226             : /************************************************************************/
     227             : 
     228       10350 : OGRShapeLayer::~OGRShapeLayer()
     229             : 
     230             : {
     231        5175 :     if (m_eNeedRepack == YES && m_bAutoRepack)
     232          10 :         Repack();
     233             : 
     234        5175 :     if (bResizeAtClose && hDBF != nullptr)
     235             :     {
     236           1 :         ResizeDBF();
     237             :     }
     238        5175 :     if (bCreateSpatialIndexAtClose && hSHP != nullptr)
     239             :     {
     240           1 :         CreateSpatialIndex(0);
     241             :     }
     242             : 
     243        5175 :     if (m_nFeaturesRead > 0 && poFeatureDefn != nullptr)
     244             :     {
     245        4230 :         CPLDebug("Shape", "%d features read on layer '%s'.",
     246        2115 :                  static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
     247             :     }
     248             : 
     249        5175 :     ClearMatchingFIDs();
     250        5175 :     ClearSpatialFIDs();
     251             : 
     252        5175 :     CPLFree(pszFullName);
     253             : 
     254        5175 :     if (poFeatureDefn != nullptr)
     255        5175 :         poFeatureDefn->Release();
     256             : 
     257        5175 :     if (hDBF != nullptr)
     258        3134 :         DBFClose(hDBF);
     259             : 
     260        5175 :     if (hSHP != nullptr)
     261        2797 :         SHPClose(hSHP);
     262             : 
     263        5175 :     if (hQIX != nullptr)
     264          36 :         SHPCloseDiskTree(hQIX);
     265             : 
     266        5175 :     if (hSBN != nullptr)
     267           3 :         SBNCloseDiskTree(hSBN);
     268       10350 : }
     269             : 
     270             : /************************************************************************/
     271             : /*                       SetModificationDate()                          */
     272             : /************************************************************************/
     273             : 
     274        5182 : void OGRShapeLayer::SetModificationDate(const char *pszStr)
     275             : {
     276        5182 :     if (hDBF && pszStr)
     277             :     {
     278          50 :         int year = 0;
     279          50 :         int month = 0;
     280          50 :         int day = 0;
     281         100 :         if ((sscanf(pszStr, "%04d-%02d-%02d", &year, &month, &day) == 3 ||
     282         100 :              sscanf(pszStr, "%04d/%02d/%02d", &year, &month, &day) == 3) &&
     283          50 :             (year >= 1900 && year <= 1900 + 255 && month >= 1 && month <= 12 &&
     284          50 :              day >= 1 && day <= 31))
     285             :         {
     286          50 :             DBFSetLastModifiedDate(hDBF, year - 1900, month, day);
     287             :         }
     288             :     }
     289        5182 : }
     290             : 
     291             : /************************************************************************/
     292             : /*                       SetWriteDBFEOFChar()                           */
     293             : /************************************************************************/
     294             : 
     295        5182 : void OGRShapeLayer::SetWriteDBFEOFChar(bool b)
     296             : {
     297        5182 :     if (hDBF)
     298             :     {
     299        5141 :         DBFSetWriteEndOfFileChar(hDBF, b);
     300             :     }
     301        5182 : }
     302             : 
     303             : /************************************************************************/
     304             : /*                          ConvertCodePage()                           */
     305             : /************************************************************************/
     306             : 
     307        4336 : static CPLString GetEncodingFromLDIDNumber(int nLDID)
     308             : {
     309        4336 :     int nCP = -1;  // Windows code page.
     310             : 
     311             :     // http://www.autopark.ru/ASBProgrammerGuide/DBFSTRUC.HTM
     312        4336 :     switch (nLDID)
     313             :     {
     314           0 :         case 1:
     315           0 :             nCP = 437;
     316           0 :             break;
     317           0 :         case 2:
     318           0 :             nCP = 850;
     319           0 :             break;
     320           1 :         case 3:
     321           1 :             nCP = 1252;
     322           1 :             break;
     323           0 :         case 4:
     324           0 :             nCP = 10000;
     325           0 :             break;
     326           0 :         case 8:
     327           0 :             nCP = 865;
     328           0 :             break;
     329           0 :         case 10:
     330           0 :             nCP = 850;
     331           0 :             break;
     332           0 :         case 11:
     333           0 :             nCP = 437;
     334           0 :             break;
     335           0 :         case 13:
     336           0 :             nCP = 437;
     337           0 :             break;
     338           0 :         case 14:
     339           0 :             nCP = 850;
     340           0 :             break;
     341           0 :         case 15:
     342           0 :             nCP = 437;
     343           0 :             break;
     344           0 :         case 16:
     345           0 :             nCP = 850;
     346           0 :             break;
     347           0 :         case 17:
     348           0 :             nCP = 437;
     349           0 :             break;
     350           0 :         case 18:
     351           0 :             nCP = 850;
     352           0 :             break;
     353           0 :         case 19:
     354           0 :             nCP = 932;
     355           0 :             break;
     356           0 :         case 20:
     357           0 :             nCP = 850;
     358           0 :             break;
     359           0 :         case 21:
     360           0 :             nCP = 437;
     361           0 :             break;
     362           0 :         case 22:
     363           0 :             nCP = 850;
     364           0 :             break;
     365           0 :         case 23:
     366           0 :             nCP = 865;
     367           0 :             break;
     368           0 :         case 24:
     369           0 :             nCP = 437;
     370           0 :             break;
     371           0 :         case 25:
     372           0 :             nCP = 437;
     373           0 :             break;
     374           0 :         case 26:
     375           0 :             nCP = 850;
     376           0 :             break;
     377           0 :         case 27:
     378           0 :             nCP = 437;
     379           0 :             break;
     380           0 :         case 28:
     381           0 :             nCP = 863;
     382           0 :             break;
     383           0 :         case 29:
     384           0 :             nCP = 850;
     385           0 :             break;
     386           0 :         case 31:
     387           0 :             nCP = 852;
     388           0 :             break;
     389           0 :         case 34:
     390           0 :             nCP = 852;
     391           0 :             break;
     392           0 :         case 35:
     393           0 :             nCP = 852;
     394           0 :             break;
     395           0 :         case 36:
     396           0 :             nCP = 860;
     397           0 :             break;
     398           0 :         case 37:
     399           0 :             nCP = 850;
     400           0 :             break;
     401           0 :         case 38:
     402           0 :             nCP = 866;
     403           0 :             break;
     404           0 :         case 55:
     405           0 :             nCP = 850;
     406           0 :             break;
     407           0 :         case 64:
     408           0 :             nCP = 852;
     409           0 :             break;
     410           1 :         case 77:
     411           1 :             nCP = 936;
     412           1 :             break;
     413           0 :         case 78:
     414           0 :             nCP = 949;
     415           0 :             break;
     416           0 :         case 79:
     417           0 :             nCP = 950;
     418           0 :             break;
     419           0 :         case 80:
     420           0 :             nCP = 874;
     421           0 :             break;
     422        4211 :         case 87:
     423        4211 :             return CPL_ENC_ISO8859_1;
     424         123 :         case 88:
     425         123 :             nCP = 1252;
     426         123 :             break;
     427           0 :         case 89:
     428           0 :             nCP = 1252;
     429           0 :             break;
     430           0 :         case 100:
     431           0 :             nCP = 852;
     432           0 :             break;
     433           0 :         case 101:
     434           0 :             nCP = 866;
     435           0 :             break;
     436           0 :         case 102:
     437           0 :             nCP = 865;
     438           0 :             break;
     439           0 :         case 103:
     440           0 :             nCP = 861;
     441           0 :             break;
     442           0 :         case 104:
     443           0 :             nCP = 895;
     444           0 :             break;
     445           0 :         case 105:
     446           0 :             nCP = 620;
     447           0 :             break;
     448           0 :         case 106:
     449           0 :             nCP = 737;
     450           0 :             break;
     451           0 :         case 107:
     452           0 :             nCP = 857;
     453           0 :             break;
     454           0 :         case 108:
     455           0 :             nCP = 863;
     456           0 :             break;
     457           0 :         case 120:
     458           0 :             nCP = 950;
     459           0 :             break;
     460           0 :         case 121:
     461           0 :             nCP = 949;
     462           0 :             break;
     463           0 :         case 122:
     464           0 :             nCP = 936;
     465           0 :             break;
     466           0 :         case 123:
     467           0 :             nCP = 932;
     468           0 :             break;
     469           0 :         case 124:
     470           0 :             nCP = 874;
     471           0 :             break;
     472           0 :         case 134:
     473           0 :             nCP = 737;
     474           0 :             break;
     475           0 :         case 135:
     476           0 :             nCP = 852;
     477           0 :             break;
     478           0 :         case 136:
     479           0 :             nCP = 857;
     480           0 :             break;
     481           0 :         case 150:
     482           0 :             nCP = 10007;
     483           0 :             break;
     484           0 :         case 151:
     485           0 :             nCP = 10029;
     486           0 :             break;
     487           0 :         case 200:
     488           0 :             nCP = 1250;
     489           0 :             break;
     490           0 :         case 201:
     491           0 :             nCP = 1251;
     492           0 :             break;
     493           0 :         case 202:
     494           0 :             nCP = 1254;
     495           0 :             break;
     496           0 :         case 203:
     497           0 :             nCP = 1253;
     498           0 :             break;
     499           0 :         case 204:
     500           0 :             nCP = 1257;
     501           0 :             break;
     502           0 :         default:
     503           0 :             break;
     504             :     }
     505             : 
     506         125 :     if (nCP < 0)
     507           0 :         return CPLString();
     508         250 :     return CPLString().Printf("CP%d", nCP);
     509             : }
     510             : 
     511          28 : static CPLString GetEncodingFromCPG(const char *pszCPG)
     512             : {
     513             :     // see https://support.esri.com/en/technical-article/000013192
     514          28 :     CPLString osEncodingFromCPG;
     515          28 :     const int nCPG = atoi(pszCPG);
     516          28 :     if ((nCPG >= 437 && nCPG <= 950) || (nCPG >= 1250 && nCPG <= 1258))
     517             :     {
     518           0 :         osEncodingFromCPG.Printf("CP%d", nCPG);
     519             :     }
     520          28 :     else if (STARTS_WITH_CI(pszCPG, "8859"))
     521             :     {
     522           0 :         if (pszCPG[4] == '-')
     523           0 :             osEncodingFromCPG.Printf("ISO-8859-%s", pszCPG + 5);
     524             :         else
     525           0 :             osEncodingFromCPG.Printf("ISO-8859-%s", pszCPG + 4);
     526             :     }
     527          28 :     else if (STARTS_WITH_CI(pszCPG, "UTF-8") || STARTS_WITH_CI(pszCPG, "UTF8"))
     528          27 :         osEncodingFromCPG = CPL_ENC_UTF8;
     529           1 :     else if (STARTS_WITH_CI(pszCPG, "ANSI 1251"))
     530           0 :         osEncodingFromCPG = "CP1251";
     531             :     else
     532             :     {
     533             :         // Try just using the CPG value directly.  Works for stuff like Big5.
     534           1 :         osEncodingFromCPG = pszCPG;
     535             :     }
     536          28 :     return osEncodingFromCPG;
     537             : }
     538             : 
     539        4361 : CPLString OGRShapeLayer::ConvertCodePage(const char *pszCodePage)
     540             : 
     541             : {
     542        4361 :     CPLString l_osEncoding;
     543             : 
     544        4361 :     if (pszCodePage == nullptr)
     545           0 :         return l_osEncoding;
     546             : 
     547        8722 :     std::string osEncodingFromLDID;
     548        4361 :     if (hDBF->iLanguageDriver != 0)
     549             :     {
     550        4336 :         SetMetadataItem("LDID_VALUE", CPLSPrintf("%d", hDBF->iLanguageDriver),
     551        4336 :                         "SHAPEFILE");
     552             : 
     553        4336 :         osEncodingFromLDID = GetEncodingFromLDIDNumber(hDBF->iLanguageDriver);
     554             :     }
     555        4361 :     if (!osEncodingFromLDID.empty())
     556             :     {
     557        4336 :         SetMetadataItem("ENCODING_FROM_LDID", osEncodingFromLDID.c_str(),
     558        4336 :                         "SHAPEFILE");
     559             :     }
     560             : 
     561        8722 :     std::string osEncodingFromCPG;
     562        4361 :     if (!STARTS_WITH_CI(pszCodePage, "LDID/"))
     563             :     {
     564          28 :         SetMetadataItem("CPG_VALUE", pszCodePage, "SHAPEFILE");
     565             : 
     566          28 :         osEncodingFromCPG = GetEncodingFromCPG(pszCodePage);
     567             : 
     568          28 :         if (!osEncodingFromCPG.empty())
     569          28 :             SetMetadataItem("ENCODING_FROM_CPG", osEncodingFromCPG.c_str(),
     570          28 :                             "SHAPEFILE");
     571             : 
     572          28 :         l_osEncoding = std::move(osEncodingFromCPG);
     573             :     }
     574        4333 :     else if (!osEncodingFromLDID.empty())
     575             :     {
     576        4333 :         l_osEncoding = std::move(osEncodingFromLDID);
     577             :     }
     578             : 
     579        4361 :     return l_osEncoding;
     580             : }
     581             : 
     582             : /************************************************************************/
     583             : /*                            CheckForQIX()                             */
     584             : /************************************************************************/
     585             : 
     586       64169 : bool OGRShapeLayer::CheckForQIX()
     587             : 
     588             : {
     589       64169 :     if (bCheckedForQIX)
     590       62483 :         return hQIX != nullptr;
     591             : 
     592        1686 :     const std::string osQIXFilename = CPLResetExtensionSafe(pszFullName, "qix");
     593             : 
     594        1686 :     hQIX = SHPOpenDiskTree(osQIXFilename.c_str(), nullptr);
     595             : 
     596        1686 :     bCheckedForQIX = true;
     597             : 
     598        1686 :     return hQIX != nullptr;
     599             : }
     600             : 
     601             : /************************************************************************/
     602             : /*                            CheckForSBN()                             */
     603             : /************************************************************************/
     604             : 
     605       64082 : bool OGRShapeLayer::CheckForSBN()
     606             : 
     607             : {
     608       64082 :     if (bCheckedForSBN)
     609       62442 :         return hSBN != nullptr;
     610             : 
     611        1640 :     const std::string osSBNFilename = CPLResetExtensionSafe(pszFullName, "sbn");
     612             : 
     613        1640 :     hSBN = SBNOpenDiskTree(osSBNFilename.c_str(), nullptr);
     614             : 
     615        1640 :     bCheckedForSBN = true;
     616             : 
     617        1640 :     return hSBN != nullptr;
     618             : }
     619             : 
     620             : /************************************************************************/
     621             : /*                            ScanIndices()                             */
     622             : /*                                                                      */
     623             : /*      Utilize optional spatial and attribute indices if they are      */
     624             : /*      available.                                                      */
     625             : /************************************************************************/
     626             : 
     627       13171 : bool OGRShapeLayer::ScanIndices()
     628             : 
     629             : {
     630       13171 :     iMatchingFID = 0;
     631             : 
     632             :     /* -------------------------------------------------------------------- */
     633             :     /*      Utilize attribute index if appropriate.                         */
     634             :     /* -------------------------------------------------------------------- */
     635       13171 :     if (m_poAttrQuery != nullptr)
     636             :     {
     637         570 :         CPLAssert(panMatchingFIDs == nullptr);
     638             : 
     639         570 :         InitializeIndexSupport(pszFullName);
     640             : 
     641         570 :         panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices(this, nullptr);
     642             :     }
     643             : 
     644             :     /* -------------------------------------------------------------------- */
     645             :     /*      Check for spatial index if we have a spatial query.             */
     646             :     /* -------------------------------------------------------------------- */
     647             : 
     648       13171 :     if (m_poFilterGeom == nullptr || hSHP == nullptr)
     649         539 :         return true;
     650             : 
     651       12632 :     OGREnvelope oSpatialFilterEnvelope;
     652       12632 :     bool bTryQIXorSBN = true;
     653             : 
     654       12632 :     m_poFilterGeom->getEnvelope(&oSpatialFilterEnvelope);
     655             : 
     656       12632 :     OGREnvelope oLayerExtent;
     657       12632 :     if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
     658             :     {
     659       12632 :         if (oSpatialFilterEnvelope.Contains(oLayerExtent))
     660             :         {
     661             :             // The spatial filter is larger than the layer extent. No use of
     662             :             // .qix file for now.
     663          30 :             return true;
     664             :         }
     665       12602 :         else if (!oSpatialFilterEnvelope.Intersects(oLayerExtent))
     666             :         {
     667             :             // No intersection : no need to check for .qix or .sbn.
     668          39 :             bTryQIXorSBN = false;
     669             : 
     670             :             // Set an empty result for spatial FIDs.
     671          39 :             free(panSpatialFIDs);
     672          39 :             panSpatialFIDs = static_cast<int *>(calloc(1, sizeof(int)));
     673          39 :             nSpatialFIDCount = 0;
     674             : 
     675          39 :             delete m_poFilterGeomLastValid;
     676          39 :             m_poFilterGeomLastValid = m_poFilterGeom->clone();
     677             :         }
     678             :     }
     679             : 
     680       12602 :     if (bTryQIXorSBN)
     681             :     {
     682       12563 :         if (!bCheckedForQIX)
     683          52 :             CPL_IGNORE_RET_VAL(CheckForQIX());
     684       12563 :         if (hQIX == nullptr && !bCheckedForSBN)
     685          36 :             CPL_IGNORE_RET_VAL(CheckForSBN());
     686             :     }
     687             : 
     688             :     /* -------------------------------------------------------------------- */
     689             :     /*      Compute spatial index if appropriate.                           */
     690             :     /* -------------------------------------------------------------------- */
     691       12602 :     if (bTryQIXorSBN && (hQIX != nullptr || hSBN != nullptr) &&
     692       12335 :         panSpatialFIDs == nullptr)
     693             :     {
     694       12330 :         double adfBoundsMin[4] = {oSpatialFilterEnvelope.MinX,
     695       12330 :                                   oSpatialFilterEnvelope.MinY, 0.0, 0.0};
     696       12330 :         double adfBoundsMax[4] = {oSpatialFilterEnvelope.MaxX,
     697       12330 :                                   oSpatialFilterEnvelope.MaxY, 0.0, 0.0};
     698             : 
     699       12330 :         if (hQIX != nullptr)
     700       12324 :             panSpatialFIDs = SHPSearchDiskTreeEx(
     701             :                 hQIX, adfBoundsMin, adfBoundsMax, &nSpatialFIDCount);
     702             :         else
     703           6 :             panSpatialFIDs = SBNSearchDiskTree(hSBN, adfBoundsMin, adfBoundsMax,
     704             :                                                &nSpatialFIDCount);
     705             : 
     706       12330 :         CPLDebug("SHAPE", "Used spatial index, got %d matches.",
     707             :                  nSpatialFIDCount);
     708             : 
     709       12330 :         delete m_poFilterGeomLastValid;
     710       12330 :         m_poFilterGeomLastValid = m_poFilterGeom->clone();
     711             :     }
     712             : 
     713             :     /* -------------------------------------------------------------------- */
     714             :     /*      Use spatial index if appropriate.                               */
     715             :     /* -------------------------------------------------------------------- */
     716       12602 :     if (panSpatialFIDs != nullptr)
     717             :     {
     718             :         // Use resulting list as matching FID list (but reallocate and
     719             :         // terminate with OGRNullFID).
     720       12374 :         if (panMatchingFIDs == nullptr)
     721             :         {
     722       12372 :             panMatchingFIDs = static_cast<GIntBig *>(
     723       12372 :                 CPLMalloc(sizeof(GIntBig) * (nSpatialFIDCount + 1)));
     724      219162 :             for (int i = 0; i < nSpatialFIDCount; i++)
     725      206790 :                 panMatchingFIDs[i] = static_cast<GIntBig>(panSpatialFIDs[i]);
     726       12372 :             panMatchingFIDs[nSpatialFIDCount] = OGRNullFID;
     727             :         }
     728             :         // Cull attribute index matches based on those in the spatial index
     729             :         // result set.  We assume that the attribute results are in sorted
     730             :         // order.
     731             :         else
     732             :         {
     733           2 :             int iWrite = 0;
     734           2 :             int iSpatial = 0;
     735             : 
     736           4 :             for (int iRead = 0; panMatchingFIDs[iRead] != OGRNullFID; iRead++)
     737             :             {
     738           7 :                 while (iSpatial < nSpatialFIDCount &&
     739           7 :                        panSpatialFIDs[iSpatial] < panMatchingFIDs[iRead])
     740           5 :                     iSpatial++;
     741             : 
     742           2 :                 if (iSpatial == nSpatialFIDCount)
     743           0 :                     continue;
     744             : 
     745           2 :                 if (panSpatialFIDs[iSpatial] == panMatchingFIDs[iRead])
     746           2 :                     panMatchingFIDs[iWrite++] = panMatchingFIDs[iRead];
     747             :             }
     748           2 :             panMatchingFIDs[iWrite] = OGRNullFID;
     749             :         }
     750             : 
     751       12374 :         if (nSpatialFIDCount > 100000)
     752             :         {
     753           0 :             ClearSpatialFIDs();
     754             :         }
     755             :     }
     756             : 
     757       12602 :     return true;
     758             : }
     759             : 
     760             : /************************************************************************/
     761             : /*                            ResetReading()                            */
     762             : /************************************************************************/
     763             : 
     764       29995 : void OGRShapeLayer::ResetReading()
     765             : 
     766             : {
     767       29995 :     if (!TouchLayer())
     768           0 :         return;
     769             : 
     770       29995 :     iMatchingFID = 0;
     771             : 
     772       29995 :     iNextShapeId = 0;
     773             : 
     774       29995 :     if (bHeaderDirty && bUpdateAccess)
     775         135 :         SyncToDisk();
     776             : 
     777       29995 :     if (hDBF)
     778       29956 :         VSIFClearErrL(VSI_SHP_GetVSIL(hDBF->fp));
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                        ClearMatchingFIDs()                           */
     783             : /************************************************************************/
     784             : 
     785       22511 : void OGRShapeLayer::ClearMatchingFIDs()
     786             : {
     787             :     /* -------------------------------------------------------------------- */
     788             :     /*      Clear previous index search result, if any.                     */
     789             :     /* -------------------------------------------------------------------- */
     790       22511 :     CPLFree(panMatchingFIDs);
     791       22511 :     panMatchingFIDs = nullptr;
     792       22511 : }
     793             : 
     794             : /************************************************************************/
     795             : /*                        ClearSpatialFIDs()                           */
     796             : /************************************************************************/
     797             : 
     798       17499 : void OGRShapeLayer::ClearSpatialFIDs()
     799             : {
     800       17499 :     if (panSpatialFIDs != nullptr)
     801             :     {
     802       12359 :         CPLDebug("SHAPE", "Clear panSpatialFIDs");
     803       12359 :         free(panSpatialFIDs);
     804             :     }
     805       17499 :     panSpatialFIDs = nullptr;
     806       17499 :     nSpatialFIDCount = 0;
     807             : 
     808       17499 :     delete m_poFilterGeomLastValid;
     809       17499 :     m_poFilterGeomLastValid = nullptr;
     810       17499 : }
     811             : 
     812             : /************************************************************************/
     813             : /*                         ISetSpatialFilter()                          */
     814             : /************************************************************************/
     815             : 
     816       14704 : OGRErr OGRShapeLayer::ISetSpatialFilter(int iGeomField,
     817             :                                         const OGRGeometry *poGeomIn)
     818             : {
     819       14704 :     ClearMatchingFIDs();
     820             : 
     821       14704 :     if (poGeomIn == nullptr)
     822             :     {
     823             :         // Do nothing.
     824             :     }
     825       24972 :     else if (m_poFilterGeomLastValid != nullptr &&
     826       12331 :              m_poFilterGeomLastValid->Equals(poGeomIn))
     827             :     {
     828             :         // Do nothing.
     829             :     }
     830       12626 :     else if (panSpatialFIDs != nullptr)
     831             :     {
     832             :         // We clear the spatialFIDs only if we have a new non-NULL spatial
     833             :         // filter, otherwise we keep the previous result cached. This can be
     834             :         // useful when several SQL layers rely on the same table layer, and use
     835             :         // the same spatial filters. But as there is in the destructor of
     836             :         // OGRGenSQLResultsLayer a clearing of the spatial filter of the table
     837             :         // layer, we need this trick.
     838       12316 :         ClearSpatialFIDs();
     839             :     }
     840             : 
     841       14704 :     return OGRLayer::ISetSpatialFilter(iGeomField, poGeomIn);
     842             : }
     843             : 
     844             : /************************************************************************/
     845             : /*                         SetAttributeFilter()                         */
     846             : /************************************************************************/
     847             : 
     848        2632 : OGRErr OGRShapeLayer::SetAttributeFilter(const char *pszAttributeFilter)
     849             : {
     850        2632 :     ClearMatchingFIDs();
     851             : 
     852        2632 :     return OGRLayer::SetAttributeFilter(pszAttributeFilter);
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                           SetNextByIndex()                           */
     857             : /*                                                                      */
     858             : /*      If we already have an FID list, we can easily reposition        */
     859             : /*      ourselves in it.                                                */
     860             : /************************************************************************/
     861             : 
     862          47 : OGRErr OGRShapeLayer::SetNextByIndex(GIntBig nIndex)
     863             : 
     864             : {
     865          47 :     if (!TouchLayer())
     866           0 :         return OGRERR_FAILURE;
     867             : 
     868          47 :     if (nIndex < 0 || nIndex > INT_MAX)
     869           6 :         return OGRERR_FAILURE;
     870             : 
     871             :     // Eventually we should try to use panMatchingFIDs list
     872             :     // if available and appropriate.
     873          41 :     if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
     874           1 :         return OGRLayer::SetNextByIndex(nIndex);
     875             : 
     876          40 :     iNextShapeId = static_cast<int>(nIndex);
     877             : 
     878          40 :     return OGRERR_NONE;
     879             : }
     880             : 
     881             : /************************************************************************/
     882             : /*                             FetchShape()                             */
     883             : /*                                                                      */
     884             : /*      Take a shape id, a geometry, and a feature, and set the feature */
     885             : /*      if the shapeid bbox intersects the geometry.                    */
     886             : /************************************************************************/
     887             : 
     888      183112 : OGRFeature *OGRShapeLayer::FetchShape(int iShapeId)
     889             : 
     890             : {
     891      183112 :     OGRFeature *poFeature = nullptr;
     892             : 
     893      183112 :     if (m_poFilterGeom != nullptr && hSHP != nullptr)
     894             :     {
     895      120559 :         SHPObject *psShape = SHPReadObject(hSHP, iShapeId);
     896             : 
     897             :         // do not trust degenerate bounds on non-point geometries
     898             :         // or bounds on null shapes.
     899      120559 :         if (psShape == nullptr ||
     900      120554 :             (psShape->nSHPType != SHPT_POINT &&
     901      117971 :              psShape->nSHPType != SHPT_POINTZ &&
     902      103330 :              psShape->nSHPType != SHPT_POINTM &&
     903      103330 :              (psShape->dfXMin == psShape->dfXMax ||
     904      103324 :               psShape->dfYMin == psShape->dfYMax)) ||
     905      120548 :             psShape->nSHPType == SHPT_NULL)
     906             :         {
     907          11 :             poFeature =
     908          11 :                 SHPReadOGRFeature(hSHP, hDBF, poFeatureDefn, iShapeId, psShape,
     909          11 :                                   osEncoding, m_bHasWarnedWrongWindingOrder);
     910             :         }
     911      120548 :         else if (m_sFilterEnvelope.MaxX < psShape->dfXMin ||
     912       77979 :                  m_sFilterEnvelope.MaxY < psShape->dfYMin ||
     913       53794 :                  psShape->dfXMax < m_sFilterEnvelope.MinX ||
     914       34728 :                  psShape->dfYMax < m_sFilterEnvelope.MinY)
     915             :         {
     916       90978 :             SHPDestroyObject(psShape);
     917       90978 :             poFeature = nullptr;
     918             :         }
     919             :         else
     920             :         {
     921             :             poFeature =
     922       29570 :                 SHPReadOGRFeature(hSHP, hDBF, poFeatureDefn, iShapeId, psShape,
     923       29570 :                                   osEncoding, m_bHasWarnedWrongWindingOrder);
     924      120559 :         }
     925             :     }
     926             :     else
     927             :     {
     928             :         poFeature =
     929       62553 :             SHPReadOGRFeature(hSHP, hDBF, poFeatureDefn, iShapeId, nullptr,
     930       62553 :                               osEncoding, m_bHasWarnedWrongWindingOrder);
     931             :     }
     932             : 
     933      183112 :     return poFeature;
     934             : }
     935             : 
     936             : /************************************************************************/
     937             : /*                           GetNextFeature()                           */
     938             : /************************************************************************/
     939             : 
     940       90233 : OGRFeature *OGRShapeLayer::GetNextFeature()
     941             : 
     942             : {
     943       90233 :     if (!TouchLayer())
     944           0 :         return nullptr;
     945             : 
     946             :     /* -------------------------------------------------------------------- */
     947             :     /*      Collect a matching list if we have attribute or spatial         */
     948             :     /*      indices.  Only do this on the first request for a given pass    */
     949             :     /*      of course.                                                      */
     950             :     /* -------------------------------------------------------------------- */
     951       90233 :     if ((m_poAttrQuery != nullptr || m_poFilterGeom != nullptr) &&
     952       30893 :         iNextShapeId == 0 && panMatchingFIDs == nullptr)
     953             :     {
     954       13136 :         ScanIndices();
     955             :     }
     956             : 
     957             :     /* -------------------------------------------------------------------- */
     958             :     /*      Loop till we find a feature matching our criteria.              */
     959             :     /* -------------------------------------------------------------------- */
     960       90233 :     OGRFeature *poFeature = nullptr;
     961             : 
     962             :     while (true)
     963             :     {
     964      184296 :         if (panMatchingFIDs != nullptr)
     965             :         {
     966      102726 :             if (panMatchingFIDs[iMatchingFID] == OGRNullFID)
     967             :             {
     968          51 :                 return nullptr;
     969             :             }
     970             : 
     971             :             // Check the shape object's geometry, and if it matches
     972             :             // any spatial filter, return it.
     973             :             poFeature =
     974      102675 :                 FetchShape(static_cast<int>(panMatchingFIDs[iMatchingFID]));
     975             : 
     976      102675 :             iMatchingFID++;
     977             :         }
     978             :         else
     979             :         {
     980       81570 :             if (iNextShapeId >= nTotalShapeCount)
     981             :             {
     982        1131 :                 return nullptr;
     983             :             }
     984             : 
     985       80439 :             if (hDBF)
     986             :             {
     987       80397 :                 if (DBFIsRecordDeleted(hDBF, iNextShapeId))
     988           2 :                     poFeature = nullptr;
     989      160790 :                 else if (VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ||
     990       80395 :                          VSIFErrorL(VSI_SHP_GetVSIL(hDBF->fp)))
     991           0 :                     return nullptr;  //* I/O error.
     992             :                 else
     993       80395 :                     poFeature = FetchShape(iNextShapeId);
     994             :             }
     995             :             else
     996          42 :                 poFeature = FetchShape(iNextShapeId);
     997             : 
     998       80439 :             iNextShapeId++;
     999             :         }
    1000             : 
    1001      183114 :         if (poFeature != nullptr)
    1002             :         {
    1003       92134 :             OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1004       92134 :             if (poGeom != nullptr)
    1005             :             {
    1006       86908 :                 poGeom->assignSpatialReference(GetSpatialRef());
    1007             :             }
    1008             : 
    1009       92134 :             m_nFeaturesRead++;
    1010             : 
    1011      184232 :             if ((m_poFilterGeom == nullptr || FilterGeometry(poGeom)) &&
    1012       92098 :                 (m_poAttrQuery == nullptr ||
    1013       18684 :                  m_poAttrQuery->Evaluate(poFeature)))
    1014             :             {
    1015       89051 :                 return poFeature;
    1016             :             }
    1017             : 
    1018        3083 :             delete poFeature;
    1019             :         }
    1020       94063 :     }
    1021             : }
    1022             : 
    1023             : /************************************************************************/
    1024             : /*                             GetFeature()                             */
    1025             : /************************************************************************/
    1026             : 
    1027         198 : OGRFeature *OGRShapeLayer::GetFeature(GIntBig nFeatureId)
    1028             : 
    1029             : {
    1030         198 :     if (!TouchLayer() || nFeatureId > INT_MAX)
    1031           6 :         return nullptr;
    1032             : 
    1033         192 :     OGRFeature *poFeature = SHPReadOGRFeature(
    1034             :         hSHP, hDBF, poFeatureDefn, static_cast<int>(nFeatureId), nullptr,
    1035         192 :         osEncoding, m_bHasWarnedWrongWindingOrder);
    1036             : 
    1037         192 :     if (poFeature == nullptr)
    1038             :     {
    1039             :         // Reading shape feature failed.
    1040          18 :         return nullptr;
    1041             :     }
    1042             : 
    1043         174 :     if (poFeature->GetGeometryRef() != nullptr)
    1044             :     {
    1045         128 :         poFeature->GetGeometryRef()->assignSpatialReference(GetSpatialRef());
    1046             :     }
    1047             : 
    1048         174 :     m_nFeaturesRead++;
    1049             : 
    1050         174 :     return poFeature;
    1051             : }
    1052             : 
    1053             : /************************************************************************/
    1054             : /*                             StartUpdate()                            */
    1055             : /************************************************************************/
    1056             : 
    1057       69076 : bool OGRShapeLayer::StartUpdate(const char *pszOperation)
    1058             : {
    1059       69076 :     if (!poDS->UncompressIfNeeded())
    1060           0 :         return false;
    1061             : 
    1062       69076 :     if (!TouchLayer())
    1063           0 :         return false;
    1064             : 
    1065       69076 :     if (!bUpdateAccess)
    1066             :     {
    1067          10 :         CPLError(CE_Failure, CPLE_NotSupported,
    1068             :                  "%s : unsupported operation on a read-only datasource.",
    1069             :                  pszOperation);
    1070          10 :         return false;
    1071             :     }
    1072             : 
    1073       69066 :     return true;
    1074             : }
    1075             : 
    1076             : /************************************************************************/
    1077             : /*                             ISetFeature()                             */
    1078             : /************************************************************************/
    1079             : 
    1080          65 : OGRErr OGRShapeLayer::ISetFeature(OGRFeature *poFeature)
    1081             : 
    1082             : {
    1083          65 :     if (!StartUpdate("SetFeature"))
    1084           1 :         return OGRERR_FAILURE;
    1085             : 
    1086          64 :     GIntBig nFID = poFeature->GetFID();
    1087          64 :     if (nFID < 0 || (hSHP != nullptr && nFID >= hSHP->nRecords) ||
    1088          60 :         (hDBF != nullptr && nFID >= hDBF->nRecords))
    1089             :     {
    1090           4 :         return OGRERR_NON_EXISTING_FEATURE;
    1091             :     }
    1092             : 
    1093          60 :     bHeaderDirty = true;
    1094          60 :     if (CheckForQIX() || CheckForSBN())
    1095           2 :         DropSpatialIndex();
    1096             : 
    1097          60 :     unsigned int nOffset = 0;
    1098          60 :     unsigned int nSize = 0;
    1099          60 :     bool bIsLastRecord = false;
    1100          60 :     if (hSHP != nullptr)
    1101             :     {
    1102          51 :         nOffset = hSHP->panRecOffset[nFID];
    1103          51 :         nSize = hSHP->panRecSize[nFID];
    1104          51 :         bIsLastRecord = (nOffset + nSize + 8 == hSHP->nFileSize);
    1105             :     }
    1106             : 
    1107             :     OGRErr eErr =
    1108          60 :         SHPWriteOGRFeature(hSHP, hDBF, poFeatureDefn, poFeature, osEncoding,
    1109          60 :                            &bTruncationWarningEmitted, bRewindOnWrite);
    1110             : 
    1111          60 :     if (hSHP != nullptr)
    1112             :     {
    1113          51 :         if (bIsLastRecord)
    1114             :         {
    1115             :             // Optimization: we don't need repacking if this is the last
    1116             :             // record of the file. Just potential truncation
    1117          19 :             CPLAssert(nOffset == hSHP->panRecOffset[nFID]);
    1118          19 :             CPLAssert(hSHP->panRecOffset[nFID] + hSHP->panRecSize[nFID] + 8 ==
    1119             :                       hSHP->nFileSize);
    1120          19 :             if (hSHP->panRecSize[nFID] < nSize)
    1121             :             {
    1122           1 :                 VSIFTruncateL(VSI_SHP_GetVSIL(hSHP->fpSHP), hSHP->nFileSize);
    1123             :             }
    1124             :         }
    1125          32 :         else if (nOffset != hSHP->panRecOffset[nFID] ||
    1126          26 :                  nSize != hSHP->panRecSize[nFID])
    1127             :         {
    1128          13 :             bSHPNeedsRepack = true;
    1129          13 :             m_eNeedRepack = YES;
    1130             :         }
    1131             :     }
    1132             : 
    1133          60 :     return eErr;
    1134             : }
    1135             : 
    1136             : /************************************************************************/
    1137             : /*                           DeleteFeature()                            */
    1138             : /************************************************************************/
    1139             : 
    1140         223 : OGRErr OGRShapeLayer::DeleteFeature(GIntBig nFID)
    1141             : 
    1142             : {
    1143         223 :     if (!StartUpdate("DeleteFeature"))
    1144           1 :         return OGRERR_FAILURE;
    1145             : 
    1146         222 :     if (nFID < 0 || (hSHP != nullptr && nFID >= hSHP->nRecords) ||
    1147         217 :         (hDBF != nullptr && nFID >= hDBF->nRecords))
    1148             :     {
    1149           5 :         return OGRERR_NON_EXISTING_FEATURE;
    1150             :     }
    1151             : 
    1152         217 :     if (!hDBF)
    1153             :     {
    1154           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1155             :                  "Attempt to delete shape in shapefile with no .dbf file.  "
    1156             :                  "Deletion is done by marking record deleted in dbf "
    1157             :                  "and is not supported without a .dbf file.");
    1158           2 :         return OGRERR_FAILURE;
    1159             :     }
    1160             : 
    1161         215 :     if (DBFIsRecordDeleted(hDBF, static_cast<int>(nFID)))
    1162             :     {
    1163           1 :         return OGRERR_NON_EXISTING_FEATURE;
    1164             :     }
    1165             : 
    1166         214 :     if (!DBFMarkRecordDeleted(hDBF, static_cast<int>(nFID), TRUE))
    1167           0 :         return OGRERR_FAILURE;
    1168             : 
    1169         214 :     bHeaderDirty = true;
    1170         214 :     if (CheckForQIX() || CheckForSBN())
    1171           1 :         DropSpatialIndex();
    1172         214 :     m_eNeedRepack = YES;
    1173             : 
    1174         214 :     return OGRERR_NONE;
    1175             : }
    1176             : 
    1177             : /************************************************************************/
    1178             : /*                           ICreateFeature()                            */
    1179             : /************************************************************************/
    1180             : 
    1181       63719 : OGRErr OGRShapeLayer::ICreateFeature(OGRFeature *poFeature)
    1182             : 
    1183             : {
    1184       63719 :     if (!StartUpdate("CreateFeature"))
    1185           2 :         return OGRERR_FAILURE;
    1186             : 
    1187      127433 :     if (hDBF != nullptr &&
    1188       63716 :         !VSI_SHP_WriteMoreDataOK(hDBF->fp, hDBF->nRecordLength))
    1189             :     {
    1190           0 :         return OGRERR_FAILURE;
    1191             :     }
    1192             : 
    1193       63717 :     bHeaderDirty = true;
    1194       63717 :     if (CheckForQIX() || CheckForSBN())
    1195           2 :         DropSpatialIndex();
    1196             : 
    1197       63717 :     poFeature->SetFID(OGRNullFID);
    1198             : 
    1199        1535 :     if (nTotalShapeCount == 0 && wkbFlatten(eRequestedGeomType) == wkbUnknown &&
    1200       66427 :         hSHP != nullptr && hSHP->nShapeType != SHPT_MULTIPATCH &&
    1201        1175 :         poFeature->GetGeometryRef() != nullptr)
    1202             :     {
    1203         647 :         OGRGeometry *poGeom = poFeature->GetGeometryRef();
    1204         647 :         int nShapeType = -1;
    1205             : 
    1206         647 :         switch (poGeom->getGeometryType())
    1207             :         {
    1208         524 :             case wkbPoint:
    1209         524 :                 nShapeType = SHPT_POINT;
    1210         524 :                 eRequestedGeomType = wkbPoint;
    1211         524 :                 break;
    1212             : 
    1213           9 :             case wkbPoint25D:
    1214           9 :                 nShapeType = SHPT_POINTZ;
    1215           9 :                 eRequestedGeomType = wkbPoint25D;
    1216           9 :                 break;
    1217             : 
    1218           1 :             case wkbPointM:
    1219           1 :                 nShapeType = SHPT_POINTM;
    1220           1 :                 eRequestedGeomType = wkbPointM;
    1221           1 :                 break;
    1222             : 
    1223           1 :             case wkbPointZM:
    1224           1 :                 nShapeType = SHPT_POINTZ;
    1225           1 :                 eRequestedGeomType = wkbPointZM;
    1226           1 :                 break;
    1227             : 
    1228           4 :             case wkbMultiPoint:
    1229           4 :                 nShapeType = SHPT_MULTIPOINT;
    1230           4 :                 eRequestedGeomType = wkbMultiPoint;
    1231           4 :                 break;
    1232             : 
    1233           2 :             case wkbMultiPoint25D:
    1234           2 :                 nShapeType = SHPT_MULTIPOINTZ;
    1235           2 :                 eRequestedGeomType = wkbMultiPoint25D;
    1236           2 :                 break;
    1237             : 
    1238           0 :             case wkbMultiPointM:
    1239           0 :                 nShapeType = SHPT_MULTIPOINTM;
    1240           0 :                 eRequestedGeomType = wkbMultiPointM;
    1241           0 :                 break;
    1242             : 
    1243           0 :             case wkbMultiPointZM:
    1244           0 :                 nShapeType = SHPT_MULTIPOINTZ;
    1245           0 :                 eRequestedGeomType = wkbMultiPointM;
    1246           0 :                 break;
    1247             : 
    1248          19 :             case wkbLineString:
    1249             :             case wkbMultiLineString:
    1250          19 :                 nShapeType = SHPT_ARC;
    1251          19 :                 eRequestedGeomType = wkbLineString;
    1252          19 :                 break;
    1253             : 
    1254           6 :             case wkbLineString25D:
    1255             :             case wkbMultiLineString25D:
    1256           6 :                 nShapeType = SHPT_ARCZ;
    1257           6 :                 eRequestedGeomType = wkbLineString25D;
    1258           6 :                 break;
    1259             : 
    1260           0 :             case wkbLineStringM:
    1261             :             case wkbMultiLineStringM:
    1262           0 :                 nShapeType = SHPT_ARCM;
    1263           0 :                 eRequestedGeomType = wkbLineStringM;
    1264           0 :                 break;
    1265             : 
    1266           0 :             case wkbLineStringZM:
    1267             :             case wkbMultiLineStringZM:
    1268           0 :                 nShapeType = SHPT_ARCZ;
    1269           0 :                 eRequestedGeomType = wkbLineStringZM;
    1270           0 :                 break;
    1271             : 
    1272          67 :             case wkbPolygon:
    1273             :             case wkbMultiPolygon:
    1274             :             case wkbTriangle:
    1275          67 :                 nShapeType = SHPT_POLYGON;
    1276          67 :                 eRequestedGeomType = wkbPolygon;
    1277          67 :                 break;
    1278             : 
    1279           7 :             case wkbPolygon25D:
    1280             :             case wkbMultiPolygon25D:
    1281             :             case wkbTriangleZ:
    1282           7 :                 nShapeType = SHPT_POLYGONZ;
    1283           7 :                 eRequestedGeomType = wkbPolygon25D;
    1284           7 :                 break;
    1285             : 
    1286           0 :             case wkbPolygonM:
    1287             :             case wkbMultiPolygonM:
    1288             :             case wkbTriangleM:
    1289           0 :                 nShapeType = SHPT_POLYGONM;
    1290           0 :                 eRequestedGeomType = wkbPolygonM;
    1291           0 :                 break;
    1292             : 
    1293           0 :             case wkbPolygonZM:
    1294             :             case wkbMultiPolygonZM:
    1295             :             case wkbTriangleZM:
    1296           0 :                 nShapeType = SHPT_POLYGONZ;
    1297           0 :                 eRequestedGeomType = wkbPolygonZM;
    1298           0 :                 break;
    1299             : 
    1300           7 :             default:
    1301           7 :                 nShapeType = -1;
    1302           7 :                 break;
    1303             :         }
    1304             : 
    1305        1289 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbTIN ||
    1306         642 :             wkbFlatten(poGeom->getGeometryType()) == wkbPolyhedralSurface)
    1307             :         {
    1308           6 :             nShapeType = SHPT_MULTIPATCH;
    1309           6 :             eRequestedGeomType = wkbUnknown;
    1310             :         }
    1311             : 
    1312         647 :         if (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
    1313             :         {
    1314           1 :             const OGRGeometryCollection *poGC = poGeom->toGeometryCollection();
    1315           1 :             bool bIsMultiPatchCompatible = false;
    1316           2 :             for (int iGeom = 0; iGeom < poGC->getNumGeometries(); iGeom++)
    1317             :             {
    1318             :                 OGRwkbGeometryType eSubGeomType =
    1319           1 :                     wkbFlatten(poGC->getGeometryRef(iGeom)->getGeometryType());
    1320           1 :                 if (eSubGeomType == wkbTIN ||
    1321             :                     eSubGeomType == wkbPolyhedralSurface)
    1322             :                 {
    1323           1 :                     bIsMultiPatchCompatible = true;
    1324             :                 }
    1325           0 :                 else if (eSubGeomType != wkbMultiPolygon)
    1326             :                 {
    1327           0 :                     bIsMultiPatchCompatible = false;
    1328           0 :                     break;
    1329             :                 }
    1330             :             }
    1331           1 :             if (bIsMultiPatchCompatible)
    1332             :             {
    1333           1 :                 nShapeType = SHPT_MULTIPATCH;
    1334           1 :                 eRequestedGeomType = wkbUnknown;
    1335             :             }
    1336             :         }
    1337             : 
    1338         647 :         if (nShapeType != -1)
    1339             :         {
    1340         647 :             whileUnsealing(poFeatureDefn)->SetGeomType(eRequestedGeomType);
    1341         647 :             ResetGeomType(nShapeType);
    1342             :         }
    1343             :     }
    1344             : 
    1345             :     const OGRErr eErr =
    1346       63717 :         SHPWriteOGRFeature(hSHP, hDBF, poFeatureDefn, poFeature, osEncoding,
    1347       63717 :                            &bTruncationWarningEmitted, bRewindOnWrite);
    1348             : 
    1349       63717 :     if (hSHP != nullptr)
    1350       63235 :         nTotalShapeCount = hSHP->nRecords;
    1351         482 :     else if (hDBF != nullptr)
    1352         482 :         nTotalShapeCount = hDBF->nRecords;
    1353             : #ifdef DEBUG
    1354             :     else  // Silence coverity.
    1355           0 :         CPLError(CE_Fatal, CPLE_AssertionFailed,
    1356             :                  "Should not happen: Both hSHP and hDBF are nullptrs");
    1357             : #endif
    1358             : 
    1359       63717 :     return eErr;
    1360             : }
    1361             : 
    1362             : /************************************************************************/
    1363             : /*               GetFeatureCountWithSpatialFilterOnly()                 */
    1364             : /*                                                                      */
    1365             : /* Specialized implementation of GetFeatureCount() when there is *only* */
    1366             : /* a spatial filter and no attribute filter.                            */
    1367             : /************************************************************************/
    1368             : 
    1369          35 : int OGRShapeLayer::GetFeatureCountWithSpatialFilterOnly()
    1370             : 
    1371             : {
    1372             :     /* -------------------------------------------------------------------- */
    1373             :     /*      Collect a matching list if we have attribute or spatial         */
    1374             :     /*      indices.  Only do this on the first request for a given pass    */
    1375             :     /*      of course.                                                      */
    1376             :     /* -------------------------------------------------------------------- */
    1377          35 :     if (panMatchingFIDs == nullptr)
    1378             :     {
    1379          35 :         ScanIndices();
    1380             :     }
    1381             : 
    1382          35 :     int nFeatureCount = 0;
    1383          35 :     int iLocalMatchingFID = 0;
    1384          35 :     int iLocalNextShapeId = 0;
    1385          35 :     bool bExpectPoints = false;
    1386             : 
    1387          35 :     if (wkbFlatten(poFeatureDefn->GetGeomType()) == wkbPoint)
    1388          18 :         bExpectPoints = true;
    1389             : 
    1390             :     /* -------------------------------------------------------------------- */
    1391             :     /*      Loop till we find a feature matching our criteria.              */
    1392             :     /* -------------------------------------------------------------------- */
    1393             : 
    1394             :     SHPObject sShape;
    1395          35 :     memset(&sShape, 0, sizeof(sShape));
    1396             : 
    1397             :     while (true)
    1398             :     {
    1399       12524 :         int iShape = -1;
    1400             : 
    1401       12524 :         if (panMatchingFIDs != nullptr)
    1402             :         {
    1403       12416 :             iShape = static_cast<int>(panMatchingFIDs[iLocalMatchingFID]);
    1404       12416 :             if (iShape == OGRNullFID)
    1405          25 :                 break;
    1406       12391 :             iLocalMatchingFID++;
    1407             :         }
    1408             :         else
    1409             :         {
    1410         108 :             if (iLocalNextShapeId >= nTotalShapeCount)
    1411          10 :                 break;
    1412          98 :             iShape = iLocalNextShapeId++;
    1413             : 
    1414          98 :             if (hDBF)
    1415             :             {
    1416          98 :                 if (DBFIsRecordDeleted(hDBF, iShape))
    1417           0 :                     continue;
    1418             : 
    1419         196 :                 if (VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ||
    1420          98 :                     VSIFErrorL(VSI_SHP_GetVSIL(hDBF->fp)))
    1421           0 :                     break;
    1422             :             }
    1423             :         }
    1424             : 
    1425             :         // Read full shape for point layers.
    1426       12489 :         SHPObject *psShape = nullptr;
    1427       12489 :         if (bExpectPoints ||
    1428       12425 :             hSHP->panRecOffset[iShape] == 0 /* lazy shx loading case */)
    1429          64 :             psShape = SHPReadObject(hSHP, iShape);
    1430             : 
    1431             :         /* --------------------------------------------------------------------
    1432             :          */
    1433             :         /*      Only read feature type and bounding box for now. In case of */
    1434             :         /*      inconclusive tests on bounding box only, we will read the full
    1435             :          */
    1436             :         /*      shape later. */
    1437             :         /* --------------------------------------------------------------------
    1438             :          */
    1439       12425 :         else if (iShape >= 0 && iShape < hSHP->nRecords &&
    1440       12425 :                  hSHP->panRecSize[iShape] > 4 + 8 * 4)
    1441             :         {
    1442       12425 :             GByte abyBuf[4 + 8 * 4] = {};
    1443       12425 :             if (hSHP->sHooks.FSeek(hSHP->fpSHP, hSHP->panRecOffset[iShape] + 8,
    1444       24850 :                                    0) == 0 &&
    1445       12425 :                 hSHP->sHooks.FRead(abyBuf, sizeof(abyBuf), 1, hSHP->fpSHP) == 1)
    1446             :             {
    1447       12425 :                 memcpy(&(sShape.nSHPType), abyBuf, 4);
    1448       12425 :                 CPL_LSBPTR32(&(sShape.nSHPType));
    1449       12425 :                 if (sShape.nSHPType != SHPT_NULL &&
    1450       12425 :                     sShape.nSHPType != SHPT_POINT &&
    1451       12425 :                     sShape.nSHPType != SHPT_POINTM &&
    1452       12425 :                     sShape.nSHPType != SHPT_POINTZ)
    1453             :                 {
    1454       12425 :                     psShape = &sShape;
    1455       12425 :                     memcpy(&(sShape.dfXMin), abyBuf + 4, 8);
    1456       12425 :                     memcpy(&(sShape.dfYMin), abyBuf + 12, 8);
    1457       12425 :                     memcpy(&(sShape.dfXMax), abyBuf + 20, 8);
    1458       12425 :                     memcpy(&(sShape.dfYMax), abyBuf + 28, 8);
    1459       12425 :                     CPL_LSBPTR64(&(sShape.dfXMin));
    1460       12425 :                     CPL_LSBPTR64(&(sShape.dfYMin));
    1461       12425 :                     CPL_LSBPTR64(&(sShape.dfXMax));
    1462       12425 :                     CPL_LSBPTR64(&(sShape.dfYMax));
    1463             :                 }
    1464             :             }
    1465             :             else
    1466             :             {
    1467           0 :                 break;
    1468             :             }
    1469             :         }
    1470             : 
    1471       12489 :         if (psShape != nullptr && psShape->nSHPType != SHPT_NULL)
    1472             :         {
    1473       12489 :             OGRGeometry *poGeometry = nullptr;
    1474       12489 :             OGREnvelope sGeomEnv;
    1475             :             // Test if we have a degenerated bounding box.
    1476       12489 :             if (psShape->nSHPType != SHPT_POINT &&
    1477       12425 :                 psShape->nSHPType != SHPT_POINTZ &&
    1478       12425 :                 psShape->nSHPType != SHPT_POINTM &&
    1479       12425 :                 (psShape->dfXMin == psShape->dfXMax ||
    1480       12425 :                  psShape->dfYMin == psShape->dfYMax))
    1481             :             {
    1482             :                 // Need to read the full geometry to compute the envelope.
    1483           0 :                 if (psShape == &sShape)
    1484           0 :                     psShape = SHPReadObject(hSHP, iShape);
    1485             : 
    1486           0 :                 if (psShape)
    1487             :                 {
    1488           0 :                     poGeometry = SHPReadOGRObject(
    1489           0 :                         hSHP, iShape, psShape, m_bHasWarnedWrongWindingOrder);
    1490           0 :                     if (poGeometry)
    1491           0 :                         poGeometry->getEnvelope(&sGeomEnv);
    1492           0 :                     psShape = nullptr;
    1493             :                 }
    1494             :             }
    1495             :             else
    1496             :             {
    1497             :                 // Trust the shape bounding box as the shape envelope.
    1498       12489 :                 sGeomEnv.MinX = psShape->dfXMin;
    1499       12489 :                 sGeomEnv.MinY = psShape->dfYMin;
    1500       12489 :                 sGeomEnv.MaxX = psShape->dfXMax;
    1501       12489 :                 sGeomEnv.MaxY = psShape->dfYMax;
    1502             :             }
    1503             : 
    1504             :             /* --------------------------------------------------------------------
    1505             :              */
    1506             :             /*      If there is no */
    1507             :             /*      intersection between the envelopes we are sure not to have
    1508             :              */
    1509             :             /*      any intersection. */
    1510             :             /* --------------------------------------------------------------------
    1511             :              */
    1512       12489 :             if (sGeomEnv.MaxX < m_sFilterEnvelope.MinX ||
    1513       12463 :                 sGeomEnv.MaxY < m_sFilterEnvelope.MinY ||
    1514       12462 :                 m_sFilterEnvelope.MaxX < sGeomEnv.MinX ||
    1515       12431 :                 m_sFilterEnvelope.MaxY < sGeomEnv.MinY)
    1516             :             {
    1517             :             }
    1518             :             /* --------------------------------------------------------------------
    1519             :              */
    1520             :             /*      If the filter geometry is its own envelope and if the */
    1521             :             /*      envelope of the geometry is inside the filter geometry, */
    1522             :             /*      the geometry itself is inside the filter geometry */
    1523             :             /* --------------------------------------------------------------------
    1524             :              */
    1525       12398 :             else if (m_bFilterIsEnvelope &&
    1526       12398 :                      sGeomEnv.MinX >= m_sFilterEnvelope.MinX &&
    1527       12243 :                      sGeomEnv.MinY >= m_sFilterEnvelope.MinY &&
    1528       12097 :                      sGeomEnv.MaxX <= m_sFilterEnvelope.MaxX &&
    1529       11975 :                      sGeomEnv.MaxY <= m_sFilterEnvelope.MaxY)
    1530             :             {
    1531       11850 :                 nFeatureCount++;
    1532             :             }
    1533             :             else
    1534             :             {
    1535             :                 /* --------------------------------------------------------------------
    1536             :                  */
    1537             :                 /*      Fallback to full intersect test (using GEOS) if we still
    1538             :                  */
    1539             :                 /*      don't know for sure. */
    1540             :                 /* --------------------------------------------------------------------
    1541             :                  */
    1542         548 :                 if (OGRGeometryFactory::haveGEOS())
    1543             :                 {
    1544             :                     // Read the full geometry.
    1545         548 :                     if (poGeometry == nullptr)
    1546             :                     {
    1547         548 :                         if (psShape == &sShape)
    1548         548 :                             psShape = SHPReadObject(hSHP, iShape);
    1549         548 :                         if (psShape)
    1550             :                         {
    1551             :                             poGeometry =
    1552        1096 :                                 SHPReadOGRObject(hSHP, iShape, psShape,
    1553         548 :                                                  m_bHasWarnedWrongWindingOrder);
    1554         548 :                             psShape = nullptr;
    1555             :                         }
    1556             :                     }
    1557         548 :                     if (poGeometry == nullptr)
    1558             :                     {
    1559           0 :                         nFeatureCount++;
    1560             :                     }
    1561         548 :                     else if (m_pPreparedFilterGeom != nullptr)
    1562             :                     {
    1563         548 :                         if (OGRPreparedGeometryIntersects(
    1564             :                                 m_pPreparedFilterGeom,
    1565         548 :                                 OGRGeometry::ToHandle(poGeometry)))
    1566             :                         {
    1567         543 :                             nFeatureCount++;
    1568             :                         }
    1569             :                     }
    1570           0 :                     else if (m_poFilterGeom->Intersects(poGeometry))
    1571           0 :                         nFeatureCount++;
    1572             :                 }
    1573             :                 else
    1574             :                 {
    1575           0 :                     nFeatureCount++;
    1576             :                 }
    1577             :             }
    1578             : 
    1579       12489 :             delete poGeometry;
    1580             :         }
    1581             :         else
    1582             :         {
    1583           0 :             nFeatureCount++;
    1584             :         }
    1585             : 
    1586       12489 :         if (psShape && psShape != &sShape)
    1587          64 :             SHPDestroyObject(psShape);
    1588       12489 :     }
    1589             : 
    1590          35 :     return nFeatureCount;
    1591             : }
    1592             : 
    1593             : /************************************************************************/
    1594             : /*                          GetFeatureCount()                           */
    1595             : /************************************************************************/
    1596             : 
    1597         560 : GIntBig OGRShapeLayer::GetFeatureCount(int bForce)
    1598             : 
    1599             : {
    1600             :     // Check if the spatial filter is non-trivial.
    1601         560 :     bool bHasTrivialSpatialFilter = false;
    1602         560 :     if (m_poFilterGeom != nullptr)
    1603             :     {
    1604          46 :         OGREnvelope oSpatialFilterEnvelope;
    1605          46 :         m_poFilterGeom->getEnvelope(&oSpatialFilterEnvelope);
    1606             : 
    1607          46 :         OGREnvelope oLayerExtent;
    1608          46 :         if (GetExtent(&oLayerExtent, TRUE) == OGRERR_NONE)
    1609             :         {
    1610          46 :             if (oSpatialFilterEnvelope.Contains(oLayerExtent))
    1611             :             {
    1612           0 :                 bHasTrivialSpatialFilter = true;
    1613             :             }
    1614             :             else
    1615             :             {
    1616          46 :                 bHasTrivialSpatialFilter = false;
    1617             :             }
    1618             :         }
    1619             :         else
    1620             :         {
    1621           0 :             bHasTrivialSpatialFilter = false;
    1622             :         }
    1623             :     }
    1624             :     else
    1625             :     {
    1626         514 :         bHasTrivialSpatialFilter = true;
    1627             :     }
    1628             : 
    1629         560 :     if (bHasTrivialSpatialFilter && m_poAttrQuery == nullptr)
    1630         468 :         return nTotalShapeCount;
    1631             : 
    1632          92 :     if (!TouchLayer())
    1633           0 :         return 0;
    1634             : 
    1635             :     // Spatial filter only.
    1636          92 :     if (m_poAttrQuery == nullptr && hSHP != nullptr)
    1637             :     {
    1638          35 :         return GetFeatureCountWithSpatialFilterOnly();
    1639             :     }
    1640             : 
    1641             :     // Attribute filter only.
    1642          57 :     if (m_poAttrQuery != nullptr && m_poFilterGeom == nullptr)
    1643             :     {
    1644             :         // See if we can ignore reading geometries.
    1645             :         const bool bSaveGeometryIgnored =
    1646          46 :             CPL_TO_BOOL(poFeatureDefn->IsGeometryIgnored());
    1647          46 :         if (!AttributeFilterEvaluationNeedsGeometry())
    1648          46 :             poFeatureDefn->SetGeometryIgnored(TRUE);
    1649             : 
    1650          46 :         GIntBig nRet = OGRLayer::GetFeatureCount(bForce);
    1651             : 
    1652          46 :         poFeatureDefn->SetGeometryIgnored(bSaveGeometryIgnored);
    1653          46 :         return nRet;
    1654             :     }
    1655             : 
    1656          11 :     return OGRLayer::GetFeatureCount(bForce);
    1657             : }
    1658             : 
    1659             : /************************************************************************/
    1660             : /*                            IGetExtent()                              */
    1661             : /*                                                                      */
    1662             : /*      Fetch extent of the data currently stored in the dataset.       */
    1663             : /*      The bForce flag has no effect on SHP files since that value     */
    1664             : /*      is always in the header.                                        */
    1665             : /*                                                                      */
    1666             : /*      Returns OGRERR_NONE/OGRRERR_FAILURE.                            */
    1667             : /************************************************************************/
    1668             : 
    1669       12810 : OGRErr OGRShapeLayer::IGetExtent(int iGeomField, OGREnvelope *psExtent,
    1670             :                                  bool bForce)
    1671             : 
    1672             : {
    1673       12810 :     if (!TouchLayer())
    1674           0 :         return OGRERR_FAILURE;
    1675             : 
    1676       12810 :     if (hSHP == nullptr)
    1677           0 :         return OGRERR_FAILURE;
    1678             : 
    1679       12810 :     double adMin[4] = {0.0, 0.0, 0.0, 0.0};
    1680       12810 :     double adMax[4] = {0.0, 0.0, 0.0, 0.0};
    1681             : 
    1682       12810 :     SHPGetInfo(hSHP, nullptr, nullptr, adMin, adMax);
    1683             : 
    1684       12810 :     psExtent->MinX = adMin[0];
    1685       12810 :     psExtent->MinY = adMin[1];
    1686       12810 :     psExtent->MaxX = adMax[0];
    1687       12810 :     psExtent->MaxY = adMax[1];
    1688             : 
    1689       25618 :     if (std::isnan(adMin[0]) || std::isnan(adMin[1]) || std::isnan(adMax[0]) ||
    1690       12808 :         std::isnan(adMax[1]))
    1691             :     {
    1692           2 :         CPLDebug("SHAPE", "Invalid extent in shape header");
    1693             : 
    1694             :         // Disable filters to avoid infinite recursion in GetNextFeature()
    1695             :         // that calls ScanIndices() that call GetExtent.
    1696           2 :         OGRFeatureQuery *poAttrQuery = m_poAttrQuery;
    1697           2 :         m_poAttrQuery = nullptr;
    1698           2 :         OGRGeometry *poFilterGeom = m_poFilterGeom;
    1699           2 :         m_poFilterGeom = nullptr;
    1700             : 
    1701           2 :         psExtent->MinX = 0;
    1702           2 :         psExtent->MinY = 0;
    1703           2 :         psExtent->MaxX = 0;
    1704           2 :         psExtent->MaxY = 0;
    1705             : 
    1706           2 :         const OGRErr eErr = OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
    1707             : 
    1708           2 :         m_poAttrQuery = poAttrQuery;
    1709           2 :         m_poFilterGeom = poFilterGeom;
    1710           2 :         return eErr;
    1711             :     }
    1712             : 
    1713       12808 :     return OGRERR_NONE;
    1714             : }
    1715             : 
    1716           4 : OGRErr OGRShapeLayer::IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent3D,
    1717             :                                    bool bForce)
    1718             : {
    1719           4 :     if (m_poFilterGeom || m_poAttrQuery)
    1720           0 :         return OGRLayer::IGetExtent3D(iGeomField, psExtent3D, bForce);
    1721             : 
    1722           4 :     if (!TouchLayer())
    1723           0 :         return OGRERR_FAILURE;
    1724             : 
    1725           4 :     if (hSHP == nullptr)
    1726           0 :         return OGRERR_FAILURE;
    1727             : 
    1728           4 :     double adMin[4] = {0.0, 0.0, 0.0, 0.0};
    1729           4 :     double adMax[4] = {0.0, 0.0, 0.0, 0.0};
    1730             : 
    1731           4 :     SHPGetInfo(hSHP, nullptr, nullptr, adMin, adMax);
    1732             : 
    1733           4 :     psExtent3D->MinX = adMin[0];
    1734           4 :     psExtent3D->MinY = adMin[1];
    1735           4 :     psExtent3D->MaxX = adMax[0];
    1736           4 :     psExtent3D->MaxY = adMax[1];
    1737             : 
    1738           4 :     if (OGR_GT_HasZ(poFeatureDefn->GetGeomType()))
    1739             :     {
    1740           1 :         psExtent3D->MinZ = adMin[2];
    1741           1 :         psExtent3D->MaxZ = adMax[2];
    1742             :     }
    1743             :     else
    1744             :     {
    1745           3 :         psExtent3D->MinZ = std::numeric_limits<double>::infinity();
    1746           3 :         psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
    1747             :     }
    1748             : 
    1749           8 :     if (std::isnan(adMin[0]) || std::isnan(adMin[1]) || std::isnan(adMax[0]) ||
    1750           4 :         std::isnan(adMax[1]))
    1751             :     {
    1752           0 :         CPLDebug("SHAPE", "Invalid extent in shape header");
    1753             : 
    1754             :         // Disable filters to avoid infinite recursion in GetNextFeature()
    1755             :         // that calls ScanIndices() that call GetExtent.
    1756           0 :         OGRFeatureQuery *poAttrQuery = m_poAttrQuery;
    1757           0 :         m_poAttrQuery = nullptr;
    1758           0 :         OGRGeometry *poFilterGeom = m_poFilterGeom;
    1759           0 :         m_poFilterGeom = nullptr;
    1760             : 
    1761             :         const OGRErr eErr =
    1762           0 :             OGRLayer::IGetExtent3D(iGeomField, psExtent3D, bForce);
    1763             : 
    1764           0 :         m_poAttrQuery = poAttrQuery;
    1765           0 :         m_poFilterGeom = poFilterGeom;
    1766           0 :         return eErr;
    1767             :     }
    1768             : 
    1769           4 :     return OGRERR_NONE;
    1770             : }
    1771             : 
    1772             : /************************************************************************/
    1773             : /*                           TestCapability()                           */
    1774             : /************************************************************************/
    1775             : 
    1776        9889 : int OGRShapeLayer::TestCapability(const char *pszCap)
    1777             : 
    1778             : {
    1779        9889 :     if (!TouchLayer())
    1780           0 :         return FALSE;
    1781             : 
    1782        9889 :     if (EQUAL(pszCap, OLCRandomRead))
    1783           6 :         return TRUE;
    1784             : 
    1785        9883 :     if (EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite))
    1786          28 :         return bUpdateAccess;
    1787             : 
    1788        9855 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1789             :     {
    1790         226 :         if (!(m_poFilterGeom == nullptr || CheckForQIX() || CheckForSBN()))
    1791          14 :             return FALSE;
    1792             : 
    1793         212 :         if (m_poAttrQuery != nullptr)
    1794             :         {
    1795          36 :             InitializeIndexSupport(pszFullName);
    1796          36 :             return m_poAttrQuery->CanUseIndex(this);
    1797             :         }
    1798         176 :         return TRUE;
    1799             :     }
    1800             : 
    1801        9629 :     if (EQUAL(pszCap, OLCDeleteFeature))
    1802           3 :         return bUpdateAccess;
    1803             : 
    1804        9626 :     if (EQUAL(pszCap, OLCFastSpatialFilter))
    1805           6 :         return CheckForQIX() || CheckForSBN();
    1806             : 
    1807        9620 :     if (EQUAL(pszCap, OLCFastGetExtent))
    1808          27 :         return TRUE;
    1809             : 
    1810        9593 :     if (EQUAL(pszCap, OLCFastGetExtent3D))
    1811           2 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
    1812             : 
    1813        9591 :     if (EQUAL(pszCap, OLCFastSetNextByIndex))
    1814          13 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
    1815             : 
    1816        9578 :     if (EQUAL(pszCap, OLCCreateField))
    1817          19 :         return bUpdateAccess;
    1818             : 
    1819        9559 :     if (EQUAL(pszCap, OLCDeleteField))
    1820           6 :         return bUpdateAccess;
    1821             : 
    1822        9553 :     if (EQUAL(pszCap, OLCReorderFields))
    1823           6 :         return bUpdateAccess;
    1824             : 
    1825        9547 :     if (EQUAL(pszCap, OLCAlterFieldDefn) ||
    1826        9541 :         EQUAL(pszCap, OLCAlterGeomFieldDefn))
    1827           7 :         return bUpdateAccess;
    1828             : 
    1829        9540 :     if (EQUAL(pszCap, OLCRename))
    1830           8 :         return bUpdateAccess;
    1831             : 
    1832        9532 :     if (EQUAL(pszCap, OLCIgnoreFields))
    1833         834 :         return TRUE;
    1834             : 
    1835        8698 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
    1836             :     {
    1837             :         // No encoding defined: we don't know.
    1838        5025 :         if (osEncoding.empty())
    1839         390 :             return FALSE;
    1840             : 
    1841        4635 :         if (hDBF == nullptr || DBFGetFieldCount(hDBF) == 0)
    1842        1609 :             return TRUE;
    1843             : 
    1844             :         // Otherwise test that we can re-encode field names to UTF-8.
    1845        3026 :         const int nFieldCount = DBFGetFieldCount(hDBF);
    1846        8530 :         for (int i = 0; i < nFieldCount; i++)
    1847             :         {
    1848        5505 :             char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
    1849        5505 :             int nWidth = 0;
    1850        5505 :             int nPrecision = 0;
    1851             : 
    1852        5505 :             DBFGetFieldInfo(hDBF, i, szFieldName, &nWidth, &nPrecision);
    1853             : 
    1854        5505 :             if (!CPLCanRecode(szFieldName, osEncoding, CPL_ENC_UTF8))
    1855             :             {
    1856           1 :                 return FALSE;
    1857             :             }
    1858             :         }
    1859             : 
    1860        3025 :         return TRUE;
    1861             :     }
    1862             : 
    1863        3673 :     if (EQUAL(pszCap, OLCMeasuredGeometries))
    1864        1629 :         return TRUE;
    1865             : 
    1866        2044 :     if (EQUAL(pszCap, OLCZGeometries))
    1867          18 :         return TRUE;
    1868             : 
    1869        2026 :     return FALSE;
    1870             : }
    1871             : 
    1872             : /************************************************************************/
    1873             : /*                            CreateField()                             */
    1874             : /************************************************************************/
    1875             : 
    1876        4933 : OGRErr OGRShapeLayer::CreateField(const OGRFieldDefn *poFieldDefn,
    1877             :                                   int bApproxOK)
    1878             : 
    1879             : {
    1880        4933 :     if (!StartUpdate("CreateField"))
    1881           1 :         return OGRERR_FAILURE;
    1882             : 
    1883        4932 :     CPLAssert(nullptr != poFieldDefn);
    1884             : 
    1885        4932 :     bool bDBFJustCreated = false;
    1886        4932 :     if (hDBF == nullptr)
    1887             :     {
    1888           1 :         const CPLString osFilename = CPLResetExtensionSafe(pszFullName, "dbf");
    1889           1 :         hDBF = DBFCreate(osFilename);
    1890             : 
    1891           1 :         if (hDBF == nullptr)
    1892             :         {
    1893           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1894             :                      "Failed to create DBF file `%s'.", osFilename.c_str());
    1895           0 :             return OGRERR_FAILURE;
    1896             :         }
    1897             : 
    1898           1 :         bDBFJustCreated = true;
    1899             :     }
    1900             : 
    1901        4932 :     if (hDBF->nHeaderLength + XBASE_FLDHDR_SZ > 65535)
    1902             :     {
    1903           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1904             :                  "Cannot add field %s. Header length limit reached "
    1905             :                  "(max 65535 bytes, 2046 fields).",
    1906             :                  poFieldDefn->GetNameRef());
    1907           1 :         return OGRERR_FAILURE;
    1908             :     }
    1909             : 
    1910        4931 :     CPLErrorReset();
    1911             : 
    1912        4931 :     if (poFeatureDefn->GetFieldCount() == 255)
    1913             :     {
    1914           2 :         CPLError(CE_Warning, CPLE_AppDefined,
    1915             :                  "Creating a 256th field, "
    1916             :                  "but some DBF readers might only support 255 fields");
    1917             :     }
    1918             : 
    1919             :     /* -------------------------------------------------------------------- */
    1920             :     /*      Normalize field name                                            */
    1921             :     /* -------------------------------------------------------------------- */
    1922        9862 :     CPLString osFieldName;
    1923        4931 :     if (!osEncoding.empty())
    1924             :     {
    1925        4930 :         CPLClearRecodeWarningFlags();
    1926        4930 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1927        4930 :         CPLErr eLastErr = CPLGetLastErrorType();
    1928             :         char *const pszRecoded =
    1929        4930 :             CPLRecode(poFieldDefn->GetNameRef(), CPL_ENC_UTF8, osEncoding);
    1930        4930 :         CPLPopErrorHandler();
    1931        4930 :         osFieldName = pszRecoded;
    1932        4930 :         CPLFree(pszRecoded);
    1933        4930 :         if (CPLGetLastErrorType() != eLastErr)
    1934             :         {
    1935           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    1936             :                      "Failed to create field name '%s': cannot convert to %s",
    1937             :                      poFieldDefn->GetNameRef(), osEncoding.c_str());
    1938           1 :             return OGRERR_FAILURE;
    1939             :         }
    1940             :     }
    1941             :     else
    1942             :     {
    1943           1 :         osFieldName = poFieldDefn->GetNameRef();
    1944             :     }
    1945             : 
    1946        4930 :     const int nNameSize = static_cast<int>(osFieldName.size());
    1947             :     char szNewFieldName[XBASE_FLDNAME_LEN_WRITE + 1];
    1948        9860 :     CPLString osRadixFieldName;
    1949        9860 :     CPLString osRadixFieldNameUC;
    1950             :     {
    1951        4930 :         char *pszTmp = CPLScanString(
    1952        4930 :             osFieldName, std::min(nNameSize, XBASE_FLDNAME_LEN_WRITE), TRUE,
    1953             :             TRUE);
    1954        4930 :         strncpy(szNewFieldName, pszTmp, sizeof(szNewFieldName) - 1);
    1955        4930 :         szNewFieldName[sizeof(szNewFieldName) - 1] = '\0';
    1956        4930 :         osRadixFieldName = pszTmp;
    1957        4930 :         osRadixFieldNameUC = CPLString(osRadixFieldName).toupper();
    1958        4930 :         CPLFree(pszTmp);
    1959             :     }
    1960             : 
    1961        9860 :     CPLString osNewFieldNameUC(szNewFieldName);
    1962        4930 :     osNewFieldNameUC.toupper();
    1963             : 
    1964        4930 :     if (m_oSetUCFieldName.empty())
    1965             :     {
    1966        1350 :         for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    1967             :         {
    1968          18 :             CPLString key(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
    1969           9 :             key.toupper();
    1970           9 :             m_oSetUCFieldName.insert(key);
    1971             :         }
    1972             :     }
    1973             : 
    1974             :     bool bFoundFieldName =
    1975        4930 :         m_oSetUCFieldName.find(osNewFieldNameUC) != m_oSetUCFieldName.end();
    1976             : 
    1977        4930 :     if (!bApproxOK && (bFoundFieldName || !EQUAL(osFieldName, szNewFieldName)))
    1978             :     {
    1979           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1980             :                  "Failed to add field named '%s'", poFieldDefn->GetNameRef());
    1981             : 
    1982           0 :         return OGRERR_FAILURE;
    1983             :     }
    1984             : 
    1985        4930 :     if (bFoundFieldName)
    1986             :     {
    1987          36 :         int nRenameNum = 1;
    1988         213 :         while (bFoundFieldName && nRenameNum < 10)
    1989             :         {
    1990         177 :             CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.8s_%.1d",
    1991             :                         osRadixFieldName.c_str(), nRenameNum);
    1992             :             osNewFieldNameUC.Printf("%.8s_%.1d", osRadixFieldNameUC.c_str(),
    1993         177 :                                     nRenameNum);
    1994         177 :             bFoundFieldName = m_oSetUCFieldName.find(osNewFieldNameUC) !=
    1995         177 :                               m_oSetUCFieldName.end();
    1996         177 :             nRenameNum++;
    1997             :         }
    1998             : 
    1999          41 :         while (bFoundFieldName && nRenameNum < 100)
    2000             :         {
    2001           5 :             CPLsnprintf(szNewFieldName, sizeof(szNewFieldName), "%.8s%.2d",
    2002             :                         osRadixFieldName.c_str(), nRenameNum);
    2003             :             osNewFieldNameUC.Printf("%.8s%.2d", osRadixFieldNameUC.c_str(),
    2004           5 :                                     nRenameNum);
    2005           5 :             bFoundFieldName = m_oSetUCFieldName.find(osNewFieldNameUC) !=
    2006           5 :                               m_oSetUCFieldName.end();
    2007           5 :             nRenameNum++;
    2008             :         }
    2009             : 
    2010          36 :         if (bFoundFieldName)
    2011             :         {
    2012             :             // One hundred similar field names!!?
    2013           0 :             CPLError(
    2014             :                 CE_Failure, CPLE_NotSupported,
    2015             :                 "Too many field names like '%s' when truncated to %d letters "
    2016             :                 "for Shapefile format.",
    2017             :                 poFieldDefn->GetNameRef(), XBASE_FLDNAME_LEN_WRITE);
    2018           0 :             return OGRERR_FAILURE;
    2019             :         }
    2020             :     }
    2021             : 
    2022        9860 :     OGRFieldDefn oModFieldDefn(poFieldDefn);
    2023             : 
    2024        4930 :     if (!EQUAL(osFieldName, szNewFieldName))
    2025             :     {
    2026          59 :         CPLError(CE_Warning, CPLE_NotSupported,
    2027             :                  "Normalized/laundered field name: '%s' to '%s'",
    2028             :                  poFieldDefn->GetNameRef(), szNewFieldName);
    2029             : 
    2030             :         // Set field name with normalized value.
    2031          59 :         oModFieldDefn.SetName(szNewFieldName);
    2032             :     }
    2033             : 
    2034             :     /* -------------------------------------------------------------------- */
    2035             :     /*      Add field to layer                                              */
    2036             :     /* -------------------------------------------------------------------- */
    2037        4930 :     char chType = 'C';
    2038        4930 :     int nWidth = 0;
    2039        4930 :     int nDecimals = 0;
    2040             : 
    2041        4930 :     switch (oModFieldDefn.GetType())
    2042             :     {
    2043        2188 :         case OFTInteger:
    2044        2188 :             if (oModFieldDefn.GetSubType() == OFSTBoolean)
    2045             :             {
    2046           1 :                 chType = 'L';
    2047           1 :                 nWidth = 1;
    2048             :             }
    2049             :             else
    2050             :             {
    2051        2187 :                 chType = 'N';
    2052        2187 :                 nWidth = oModFieldDefn.GetWidth();
    2053        2187 :                 if (nWidth == 0)
    2054        2161 :                     nWidth = 9;
    2055             :             }
    2056        2188 :             break;
    2057             : 
    2058         120 :         case OFTInteger64:
    2059         120 :             chType = 'N';
    2060         120 :             nWidth = oModFieldDefn.GetWidth();
    2061         120 :             if (nWidth == 0)
    2062          21 :                 nWidth = 18;
    2063         120 :             break;
    2064             : 
    2065         194 :         case OFTReal:
    2066         194 :             chType = 'N';
    2067         194 :             nWidth = oModFieldDefn.GetWidth();
    2068         194 :             nDecimals = oModFieldDefn.GetPrecision();
    2069         194 :             if (nWidth == 0)
    2070             :             {
    2071         103 :                 nWidth = 24;
    2072         103 :                 nDecimals = 15;
    2073             :             }
    2074         194 :             break;
    2075             : 
    2076        2392 :         case OFTString:
    2077        2392 :             chType = 'C';
    2078        2392 :             nWidth = oModFieldDefn.GetWidth();
    2079        2392 :             if (nWidth == 0)
    2080        2245 :                 nWidth = 80;
    2081         147 :             else if (nWidth > OGR_DBF_MAX_FIELD_WIDTH)
    2082             :             {
    2083           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2084             :                          "Field %s of width %d truncated to %d.",
    2085             :                          szNewFieldName, nWidth, OGR_DBF_MAX_FIELD_WIDTH);
    2086           1 :                 nWidth = OGR_DBF_MAX_FIELD_WIDTH;
    2087             :             }
    2088        2392 :             break;
    2089             : 
    2090          20 :         case OFTDate:
    2091          20 :             chType = 'D';
    2092          20 :             nWidth = 8;
    2093          20 :             break;
    2094             : 
    2095          16 :         case OFTDateTime:
    2096          16 :             CPLError(
    2097             :                 CE_Warning, CPLE_NotSupported,
    2098             :                 "Field %s created as String field, though DateTime requested.",
    2099             :                 szNewFieldName);
    2100          16 :             chType = 'C';
    2101          16 :             nWidth = static_cast<int>(strlen("YYYY-MM-DDTHH:MM:SS.sss+HH:MM"));
    2102          16 :             oModFieldDefn.SetType(OFTString);
    2103          16 :             break;
    2104             : 
    2105           0 :         default:
    2106           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2107             :                      "Can't create fields of type %s on shapefile layers.",
    2108             :                      OGRFieldDefn::GetFieldTypeName(oModFieldDefn.GetType()));
    2109             : 
    2110           0 :             return OGRERR_FAILURE;
    2111             :             break;
    2112             :     }
    2113             : 
    2114        4930 :     oModFieldDefn.SetWidth(nWidth);
    2115        4930 :     oModFieldDefn.SetPrecision(nDecimals);
    2116             : 
    2117             :     // Suppress the dummy FID field if we have created it just before.
    2118        4930 :     if (DBFGetFieldCount(hDBF) == 1 && poFeatureDefn->GetFieldCount() == 0)
    2119             :     {
    2120           3 :         DBFDeleteField(hDBF, 0);
    2121             :     }
    2122             : 
    2123             :     const int iNewField =
    2124        4930 :         DBFAddNativeFieldType(hDBF, szNewFieldName, chType, nWidth, nDecimals);
    2125             : 
    2126        4930 :     if (iNewField != -1)
    2127             :     {
    2128        4929 :         m_oSetUCFieldName.insert(osNewFieldNameUC);
    2129             : 
    2130        4929 :         whileUnsealing(poFeatureDefn)->AddFieldDefn(&oModFieldDefn);
    2131             : 
    2132        4929 :         if (bDBFJustCreated)
    2133             :         {
    2134           2 :             for (int i = 0; i < nTotalShapeCount; i++)
    2135             :             {
    2136           1 :                 DBFWriteNULLAttribute(hDBF, i, 0);
    2137             :             }
    2138             :         }
    2139             : 
    2140        4929 :         return OGRERR_NONE;
    2141             :     }
    2142             : 
    2143           1 :     CPLError(CE_Failure, CPLE_AppDefined,
    2144             :              "Can't create field %s in Shape DBF file, reason unknown.",
    2145             :              szNewFieldName);
    2146             : 
    2147           1 :     return OGRERR_FAILURE;
    2148             : }
    2149             : 
    2150             : /************************************************************************/
    2151             : /*                            DeleteField()                             */
    2152             : /************************************************************************/
    2153             : 
    2154          12 : OGRErr OGRShapeLayer::DeleteField(int iField)
    2155             : {
    2156          12 :     if (!StartUpdate("DeleteField"))
    2157           1 :         return OGRERR_FAILURE;
    2158             : 
    2159          11 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2160             :     {
    2161           3 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2162           3 :         return OGRERR_FAILURE;
    2163             :     }
    2164             : 
    2165           8 :     m_oSetUCFieldName.clear();
    2166             : 
    2167           8 :     if (DBFDeleteField(hDBF, iField))
    2168             :     {
    2169           8 :         TruncateDBF();
    2170             : 
    2171           8 :         return whileUnsealing(poFeatureDefn)->DeleteFieldDefn(iField);
    2172             :     }
    2173             : 
    2174           0 :     return OGRERR_FAILURE;
    2175             : }
    2176             : 
    2177             : /************************************************************************/
    2178             : /*                           ReorderFields()                            */
    2179             : /************************************************************************/
    2180             : 
    2181          25 : OGRErr OGRShapeLayer::ReorderFields(int *panMap)
    2182             : {
    2183          25 :     if (!StartUpdate("ReorderFields"))
    2184           1 :         return OGRERR_FAILURE;
    2185             : 
    2186          24 :     if (poFeatureDefn->GetFieldCount() == 0)
    2187           5 :         return OGRERR_NONE;
    2188             : 
    2189          19 :     OGRErr eErr = OGRCheckPermutation(panMap, poFeatureDefn->GetFieldCount());
    2190          19 :     if (eErr != OGRERR_NONE)
    2191           2 :         return eErr;
    2192             : 
    2193          17 :     if (DBFReorderFields(hDBF, panMap))
    2194             :     {
    2195          17 :         return whileUnsealing(poFeatureDefn)->ReorderFieldDefns(panMap);
    2196             :     }
    2197             : 
    2198           0 :     return OGRERR_FAILURE;
    2199             : }
    2200             : 
    2201             : /************************************************************************/
    2202             : /*                           AlterFieldDefn()                           */
    2203             : /************************************************************************/
    2204             : 
    2205          21 : OGRErr OGRShapeLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
    2206             :                                      int nFlagsIn)
    2207             : {
    2208          21 :     if (!StartUpdate("AlterFieldDefn"))
    2209           1 :         return OGRERR_FAILURE;
    2210             : 
    2211          20 :     if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
    2212             :     {
    2213           3 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2214           3 :         return OGRERR_FAILURE;
    2215             :     }
    2216             : 
    2217          17 :     m_oSetUCFieldName.clear();
    2218             : 
    2219          17 :     OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    2220          17 :     OGRFieldType eType = poFieldDefn->GetType();
    2221             : 
    2222          34 :     auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    2223             : 
    2224             :     // On reading we support up to 11 characters
    2225          17 :     char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
    2226          17 :     int nWidth = 0;
    2227          17 :     int nPrecision = 0;
    2228          17 :     DBFGetFieldInfo(hDBF, iField, szFieldName, &nWidth, &nPrecision);
    2229          17 :     char chNativeType = DBFGetNativeFieldType(hDBF, iField);
    2230             : 
    2231          32 :     if ((nFlagsIn & ALTER_TYPE_FLAG) &&
    2232          15 :         poNewFieldDefn->GetType() != poFieldDefn->GetType())
    2233             :     {
    2234           5 :         if (poNewFieldDefn->GetType() == OFTInteger64 &&
    2235           1 :             poFieldDefn->GetType() == OFTInteger)
    2236             :         {
    2237           1 :             eType = poNewFieldDefn->GetType();
    2238             :         }
    2239           3 :         else if (poNewFieldDefn->GetType() != OFTString)
    2240             :         {
    2241           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2242             :                      "Can only convert to OFTString");
    2243           1 :             return OGRERR_FAILURE;
    2244             :         }
    2245             :         else
    2246             :         {
    2247           2 :             chNativeType = 'C';
    2248           2 :             eType = poNewFieldDefn->GetType();
    2249             :         }
    2250             :     }
    2251             : 
    2252          16 :     if (nFlagsIn & ALTER_NAME_FLAG)
    2253             :     {
    2254          15 :         CPLString osFieldName;
    2255          15 :         if (!osEncoding.empty())
    2256             :         {
    2257          15 :             CPLClearRecodeWarningFlags();
    2258          15 :             CPLErrorReset();
    2259          15 :             CPLPushErrorHandler(CPLQuietErrorHandler);
    2260          15 :             char *pszRecoded = CPLRecode(poNewFieldDefn->GetNameRef(),
    2261             :                                          CPL_ENC_UTF8, osEncoding);
    2262          15 :             CPLPopErrorHandler();
    2263          15 :             osFieldName = pszRecoded;
    2264          15 :             CPLFree(pszRecoded);
    2265          15 :             if (CPLGetLastErrorType() != 0)
    2266             :             {
    2267           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2268             :                          "Failed to rename field name to '%s': "
    2269             :                          "cannot convert to %s",
    2270             :                          poNewFieldDefn->GetNameRef(), osEncoding.c_str());
    2271           1 :                 return OGRERR_FAILURE;
    2272             :             }
    2273             :         }
    2274             :         else
    2275             :         {
    2276           0 :             osFieldName = poNewFieldDefn->GetNameRef();
    2277             :         }
    2278             : 
    2279          14 :         strncpy(szFieldName, osFieldName, sizeof(szFieldName) - 1);
    2280          14 :         szFieldName[sizeof(szFieldName) - 1] = '\0';
    2281             :     }
    2282          15 :     if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
    2283             :     {
    2284          13 :         nWidth = poNewFieldDefn->GetWidth();
    2285          13 :         nPrecision = poNewFieldDefn->GetPrecision();
    2286             :     }
    2287             : 
    2288          15 :     if (DBFAlterFieldDefn(hDBF, iField, szFieldName, chNativeType, nWidth,
    2289          15 :                           nPrecision))
    2290             :     {
    2291          15 :         if (nFlagsIn & ALTER_TYPE_FLAG)
    2292          14 :             poFieldDefn->SetType(eType);
    2293          15 :         if (nFlagsIn & ALTER_NAME_FLAG)
    2294          14 :             poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
    2295          15 :         if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
    2296             :         {
    2297          13 :             poFieldDefn->SetWidth(nWidth);
    2298          13 :             poFieldDefn->SetPrecision(nPrecision);
    2299             : 
    2300          13 :             TruncateDBF();
    2301             :         }
    2302          15 :         return OGRERR_NONE;
    2303             :     }
    2304             : 
    2305           0 :     return OGRERR_FAILURE;
    2306             : }
    2307             : 
    2308             : /************************************************************************/
    2309             : /*                         AlterGeomFieldDefn()                         */
    2310             : /************************************************************************/
    2311             : 
    2312           6 : OGRErr OGRShapeLayer::AlterGeomFieldDefn(
    2313             :     int iGeomField, const OGRGeomFieldDefn *poNewGeomFieldDefn, int nFlagsIn)
    2314             : {
    2315           6 :     if (!StartUpdate("AlterGeomFieldDefn"))
    2316           0 :         return OGRERR_FAILURE;
    2317             : 
    2318           6 :     if (iGeomField < 0 || iGeomField >= poFeatureDefn->GetGeomFieldCount())
    2319             :     {
    2320           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    2321           1 :         return OGRERR_FAILURE;
    2322             :     }
    2323             : 
    2324           5 :     auto poFieldDefn = cpl::down_cast<OGRShapeGeomFieldDefn *>(
    2325           5 :         poFeatureDefn->GetGeomFieldDefn(iGeomField));
    2326          10 :     auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    2327             : 
    2328           5 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
    2329             :     {
    2330           5 :         if (strcmp(poNewGeomFieldDefn->GetNameRef(),
    2331           5 :                    poFieldDefn->GetNameRef()) != 0)
    2332             :         {
    2333           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2334             :                      "Altering the geometry field name is not supported for "
    2335             :                      "shapefiles");
    2336           0 :             return OGRERR_FAILURE;
    2337             :         }
    2338             :     }
    2339             : 
    2340           5 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
    2341             :     {
    2342           5 :         if (poFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    2343             :         {
    2344           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2345             :                      "Altering the geometry field type is not supported for "
    2346             :                      "shapefiles");
    2347           1 :             return OGRERR_FAILURE;
    2348             :         }
    2349             :     }
    2350             : 
    2351           4 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG)
    2352             :     {
    2353           4 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    2354           4 :         if (poNewSRSRef && poNewSRSRef->GetCoordinateEpoch() > 0)
    2355             :         {
    2356           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    2357             :                      "Setting a coordinate epoch is not supported for "
    2358             :                      "shapefiles");
    2359           1 :             return OGRERR_FAILURE;
    2360             :         }
    2361             :     }
    2362             : 
    2363           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG)
    2364             :     {
    2365           3 :         if (poFieldDefn->GetPrjFilename().empty())
    2366             :         {
    2367           6 :             poFieldDefn->SetPrjFilename(
    2368           4 :                 CPLResetExtensionSafe(pszFullName, "prj").c_str());
    2369             :         }
    2370             : 
    2371           3 :         const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
    2372           3 :         if (poNewSRSRef)
    2373             :         {
    2374           2 :             char *pszWKT = nullptr;
    2375           2 :             VSILFILE *fp = nullptr;
    2376           2 :             const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
    2377           4 :             if (poNewSRSRef->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE &&
    2378           2 :                 (fp = VSIFOpenL(poFieldDefn->GetPrjFilename().c_str(), "wt")) !=
    2379             :                     nullptr)
    2380             :             {
    2381           2 :                 VSIFWriteL(pszWKT, strlen(pszWKT), 1, fp);
    2382           2 :                 VSIFCloseL(fp);
    2383             :             }
    2384             :             else
    2385             :             {
    2386           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot write %s",
    2387           0 :                          poFieldDefn->GetPrjFilename().c_str());
    2388           0 :                 CPLFree(pszWKT);
    2389           0 :                 return OGRERR_FAILURE;
    2390             :             }
    2391             : 
    2392           2 :             CPLFree(pszWKT);
    2393             : 
    2394           2 :             auto poNewSRS = poNewSRSRef->Clone();
    2395           2 :             poFieldDefn->SetSpatialRef(poNewSRS);
    2396           2 :             poNewSRS->Release();
    2397             :         }
    2398             :         else
    2399             :         {
    2400           1 :             poFieldDefn->SetSpatialRef(nullptr);
    2401             :             VSIStatBufL sStat;
    2402           2 :             if (VSIStatL(poFieldDefn->GetPrjFilename().c_str(), &sStat) == 0 &&
    2403           1 :                 VSIUnlink(poFieldDefn->GetPrjFilename().c_str()) != 0)
    2404             :             {
    2405           0 :                 CPLError(CE_Failure, CPLE_FileIO, "Cannot delete %s",
    2406           0 :                          poFieldDefn->GetPrjFilename().c_str());
    2407           0 :                 return OGRERR_FAILURE;
    2408             :             }
    2409             :         }
    2410           3 :         poFieldDefn->SetSRSSet();
    2411             :     }
    2412             : 
    2413           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG)
    2414           3 :         poFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
    2415           3 :     if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
    2416           3 :         poFieldDefn->SetNullable(poNewGeomFieldDefn->IsNullable());
    2417             : 
    2418           3 :     return OGRERR_NONE;
    2419             : }
    2420             : 
    2421             : /************************************************************************/
    2422             : /*                           GetSpatialRef()                            */
    2423             : /************************************************************************/
    2424             : 
    2425       92422 : const OGRSpatialReference *OGRShapeGeomFieldDefn::GetSpatialRef() const
    2426             : 
    2427             : {
    2428       92422 :     if (bSRSSet)
    2429       90879 :         return poSRS;
    2430             : 
    2431        1543 :     bSRSSet = true;
    2432             : 
    2433             :     /* -------------------------------------------------------------------- */
    2434             :     /*      Is there an associated .prj file we can read?                   */
    2435             :     /* -------------------------------------------------------------------- */
    2436        1543 :     std::string l_osPrjFile = CPLResetExtensionSafe(pszFullName, "prj");
    2437             : 
    2438        1543 :     char *apszOptions[] = {
    2439             :         const_cast<char *>("EMIT_ERROR_IF_CANNOT_OPEN_FILE=FALSE"), nullptr};
    2440        1543 :     char **papszLines = CSLLoad2(l_osPrjFile.c_str(), -1, -1, apszOptions);
    2441        1543 :     if (papszLines == nullptr)
    2442             :     {
    2443        1248 :         l_osPrjFile = CPLResetExtensionSafe(pszFullName, "PRJ");
    2444        1248 :         papszLines = CSLLoad2(l_osPrjFile.c_str(), -1, -1, apszOptions);
    2445             :     }
    2446             : 
    2447        1543 :     if (papszLines != nullptr)
    2448             :     {
    2449         737 :         osPrjFile = std::move(l_osPrjFile);
    2450             : 
    2451         737 :         auto poSRSNonConst = new OGRSpatialReference();
    2452         737 :         poSRSNonConst->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2453             :         // Remove UTF-8 BOM if found
    2454             :         // http://lists.osgeo.org/pipermail/gdal-dev/2014-July/039527.html
    2455         737 :         if (static_cast<unsigned char>(papszLines[0][0]) == 0xEF &&
    2456           1 :             static_cast<unsigned char>(papszLines[0][1]) == 0xBB &&
    2457           1 :             static_cast<unsigned char>(papszLines[0][2]) == 0xBF)
    2458             :         {
    2459           1 :             memmove(papszLines[0], papszLines[0] + 3,
    2460           1 :                     strlen(papszLines[0] + 3) + 1);
    2461             :         }
    2462         737 :         if (STARTS_WITH_CI(papszLines[0], "GEOGCS["))
    2463             :         {
    2464             :             // Strip AXIS[] in GEOGCS to address use case of
    2465             :             // https://github.com/OSGeo/gdal/issues/8452
    2466         512 :             std::string osVal;
    2467         523 :             for (CSLConstList papszIter = papszLines; *papszIter; ++papszIter)
    2468         267 :                 osVal += *papszIter;
    2469         512 :             OGR_SRSNode oSRSNode;
    2470         256 :             const char *pszVal = osVal.c_str();
    2471         256 :             if (oSRSNode.importFromWkt(&pszVal) == OGRERR_NONE)
    2472             :             {
    2473         256 :                 oSRSNode.StripNodes("AXIS");
    2474         256 :                 char *pszWKT = nullptr;
    2475         256 :                 oSRSNode.exportToWkt(&pszWKT);
    2476         256 :                 if (pszWKT)
    2477             :                 {
    2478         256 :                     CSLDestroy(papszLines);
    2479             :                     papszLines =
    2480         256 :                         static_cast<char **>(CPLCalloc(2, sizeof(char *)));
    2481         256 :                     papszLines[0] = pszWKT;
    2482             :                 }
    2483             :             }
    2484             :         }
    2485         737 :         if (poSRSNonConst->importFromESRI(papszLines) != OGRERR_NONE)
    2486             :         {
    2487           1 :             delete poSRSNonConst;
    2488           1 :             poSRSNonConst = nullptr;
    2489             :         }
    2490         737 :         CSLDestroy(papszLines);
    2491             : 
    2492         737 :         if (poSRSNonConst)
    2493             :         {
    2494         736 :             if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
    2495             :             {
    2496         736 :                 auto poSRSMatch = poSRSNonConst->FindBestMatch();
    2497         736 :                 if (poSRSMatch)
    2498             :                 {
    2499         735 :                     poSRSNonConst->Release();
    2500         735 :                     poSRSNonConst = poSRSMatch;
    2501         735 :                     poSRSNonConst->SetAxisMappingStrategy(
    2502             :                         OAMS_TRADITIONAL_GIS_ORDER);
    2503             :                 }
    2504             :             }
    2505             :             else
    2506             :             {
    2507           0 :                 poSRSNonConst->AutoIdentifyEPSG();
    2508             :             }
    2509         736 :             poSRS = poSRSNonConst;
    2510             :         }
    2511             :     }
    2512             : 
    2513        1543 :     return poSRS;
    2514             : }
    2515             : 
    2516             : /************************************************************************/
    2517             : /*                           ResetGeomType()                            */
    2518             : /*                                                                      */
    2519             : /*      Modify the geometry type for this file.  Used to convert to     */
    2520             : /*      a different geometry type when a layer was created with a       */
    2521             : /*      type of unknown, and we get to the first feature to             */
    2522             : /*      establish the type.                                             */
    2523             : /************************************************************************/
    2524             : 
    2525         647 : int OGRShapeLayer::ResetGeomType(int nNewGeomType)
    2526             : 
    2527             : {
    2528         647 :     if (nTotalShapeCount > 0)
    2529           0 :         return FALSE;
    2530             : 
    2531         647 :     if (hSHP->fpSHX == nullptr)
    2532             :     {
    2533           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2534             :                  "OGRShapeLayer::ResetGeomType failed: SHX file is closed");
    2535           0 :         return FALSE;
    2536             :     }
    2537             : 
    2538             :     /* -------------------------------------------------------------------- */
    2539             :     /*      Update .shp header.                                             */
    2540             :     /* -------------------------------------------------------------------- */
    2541         647 :     int nStartPos = static_cast<int>(hSHP->sHooks.FTell(hSHP->fpSHP));
    2542             : 
    2543         647 :     char abyHeader[100] = {};
    2544        1294 :     if (hSHP->sHooks.FSeek(hSHP->fpSHP, 0, SEEK_SET) != 0 ||
    2545         647 :         hSHP->sHooks.FRead(abyHeader, 100, 1, hSHP->fpSHP) != 1)
    2546           0 :         return FALSE;
    2547             : 
    2548         647 :     *(reinterpret_cast<GInt32 *>(abyHeader + 32)) = CPL_LSBWORD32(nNewGeomType);
    2549             : 
    2550        1294 :     if (hSHP->sHooks.FSeek(hSHP->fpSHP, 0, SEEK_SET) != 0 ||
    2551         647 :         hSHP->sHooks.FWrite(abyHeader, 100, 1, hSHP->fpSHP) != 1)
    2552           0 :         return FALSE;
    2553             : 
    2554         647 :     if (hSHP->sHooks.FSeek(hSHP->fpSHP, nStartPos, SEEK_SET) != 0)
    2555           0 :         return FALSE;
    2556             : 
    2557             :     /* -------------------------------------------------------------------- */
    2558             :     /*      Update .shx header.                                             */
    2559             :     /* -------------------------------------------------------------------- */
    2560         647 :     nStartPos = static_cast<int>(hSHP->sHooks.FTell(hSHP->fpSHX));
    2561             : 
    2562        1294 :     if (hSHP->sHooks.FSeek(hSHP->fpSHX, 0, SEEK_SET) != 0 ||
    2563         647 :         hSHP->sHooks.FRead(abyHeader, 100, 1, hSHP->fpSHX) != 1)
    2564           0 :         return FALSE;
    2565             : 
    2566         647 :     *(reinterpret_cast<GInt32 *>(abyHeader + 32)) = CPL_LSBWORD32(nNewGeomType);
    2567             : 
    2568        1294 :     if (hSHP->sHooks.FSeek(hSHP->fpSHX, 0, SEEK_SET) != 0 ||
    2569         647 :         hSHP->sHooks.FWrite(abyHeader, 100, 1, hSHP->fpSHX) != 1)
    2570           0 :         return FALSE;
    2571             : 
    2572         647 :     if (hSHP->sHooks.FSeek(hSHP->fpSHX, nStartPos, SEEK_SET) != 0)
    2573           0 :         return FALSE;
    2574             : 
    2575             :     /* -------------------------------------------------------------------- */
    2576             :     /*      Update other information.                                       */
    2577             :     /* -------------------------------------------------------------------- */
    2578         647 :     hSHP->nShapeType = nNewGeomType;
    2579             : 
    2580         647 :     return TRUE;
    2581             : }
    2582             : 
    2583             : /************************************************************************/
    2584             : /*                             SyncToDisk()                             */
    2585             : /************************************************************************/
    2586             : 
    2587         325 : OGRErr OGRShapeLayer::SyncToDisk()
    2588             : 
    2589             : {
    2590         325 :     if (!TouchLayer())
    2591           0 :         return OGRERR_FAILURE;
    2592             : 
    2593         325 :     if (bHeaderDirty)
    2594             :     {
    2595         294 :         if (hSHP != nullptr)
    2596         198 :             SHPWriteHeader(hSHP);
    2597             : 
    2598         294 :         if (hDBF != nullptr)
    2599         294 :             DBFUpdateHeader(hDBF);
    2600             : 
    2601         294 :         bHeaderDirty = false;
    2602             :     }
    2603             : 
    2604         325 :     if (hSHP != nullptr)
    2605             :     {
    2606         228 :         hSHP->sHooks.FFlush(hSHP->fpSHP);
    2607         228 :         if (hSHP->fpSHX != nullptr)
    2608         228 :             hSHP->sHooks.FFlush(hSHP->fpSHX);
    2609             :     }
    2610             : 
    2611         325 :     if (hDBF != nullptr)
    2612             :     {
    2613         325 :         hDBF->sHooks.FFlush(hDBF->fp);
    2614             :     }
    2615             : 
    2616         325 :     if (m_eNeedRepack == YES && m_bAutoRepack)
    2617          13 :         Repack();
    2618             : 
    2619         325 :     return OGRERR_NONE;
    2620             : }
    2621             : 
    2622             : /************************************************************************/
    2623             : /*                          DropSpatialIndex()                          */
    2624             : /************************************************************************/
    2625             : 
    2626           9 : OGRErr OGRShapeLayer::DropSpatialIndex()
    2627             : 
    2628             : {
    2629           9 :     if (!StartUpdate("DropSpatialIndex"))
    2630           0 :         return OGRERR_FAILURE;
    2631             : 
    2632           9 :     if (!CheckForQIX() && !CheckForSBN())
    2633             :     {
    2634           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    2635             :                  "Layer %s has no spatial index, DROP SPATIAL INDEX failed.",
    2636           1 :                  poFeatureDefn->GetName());
    2637           1 :         return OGRERR_FAILURE;
    2638             :     }
    2639             : 
    2640           8 :     const bool bHadQIX = hQIX != nullptr;
    2641             : 
    2642           8 :     SHPCloseDiskTree(hQIX);
    2643           8 :     hQIX = nullptr;
    2644           8 :     bCheckedForQIX = false;
    2645             : 
    2646           8 :     SBNCloseDiskTree(hSBN);
    2647           8 :     hSBN = nullptr;
    2648           8 :     bCheckedForSBN = false;
    2649             : 
    2650           8 :     if (bHadQIX)
    2651             :     {
    2652             :         const std::string osQIXFilename =
    2653           7 :             CPLResetExtensionSafe(pszFullName, "qix");
    2654           7 :         CPLDebug("SHAPE", "Unlinking index file %s", osQIXFilename.c_str());
    2655             : 
    2656           7 :         if (VSIUnlink(osQIXFilename.c_str()) != 0)
    2657             :         {
    2658           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2659             :                      "Failed to delete file %s.\n%s", osQIXFilename.c_str(),
    2660           0 :                      VSIStrerror(errno));
    2661           0 :             return OGRERR_FAILURE;
    2662             :         }
    2663             :     }
    2664             : 
    2665           8 :     if (!bSbnSbxDeleted)
    2666             :     {
    2667           7 :         const char papszExt[2][4] = {"sbn", "sbx"};
    2668          21 :         for (int i = 0; i < 2; i++)
    2669             :         {
    2670             :             const std::string osIndexFilename =
    2671          28 :                 CPLResetExtensionSafe(pszFullName, papszExt[i]);
    2672          14 :             CPLDebug("SHAPE", "Trying to unlink index file %s",
    2673             :                      osIndexFilename.c_str());
    2674             : 
    2675          14 :             if (VSIUnlink(osIndexFilename.c_str()) != 0)
    2676             :             {
    2677           6 :                 CPLDebug("SHAPE", "Failed to delete file %s.\n%s",
    2678           6 :                          osIndexFilename.c_str(), VSIStrerror(errno));
    2679             :             }
    2680             :         }
    2681             :     }
    2682           8 :     bSbnSbxDeleted = true;
    2683             : 
    2684           8 :     ClearSpatialFIDs();
    2685             : 
    2686           8 :     return OGRERR_NONE;
    2687             : }
    2688             : 
    2689             : /************************************************************************/
    2690             : /*                         CreateSpatialIndex()                         */
    2691             : /************************************************************************/
    2692             : 
    2693          17 : OGRErr OGRShapeLayer::CreateSpatialIndex(int nMaxDepth)
    2694             : 
    2695             : {
    2696          17 :     if (!StartUpdate("CreateSpatialIndex"))
    2697           0 :         return OGRERR_FAILURE;
    2698             : 
    2699             :     /* -------------------------------------------------------------------- */
    2700             :     /*      If we have an existing spatial index, blow it away first.       */
    2701             :     /* -------------------------------------------------------------------- */
    2702          17 :     if (CheckForQIX())
    2703           1 :         DropSpatialIndex();
    2704             : 
    2705          17 :     bCheckedForQIX = false;
    2706             : 
    2707             :     /* -------------------------------------------------------------------- */
    2708             :     /*      Build a quadtree structure for this file.                       */
    2709             :     /* -------------------------------------------------------------------- */
    2710          17 :     OGRShapeLayer::SyncToDisk();
    2711          17 :     SHPTree *psTree = SHPCreateTree(hSHP, 2, nMaxDepth, nullptr, nullptr);
    2712             : 
    2713          17 :     if (nullptr == psTree)
    2714             :     {
    2715             :         // TODO(mloskot): Is it better to return OGRERR_NOT_ENOUGH_MEMORY?
    2716           0 :         CPLDebug("SHAPE",
    2717             :                  "Index creation failure. Likely, memory allocation error.");
    2718             : 
    2719           0 :         return OGRERR_FAILURE;
    2720             :     }
    2721             : 
    2722             :     /* -------------------------------------------------------------------- */
    2723             :     /*      Trim unused nodes from the tree.                                */
    2724             :     /* -------------------------------------------------------------------- */
    2725          17 :     SHPTreeTrimExtraNodes(psTree);
    2726             : 
    2727             :     /* -------------------------------------------------------------------- */
    2728             :     /*      Dump tree to .qix file.                                         */
    2729             :     /* -------------------------------------------------------------------- */
    2730             :     char *pszQIXFilename =
    2731          17 :         CPLStrdup(CPLResetExtensionSafe(pszFullName, "qix").c_str());
    2732             : 
    2733          17 :     CPLDebug("SHAPE", "Creating index file %s", pszQIXFilename);
    2734             : 
    2735          17 :     SHPWriteTree(psTree, pszQIXFilename);
    2736          17 :     CPLFree(pszQIXFilename);
    2737             : 
    2738             :     /* -------------------------------------------------------------------- */
    2739             :     /*      cleanup                                                         */
    2740             :     /* -------------------------------------------------------------------- */
    2741          17 :     SHPDestroyTree(psTree);
    2742             : 
    2743          17 :     CPL_IGNORE_RET_VAL(CheckForQIX());
    2744             : 
    2745          17 :     return OGRERR_NONE;
    2746             : }
    2747             : 
    2748             : /************************************************************************/
    2749             : /*                       CheckFileDeletion()                            */
    2750             : /************************************************************************/
    2751             : 
    2752          73 : static void CheckFileDeletion(const CPLString &osFilename)
    2753             : {
    2754             :     // On Windows, sometimes the file is still triansiently reported
    2755             :     // as existing although being deleted, which makes QGIS things that
    2756             :     // an issue arose. The following helps to reduce that risk.
    2757             :     VSIStatBufL sStat;
    2758          73 :     if (VSIStatL(osFilename, &sStat) == 0 && VSIStatL(osFilename, &sStat) == 0)
    2759             :     {
    2760           0 :         CPLDebug("Shape",
    2761             :                  "File %s is still reported as existing whereas "
    2762             :                  "it should have been deleted",
    2763             :                  osFilename.c_str());
    2764             :     }
    2765          73 : }
    2766             : 
    2767             : /************************************************************************/
    2768             : /*                         ForceDeleteFile()                            */
    2769             : /************************************************************************/
    2770             : 
    2771           6 : static void ForceDeleteFile(const CPLString &osFilename)
    2772             : {
    2773           6 :     if (VSIUnlink(osFilename) != 0)
    2774             :     {
    2775             :         // In case of failure retry with a small delay (Windows specific)
    2776           0 :         CPLSleep(0.1);
    2777           0 :         if (VSIUnlink(osFilename) != 0)
    2778             :         {
    2779           0 :             CPLDebug("Shape", "Cannot delete %s : %s", osFilename.c_str(),
    2780           0 :                      VSIStrerror(errno));
    2781             :         }
    2782             :     }
    2783           6 :     CheckFileDeletion(osFilename);
    2784           6 : }
    2785             : 
    2786             : /************************************************************************/
    2787             : /*                               Repack()                               */
    2788             : /*                                                                      */
    2789             : /*      Repack the shape and dbf file, dropping deleted records.        */
    2790             : /*      FIDs may change.                                                */
    2791             : /************************************************************************/
    2792             : 
    2793          40 : OGRErr OGRShapeLayer::Repack()
    2794             : 
    2795             : {
    2796          40 :     if (m_eNeedRepack == NO)
    2797             :     {
    2798           1 :         CPLDebug("Shape", "REPACK: nothing to do. Was done previously");
    2799           1 :         return OGRERR_NONE;
    2800             :     }
    2801             : 
    2802          39 :     if (!StartUpdate("Repack"))
    2803           1 :         return OGRERR_FAILURE;
    2804             : 
    2805             :     /* -------------------------------------------------------------------- */
    2806             :     /*      Build a list of records to be dropped.                          */
    2807             :     /* -------------------------------------------------------------------- */
    2808          76 :     std::vector<int> anRecordsToDelete;
    2809          38 :     OGRErr eErr = OGRERR_NONE;
    2810             : 
    2811          38 :     CPLDebug("Shape", "REPACK: Checking if features have been deleted");
    2812             : 
    2813          38 :     if (hDBF != nullptr)
    2814             :     {
    2815             :         try
    2816             :         {
    2817         510 :             for (int iShape = 0; iShape < nTotalShapeCount; iShape++)
    2818             :             {
    2819         475 :                 if (DBFIsRecordDeleted(hDBF, iShape))
    2820             :                 {
    2821         212 :                     anRecordsToDelete.push_back(iShape);
    2822             :                 }
    2823         950 :                 if (VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ||
    2824         475 :                     VSIFErrorL(VSI_SHP_GetVSIL(hDBF->fp)))
    2825             :                 {
    2826           0 :                     return OGRERR_FAILURE;  // I/O error.
    2827             :                 }
    2828             :             }
    2829             :         }
    2830           0 :         catch (const std::bad_alloc &)
    2831             :         {
    2832           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory in Repack()");
    2833           0 :             return OGRERR_FAILURE;
    2834             :         }
    2835             :     }
    2836             : 
    2837             :     /* -------------------------------------------------------------------- */
    2838             :     /*      If there are no records marked for deletion, we take no         */
    2839             :     /*      action.                                                         */
    2840             :     /* -------------------------------------------------------------------- */
    2841          38 :     if (anRecordsToDelete.empty() && !bSHPNeedsRepack)
    2842             :     {
    2843           8 :         CPLDebug("Shape", "REPACK: nothing to do");
    2844           8 :         return OGRERR_NONE;
    2845             :     }
    2846             : 
    2847             :     /* -------------------------------------------------------------------- */
    2848             :     /*      Find existing filenames with exact case (see #3293).            */
    2849             :     /* -------------------------------------------------------------------- */
    2850          60 :     const CPLString osDirname(CPLGetPathSafe(pszFullName));
    2851          60 :     const CPLString osBasename(CPLGetBasenameSafe(pszFullName));
    2852             : 
    2853          60 :     CPLString osDBFName;
    2854          60 :     CPLString osSHPName;
    2855          60 :     CPLString osSHXName;
    2856          60 :     CPLString osCPGName;
    2857          30 :     char **papszCandidates = VSIReadDir(osDirname);
    2858          30 :     int i = 0;
    2859         171 :     while (papszCandidates != nullptr && papszCandidates[i] != nullptr)
    2860             :     {
    2861             :         const CPLString osCandidateBasename =
    2862         282 :             CPLGetBasenameSafe(papszCandidates[i]);
    2863             :         const CPLString osCandidateExtension =
    2864         141 :             CPLGetExtensionSafe(papszCandidates[i]);
    2865             : #ifdef _WIN32
    2866             :         // On Windows, as filenames are case insensitive, a shapefile layer can
    2867             :         // be made of foo.shp and FOO.DBF, so use case insensitive comparison.
    2868             :         if (EQUAL(osCandidateBasename, osBasename))
    2869             : #else
    2870         141 :         if (osCandidateBasename.compare(osBasename) == 0)
    2871             : #endif
    2872             :         {
    2873          88 :             if (EQUAL(osCandidateExtension, "dbf"))
    2874             :                 osDBFName =
    2875          30 :                     CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
    2876          58 :             else if (EQUAL(osCandidateExtension, "shp"))
    2877             :                 osSHPName =
    2878          23 :                     CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
    2879          35 :             else if (EQUAL(osCandidateExtension, "shx"))
    2880             :                 osSHXName =
    2881          23 :                     CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
    2882          12 :             else if (EQUAL(osCandidateExtension, "cpg"))
    2883             :                 osCPGName =
    2884           3 :                     CPLFormFilenameSafe(osDirname, papszCandidates[i], nullptr);
    2885             :         }
    2886             : 
    2887         141 :         i++;
    2888             :     }
    2889          30 :     CSLDestroy(papszCandidates);
    2890          30 :     papszCandidates = nullptr;
    2891             : 
    2892          30 :     if (hDBF != nullptr && osDBFName.empty())
    2893             :     {
    2894           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2895             :                  "Cannot find the filename of the DBF file, but we managed to "
    2896             :                  "open it before !");
    2897             :         // Should not happen, really.
    2898           0 :         return OGRERR_FAILURE;
    2899             :     }
    2900             : 
    2901          30 :     if (hSHP != nullptr && osSHPName.empty())
    2902             :     {
    2903           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2904             :                  "Cannot find the filename of the SHP file, but we managed to "
    2905             :                  "open it before !");
    2906             :         // Should not happen, really.
    2907           0 :         return OGRERR_FAILURE;
    2908             :     }
    2909             : 
    2910          30 :     if (hSHP != nullptr && osSHXName.empty())
    2911             :     {
    2912           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2913             :                  "Cannot find the filename of the SHX file, but we managed to "
    2914             :                  "open it before !");
    2915             :         // Should not happen, really.
    2916           0 :         return OGRERR_FAILURE;
    2917             :     }
    2918             : 
    2919             :     /* -------------------------------------------------------------------- */
    2920             :     /*      Cleanup any existing spatial index.  It will become             */
    2921             :     /*      meaningless when the fids change.                               */
    2922             :     /* -------------------------------------------------------------------- */
    2923          30 :     if (CheckForQIX() || CheckForSBN())
    2924           0 :         DropSpatialIndex();
    2925             : 
    2926             :     /* -------------------------------------------------------------------- */
    2927             :     /*      Create a new dbf file, matching the old.                        */
    2928             :     /* -------------------------------------------------------------------- */
    2929          30 :     bool bMustReopenDBF = false;
    2930          60 :     CPLString oTempFileDBF;
    2931             :     const int nNewRecords =
    2932          30 :         nTotalShapeCount - static_cast<int>(anRecordsToDelete.size());
    2933             : 
    2934          30 :     if (hDBF != nullptr && !anRecordsToDelete.empty())
    2935             :     {
    2936          24 :         CPLDebug("Shape", "REPACK: repacking .dbf");
    2937          24 :         bMustReopenDBF = true;
    2938             : 
    2939          24 :         oTempFileDBF = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
    2940          24 :         oTempFileDBF += "_packed.dbf";
    2941             : 
    2942          24 :         DBFHandle hNewDBF = DBFCloneEmpty(hDBF, oTempFileDBF);
    2943          24 :         if (hNewDBF == nullptr)
    2944             :         {
    2945           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    2946             :                      "Failed to create temp file %s.", oTempFileDBF.c_str());
    2947           0 :             return OGRERR_FAILURE;
    2948             :         }
    2949             : 
    2950             :         // Delete temporary .cpg file if existing.
    2951          24 :         if (!osCPGName.empty())
    2952             :         {
    2953             :             CPLString oCPGTempFile =
    2954           6 :                 CPLFormFilenameSafe(osDirname, osBasename, nullptr);
    2955           3 :             oCPGTempFile += "_packed.cpg";
    2956           3 :             ForceDeleteFile(oCPGTempFile);
    2957             :         }
    2958             : 
    2959             :         /* --------------------------------------------------------------------
    2960             :          */
    2961             :         /*      Copy over all records that are not deleted. */
    2962             :         /* --------------------------------------------------------------------
    2963             :          */
    2964          24 :         int iDestShape = 0;
    2965          24 :         size_t iNextDeletedShape = 0;
    2966             : 
    2967         400 :         for (int iShape = 0; iShape < nTotalShapeCount && eErr == OGRERR_NONE;
    2968             :              iShape++)
    2969             :         {
    2970         644 :             if (iNextDeletedShape < anRecordsToDelete.size() &&
    2971         268 :                 anRecordsToDelete[iNextDeletedShape] == iShape)
    2972             :             {
    2973         212 :                 iNextDeletedShape++;
    2974             :             }
    2975             :             else
    2976             :             {
    2977         164 :                 void *pTuple = const_cast<char *>(DBFReadTuple(hDBF, iShape));
    2978         328 :                 if (pTuple == nullptr ||
    2979         164 :                     !DBFWriteTuple(hNewDBF, iDestShape++, pTuple))
    2980             :                 {
    2981           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2982             :                              "Error writing record %d in .dbf", iShape);
    2983           0 :                     eErr = OGRERR_FAILURE;
    2984             :                 }
    2985             :             }
    2986             :         }
    2987             : 
    2988          24 :         DBFClose(hNewDBF);
    2989             : 
    2990          24 :         if (eErr != OGRERR_NONE)
    2991             :         {
    2992           0 :             VSIUnlink(oTempFileDBF);
    2993           0 :             return eErr;
    2994             :         }
    2995             :     }
    2996             : 
    2997             :     /* -------------------------------------------------------------------- */
    2998             :     /*      Now create a shapefile matching the old one.                    */
    2999             :     /* -------------------------------------------------------------------- */
    3000          30 :     bool bMustReopenSHP = hSHP != nullptr;
    3001          60 :     CPLString oTempFileSHP;
    3002          60 :     CPLString oTempFileSHX;
    3003             : 
    3004             :     SHPInfo sSHPInfo;
    3005          30 :     memset(&sSHPInfo, 0, sizeof(sSHPInfo));
    3006          30 :     unsigned int *panRecOffsetNew = nullptr;
    3007          30 :     unsigned int *panRecSizeNew = nullptr;
    3008             : 
    3009             :     // On Windows, use the pack-in-place approach, ie copy the content of
    3010             :     // the _packed files on top of the existing opened files. This avoids
    3011             :     // many issues with files being locked, at the expense of more I/O
    3012             :     const bool bPackInPlace =
    3013          30 :         CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
    3014             : #ifdef _WIN32
    3015             :                                        "YES"
    3016             : #else
    3017             :                                        "NO"
    3018             : #endif
    3019             :                                        ));
    3020             : 
    3021          30 :     if (hSHP != nullptr)
    3022             :     {
    3023          23 :         CPLDebug("Shape", "REPACK: repacking .shp + .shx");
    3024             : 
    3025          23 :         oTempFileSHP = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
    3026          23 :         oTempFileSHP += "_packed.shp";
    3027          23 :         oTempFileSHX = CPLFormFilenameSafe(osDirname, osBasename, nullptr);
    3028          23 :         oTempFileSHX += "_packed.shx";
    3029             : 
    3030          23 :         SHPHandle hNewSHP = SHPCreate(oTempFileSHP, hSHP->nShapeType);
    3031          23 :         if (hNewSHP == nullptr)
    3032             :         {
    3033           0 :             if (!oTempFileDBF.empty())
    3034           0 :                 VSIUnlink(oTempFileDBF);
    3035           0 :             return OGRERR_FAILURE;
    3036             :         }
    3037             : 
    3038             :         /* --------------------------------------------------------------------
    3039             :          */
    3040             :         /*      Copy over all records that are not deleted. */
    3041             :         /* --------------------------------------------------------------------
    3042             :          */
    3043          23 :         size_t iNextDeletedShape = 0;
    3044             : 
    3045         191 :         for (int iShape = 0; iShape < nTotalShapeCount && eErr == OGRERR_NONE;
    3046             :              iShape++)
    3047             :         {
    3048         241 :             if (iNextDeletedShape < anRecordsToDelete.size() &&
    3049          73 :                 anRecordsToDelete[iNextDeletedShape] == iShape)
    3050             :             {
    3051          17 :                 iNextDeletedShape++;
    3052             :             }
    3053             :             else
    3054             :             {
    3055         151 :                 SHPObject *hObject = SHPReadObject(hSHP, iShape);
    3056         302 :                 if (hObject == nullptr ||
    3057         151 :                     SHPWriteObject(hNewSHP, -1, hObject) == -1)
    3058             :                 {
    3059           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    3060             :                              "Error writing record %d in .shp", iShape);
    3061           0 :                     eErr = OGRERR_FAILURE;
    3062             :                 }
    3063             : 
    3064         151 :                 if (hObject)
    3065         151 :                     SHPDestroyObject(hObject);
    3066             :             }
    3067             :         }
    3068             : 
    3069          23 :         if (bPackInPlace)
    3070             :         {
    3071             :             // Backup information of the updated shape context so as to
    3072             :             // restore it later in the current shape context
    3073           1 :             memcpy(&sSHPInfo, hNewSHP, sizeof(sSHPInfo));
    3074             : 
    3075             :             // Use malloc like shapelib does
    3076             :             panRecOffsetNew = reinterpret_cast<unsigned int *>(
    3077           1 :                 malloc(sizeof(unsigned int) * hNewSHP->nMaxRecords));
    3078             :             panRecSizeNew = reinterpret_cast<unsigned int *>(
    3079           1 :                 malloc(sizeof(unsigned int) * hNewSHP->nMaxRecords));
    3080           1 :             if (panRecOffsetNew == nullptr || panRecSizeNew == nullptr)
    3081             :             {
    3082           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    3083             :                          "Cannot allocate panRecOffsetNew/panRecSizeNew");
    3084           0 :                 eErr = OGRERR_FAILURE;
    3085             :             }
    3086             :             else
    3087             :             {
    3088           1 :                 memcpy(panRecOffsetNew, hNewSHP->panRecOffset,
    3089           1 :                        sizeof(unsigned int) * hNewSHP->nRecords);
    3090           1 :                 memcpy(panRecSizeNew, hNewSHP->panRecSize,
    3091           1 :                        sizeof(unsigned int) * hNewSHP->nRecords);
    3092             :             }
    3093             :         }
    3094             : 
    3095          23 :         SHPClose(hNewSHP);
    3096             : 
    3097          23 :         if (eErr != OGRERR_NONE)
    3098             :         {
    3099           0 :             VSIUnlink(oTempFileSHP);
    3100           0 :             VSIUnlink(oTempFileSHX);
    3101           0 :             if (!oTempFileDBF.empty())
    3102           0 :                 VSIUnlink(oTempFileDBF);
    3103           0 :             free(panRecOffsetNew);
    3104           0 :             free(panRecSizeNew);
    3105           0 :             return eErr;
    3106             :         }
    3107             :     }
    3108             : 
    3109             :     // We could also use pack in place for Unix but this involves extra I/O
    3110             :     // w.r.t to the delete and rename approach
    3111             : 
    3112          30 :     if (bPackInPlace)
    3113             :     {
    3114           1 :         if (hDBF != nullptr && !oTempFileDBF.empty())
    3115             :         {
    3116           1 :             if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(hDBF->fp),
    3117             :                                                  oTempFileDBF))
    3118             :             {
    3119           0 :                 CPLError(
    3120             :                     CE_Failure, CPLE_FileIO,
    3121             :                     "An error occurred while copying the content of %s on top "
    3122             :                     "of %s. "
    3123             :                     "The non corrupted version is in the _packed.dbf, "
    3124             :                     "_packed.shp and _packed.shx files that you should rename "
    3125             :                     "on top of the main ones.",
    3126           0 :                     oTempFileDBF.c_str(), VSI_SHP_GetFilename(hDBF->fp));
    3127           0 :                 free(panRecOffsetNew);
    3128           0 :                 free(panRecSizeNew);
    3129             : 
    3130           0 :                 DBFClose(hDBF);
    3131           0 :                 hDBF = nullptr;
    3132           0 :                 if (hSHP != nullptr)
    3133             :                 {
    3134           0 :                     SHPClose(hSHP);
    3135           0 :                     hSHP = nullptr;
    3136             :                 }
    3137             : 
    3138           0 :                 return OGRERR_FAILURE;
    3139             :             }
    3140             : 
    3141             :             // Refresh current handle
    3142           1 :             hDBF->nRecords = nNewRecords;
    3143             :         }
    3144             : 
    3145           1 :         if (hSHP != nullptr && !oTempFileSHP.empty())
    3146             :         {
    3147           1 :             if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(hSHP->fpSHP),
    3148             :                                                  oTempFileSHP))
    3149             :             {
    3150           0 :                 CPLError(
    3151             :                     CE_Failure, CPLE_FileIO,
    3152             :                     "An error occurred while copying the content of %s on top "
    3153             :                     "of %s. "
    3154             :                     "The non corrupted version is in the _packed.dbf, "
    3155             :                     "_packed.shp and _packed.shx files that you should rename "
    3156             :                     "on top of the main ones.",
    3157           0 :                     oTempFileSHP.c_str(), VSI_SHP_GetFilename(hSHP->fpSHP));
    3158           0 :                 free(panRecOffsetNew);
    3159           0 :                 free(panRecSizeNew);
    3160             : 
    3161           0 :                 if (hDBF != nullptr)
    3162             :                 {
    3163           0 :                     DBFClose(hDBF);
    3164           0 :                     hDBF = nullptr;
    3165             :                 }
    3166           0 :                 SHPClose(hSHP);
    3167           0 :                 hSHP = nullptr;
    3168             : 
    3169           0 :                 return OGRERR_FAILURE;
    3170             :             }
    3171           1 :             if (!OGRShapeDataSource::CopyInPlace(VSI_SHP_GetVSIL(hSHP->fpSHX),
    3172             :                                                  oTempFileSHX))
    3173             :             {
    3174           0 :                 CPLError(
    3175             :                     CE_Failure, CPLE_FileIO,
    3176             :                     "An error occurred while copying the content of %s on top "
    3177             :                     "of %s. "
    3178             :                     "The non corrupted version is in the _packed.dbf, "
    3179             :                     "_packed.shp and _packed.shx files that you should rename "
    3180             :                     "on top of the main ones.",
    3181           0 :                     oTempFileSHX.c_str(), VSI_SHP_GetFilename(hSHP->fpSHX));
    3182           0 :                 free(panRecOffsetNew);
    3183           0 :                 free(panRecSizeNew);
    3184             : 
    3185           0 :                 if (hDBF != nullptr)
    3186             :                 {
    3187           0 :                     DBFClose(hDBF);
    3188           0 :                     hDBF = nullptr;
    3189             :                 }
    3190           0 :                 SHPClose(hSHP);
    3191           0 :                 hSHP = nullptr;
    3192             : 
    3193           0 :                 return OGRERR_FAILURE;
    3194             :             }
    3195             : 
    3196             :             // Refresh current handle
    3197           1 :             hSHP->nRecords = sSHPInfo.nRecords;
    3198           1 :             hSHP->nMaxRecords = sSHPInfo.nMaxRecords;
    3199           1 :             hSHP->nFileSize = sSHPInfo.nFileSize;
    3200             :             CPLAssert(sizeof(sSHPInfo.adBoundsMin) == 4 * sizeof(double));
    3201           1 :             memcpy(hSHP->adBoundsMin, sSHPInfo.adBoundsMin,
    3202             :                    sizeof(sSHPInfo.adBoundsMin));
    3203           1 :             memcpy(hSHP->adBoundsMax, sSHPInfo.adBoundsMax,
    3204             :                    sizeof(sSHPInfo.adBoundsMax));
    3205           1 :             free(hSHP->panRecOffset);
    3206           1 :             free(hSHP->panRecSize);
    3207           1 :             hSHP->panRecOffset = panRecOffsetNew;
    3208           1 :             hSHP->panRecSize = panRecSizeNew;
    3209             :         }
    3210             :         else
    3211             :         {
    3212             :             // The free() are not really necessary but CSA doesn't realize it
    3213           0 :             free(panRecOffsetNew);
    3214           0 :             free(panRecSizeNew);
    3215             :         }
    3216             : 
    3217             :         // Now that everything is successful, we can delete the temp files
    3218           1 :         if (!oTempFileDBF.empty())
    3219             :         {
    3220           1 :             ForceDeleteFile(oTempFileDBF);
    3221             :         }
    3222           1 :         if (!oTempFileSHP.empty())
    3223             :         {
    3224           1 :             ForceDeleteFile(oTempFileSHP);
    3225           1 :             ForceDeleteFile(oTempFileSHX);
    3226             :         }
    3227             :     }
    3228             :     else
    3229             :     {
    3230             :         /* --------------------------------------------------------------------
    3231             :          */
    3232             :         /*      Cleanup the old .dbf, .shp, .shx and rename the new ones. */
    3233             :         /* --------------------------------------------------------------------
    3234             :          */
    3235          29 :         if (!oTempFileDBF.empty())
    3236             :         {
    3237          23 :             DBFClose(hDBF);
    3238          23 :             hDBF = nullptr;
    3239             : 
    3240          23 :             if (VSIUnlink(osDBFName) != 0)
    3241             :             {
    3242           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3243             :                          "Failed to delete old DBF file: %s",
    3244           0 :                          VSIStrerror(errno));
    3245             : 
    3246           0 :                 hDBF = poDS->DS_DBFOpen(osDBFName, bUpdateAccess ? "r+" : "r");
    3247             : 
    3248           0 :                 VSIUnlink(oTempFileDBF);
    3249             : 
    3250           0 :                 return OGRERR_FAILURE;
    3251             :             }
    3252             : 
    3253          23 :             if (VSIRename(oTempFileDBF, osDBFName) != 0)
    3254             :             {
    3255           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3256           0 :                          "Can not rename new DBF file: %s", VSIStrerror(errno));
    3257           0 :                 return OGRERR_FAILURE;
    3258             :             }
    3259             : 
    3260          23 :             CheckFileDeletion(oTempFileDBF);
    3261             :         }
    3262             : 
    3263          29 :         if (!oTempFileSHP.empty())
    3264             :         {
    3265          22 :             SHPClose(hSHP);
    3266          22 :             hSHP = nullptr;
    3267             : 
    3268          22 :             if (VSIUnlink(osSHPName) != 0)
    3269             :             {
    3270           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3271           0 :                          "Can not delete old SHP file: %s", VSIStrerror(errno));
    3272           0 :                 return OGRERR_FAILURE;
    3273             :             }
    3274             : 
    3275          22 :             if (VSIUnlink(osSHXName) != 0)
    3276             :             {
    3277           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3278           0 :                          "Can not delete old SHX file: %s", VSIStrerror(errno));
    3279           0 :                 return OGRERR_FAILURE;
    3280             :             }
    3281             : 
    3282          22 :             if (VSIRename(oTempFileSHP, osSHPName) != 0)
    3283             :             {
    3284           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3285           0 :                          "Can not rename new SHP file: %s", VSIStrerror(errno));
    3286           0 :                 return OGRERR_FAILURE;
    3287             :             }
    3288             : 
    3289          22 :             if (VSIRename(oTempFileSHX, osSHXName) != 0)
    3290             :             {
    3291           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    3292           0 :                          "Can not rename new SHX file: %s", VSIStrerror(errno));
    3293           0 :                 return OGRERR_FAILURE;
    3294             :             }
    3295             : 
    3296          22 :             CheckFileDeletion(oTempFileSHP);
    3297          22 :             CheckFileDeletion(oTempFileSHX);
    3298             :         }
    3299             : 
    3300             :         /* --------------------------------------------------------------------
    3301             :          */
    3302             :         /*      Reopen the shapefile */
    3303             :         /*                                                                      */
    3304             :         /* We do not need to reimplement OGRShapeDataSource::OpenFile() here */
    3305             :         /* with the fully featured error checking. */
    3306             :         /* If all operations above succeeded, then all necessary files are */
    3307             :         /* in the right place and accessible. */
    3308             :         /* --------------------------------------------------------------------
    3309             :          */
    3310             : 
    3311          29 :         const char *const pszAccess = bUpdateAccess ? "r+" : "r";
    3312             : 
    3313          29 :         if (bMustReopenSHP)
    3314          22 :             hSHP = poDS->DS_SHPOpen(osSHPName, pszAccess);
    3315          29 :         if (bMustReopenDBF)
    3316          23 :             hDBF = poDS->DS_DBFOpen(osDBFName, pszAccess);
    3317             : 
    3318          29 :         if ((bMustReopenSHP && nullptr == hSHP) ||
    3319          23 :             (bMustReopenDBF && nullptr == hDBF))
    3320           0 :             return OGRERR_FAILURE;
    3321             :     }
    3322             : 
    3323             :     /* -------------------------------------------------------------------- */
    3324             :     /*      Update total shape count.                                       */
    3325             :     /* -------------------------------------------------------------------- */
    3326          30 :     if (hDBF != nullptr)
    3327          30 :         nTotalShapeCount = hDBF->nRecords;
    3328          30 :     bSHPNeedsRepack = false;
    3329          30 :     m_eNeedRepack = NO;
    3330             : 
    3331          30 :     return OGRERR_NONE;
    3332             : }
    3333             : 
    3334             : /************************************************************************/
    3335             : /*                               ResizeDBF()                            */
    3336             : /*                                                                      */
    3337             : /*      Autoshrink columns of the DBF file to their minimum             */
    3338             : /*      size, according to the existing data.                           */
    3339             : /************************************************************************/
    3340             : 
    3341           2 : OGRErr OGRShapeLayer::ResizeDBF()
    3342             : 
    3343             : {
    3344           2 :     if (!StartUpdate("ResizeDBF"))
    3345           0 :         return OGRERR_FAILURE;
    3346             : 
    3347           2 :     if (hDBF == nullptr)
    3348             :     {
    3349           0 :         CPLError(
    3350             :             CE_Failure, CPLE_NotSupported,
    3351             :             "Attempt to RESIZE a shapefile with no .dbf file not supported.");
    3352           0 :         return OGRERR_FAILURE;
    3353             :     }
    3354             : 
    3355             :     /* Look which columns must be examined */
    3356             :     int *panColMap = static_cast<int *>(
    3357           2 :         CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int)));
    3358             :     int *panBestWidth = static_cast<int *>(
    3359           2 :         CPLMalloc(poFeatureDefn->GetFieldCount() * sizeof(int)));
    3360           2 :     int nStringCols = 0;
    3361           8 :     for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
    3362             :     {
    3363          10 :         if (poFeatureDefn->GetFieldDefn(i)->GetType() == OFTString ||
    3364          10 :             poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger ||
    3365           0 :             poFeatureDefn->GetFieldDefn(i)->GetType() == OFTInteger64)
    3366             :         {
    3367           6 :             panColMap[nStringCols] = i;
    3368           6 :             panBestWidth[nStringCols] = 1;
    3369           6 :             nStringCols++;
    3370             :         }
    3371             :     }
    3372             : 
    3373           2 :     if (nStringCols == 0)
    3374             :     {
    3375             :         // Nothing to do.
    3376           0 :         CPLFree(panColMap);
    3377           0 :         CPLFree(panBestWidth);
    3378           0 :         return OGRERR_NONE;
    3379             :     }
    3380             : 
    3381           2 :     CPLDebug("SHAPE", "Computing optimal column size...");
    3382             : 
    3383           2 :     bool bAlreadyWarned = false;
    3384           8 :     for (int i = 0; i < hDBF->nRecords; i++)
    3385             :     {
    3386           6 :         if (!DBFIsRecordDeleted(hDBF, i))
    3387             :         {
    3388          24 :             for (int j = 0; j < nStringCols; j++)
    3389             :             {
    3390          18 :                 if (DBFIsAttributeNULL(hDBF, i, panColMap[j]))
    3391           6 :                     continue;
    3392             : 
    3393             :                 const char *pszVal =
    3394          12 :                     DBFReadStringAttribute(hDBF, i, panColMap[j]);
    3395          12 :                 const int nLen = static_cast<int>(strlen(pszVal));
    3396          12 :                 if (nLen > panBestWidth[j])
    3397           6 :                     panBestWidth[j] = nLen;
    3398             :             }
    3399             :         }
    3400           0 :         else if (!bAlreadyWarned)
    3401             :         {
    3402           0 :             bAlreadyWarned = true;
    3403           0 :             CPLDebug(
    3404             :                 "SHAPE",
    3405             :                 "DBF file would also need a REPACK due to deleted records");
    3406             :         }
    3407             :     }
    3408             : 
    3409           8 :     for (int j = 0; j < nStringCols; j++)
    3410             :     {
    3411           6 :         const int iField = panColMap[j];
    3412           6 :         OGRFieldDefn *const poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
    3413             : 
    3414           6 :         const char chNativeType = DBFGetNativeFieldType(hDBF, iField);
    3415           6 :         char szFieldName[XBASE_FLDNAME_LEN_READ + 1] = {};
    3416           6 :         int nOriWidth = 0;
    3417           6 :         int nPrecision = 0;
    3418           6 :         DBFGetFieldInfo(hDBF, iField, szFieldName, &nOriWidth, &nPrecision);
    3419             : 
    3420           6 :         if (panBestWidth[j] < nOriWidth)
    3421             :         {
    3422           3 :             CPLDebug("SHAPE",
    3423             :                      "Shrinking field %d (%s) from %d to %d characters", iField,
    3424           3 :                      poFieldDefn->GetNameRef(), nOriWidth, panBestWidth[j]);
    3425             : 
    3426           3 :             if (!DBFAlterFieldDefn(hDBF, iField, szFieldName, chNativeType,
    3427           3 :                                    panBestWidth[j], nPrecision))
    3428             :             {
    3429           0 :                 CPLError(
    3430             :                     CE_Failure, CPLE_AppDefined,
    3431             :                     "Shrinking field %d (%s) from %d to %d characters failed",
    3432             :                     iField, poFieldDefn->GetNameRef(), nOriWidth,
    3433           0 :                     panBestWidth[j]);
    3434             : 
    3435           0 :                 CPLFree(panColMap);
    3436           0 :                 CPLFree(panBestWidth);
    3437             : 
    3438           0 :                 return OGRERR_FAILURE;
    3439             :             }
    3440             :             else
    3441             :             {
    3442           3 :                 whileUnsealing(poFieldDefn)->SetWidth(panBestWidth[j]);
    3443             :             }
    3444             :         }
    3445             :     }
    3446             : 
    3447           2 :     TruncateDBF();
    3448             : 
    3449           2 :     CPLFree(panColMap);
    3450           2 :     CPLFree(panBestWidth);
    3451             : 
    3452           2 :     return OGRERR_NONE;
    3453             : }
    3454             : 
    3455             : /************************************************************************/
    3456             : /*                          TruncateDBF()                               */
    3457             : /************************************************************************/
    3458             : 
    3459          23 : void OGRShapeLayer::TruncateDBF()
    3460             : {
    3461          23 :     if (hDBF == nullptr)
    3462           0 :         return;
    3463             : 
    3464          23 :     hDBF->sHooks.FSeek(hDBF->fp, 0, SEEK_END);
    3465          23 :     vsi_l_offset nOldSize = hDBF->sHooks.FTell(hDBF->fp);
    3466          23 :     vsi_l_offset nNewSize =
    3467          23 :         hDBF->nRecordLength * static_cast<SAOffset>(hDBF->nRecords) +
    3468          23 :         hDBF->nHeaderLength;
    3469          23 :     if (hDBF->bWriteEndOfFileChar)
    3470          20 :         nNewSize++;
    3471          23 :     if (nNewSize < nOldSize)
    3472             :     {
    3473          12 :         CPLDebug("SHAPE",
    3474             :                  "Truncating DBF file from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB
    3475             :                  " bytes",
    3476             :                  nOldSize, nNewSize);
    3477          12 :         VSIFTruncateL(VSI_SHP_GetVSIL(hDBF->fp), nNewSize);
    3478             :     }
    3479          23 :     hDBF->sHooks.FSeek(hDBF->fp, 0, SEEK_SET);
    3480             : }
    3481             : 
    3482             : /************************************************************************/
    3483             : /*                        RecomputeExtent()                             */
    3484             : /*                                                                      */
    3485             : /*      Force recomputation of the extent of the .SHP file              */
    3486             : /************************************************************************/
    3487             : 
    3488           5 : OGRErr OGRShapeLayer::RecomputeExtent()
    3489             : {
    3490           5 :     if (!StartUpdate("RecomputeExtent"))
    3491           1 :         return OGRERR_FAILURE;
    3492             : 
    3493           4 :     if (hSHP == nullptr)
    3494             :     {
    3495           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3496             :                  "The RECOMPUTE EXTENT operation is not permitted on a layer "
    3497             :                  "without .SHP file.");
    3498           0 :         return OGRERR_FAILURE;
    3499             :     }
    3500             : 
    3501           4 :     double adBoundsMin[4] = {0.0, 0.0, 0.0, 0.0};
    3502           4 :     double adBoundsMax[4] = {0.0, 0.0, 0.0, 0.0};
    3503             : 
    3504           4 :     bool bHasBeenInit = false;
    3505             : 
    3506          34 :     for (int iShape = 0; iShape < nTotalShapeCount; iShape++)
    3507             :     {
    3508          30 :         if (hDBF == nullptr || !DBFIsRecordDeleted(hDBF, iShape))
    3509             :         {
    3510          30 :             SHPObject *psObject = SHPReadObject(hSHP, iShape);
    3511          30 :             if (psObject != nullptr && psObject->nSHPType != SHPT_NULL &&
    3512          29 :                 psObject->nVertices != 0)
    3513             :             {
    3514          29 :                 if (!bHasBeenInit)
    3515             :                 {
    3516           4 :                     bHasBeenInit = true;
    3517           4 :                     adBoundsMin[0] = psObject->padfX[0];
    3518           4 :                     adBoundsMax[0] = psObject->padfX[0];
    3519           4 :                     adBoundsMin[1] = psObject->padfY[0];
    3520           4 :                     adBoundsMax[1] = psObject->padfY[0];
    3521           4 :                     if (psObject->padfZ)
    3522             :                     {
    3523           3 :                         adBoundsMin[2] = psObject->padfZ[0];
    3524           3 :                         adBoundsMax[2] = psObject->padfZ[0];
    3525             :                     }
    3526           4 :                     if (psObject->padfM)
    3527             :                     {
    3528           2 :                         adBoundsMin[3] = psObject->padfM[0];
    3529           2 :                         adBoundsMax[3] = psObject->padfM[0];
    3530             :                     }
    3531             :                 }
    3532             : 
    3533          66 :                 for (int i = 0; i < psObject->nVertices; i++)
    3534             :                 {
    3535          37 :                     adBoundsMin[0] =
    3536          37 :                         std::min(adBoundsMin[0], psObject->padfX[i]);
    3537          37 :                     adBoundsMin[1] =
    3538          37 :                         std::min(adBoundsMin[1], psObject->padfY[i]);
    3539          37 :                     adBoundsMax[0] =
    3540          37 :                         std::max(adBoundsMax[0], psObject->padfX[i]);
    3541          37 :                     adBoundsMax[1] =
    3542          37 :                         std::max(adBoundsMax[1], psObject->padfY[i]);
    3543          37 :                     if (psObject->padfZ)
    3544             :                     {
    3545          32 :                         adBoundsMin[2] =
    3546          32 :                             std::min(adBoundsMin[2], psObject->padfZ[i]);
    3547          32 :                         adBoundsMax[2] =
    3548          32 :                             std::max(adBoundsMax[2], psObject->padfZ[i]);
    3549             :                     }
    3550          37 :                     if (psObject->padfM)
    3551             :                     {
    3552          27 :                         adBoundsMax[3] =
    3553          27 :                             std::max(adBoundsMax[3], psObject->padfM[i]);
    3554          27 :                         adBoundsMin[3] =
    3555          27 :                             std::min(adBoundsMin[3], psObject->padfM[i]);
    3556             :                     }
    3557             :                 }
    3558             :             }
    3559          30 :             SHPDestroyObject(psObject);
    3560             :         }
    3561             :     }
    3562             : 
    3563           4 :     if (memcmp(hSHP->adBoundsMin, adBoundsMin, 4 * sizeof(double)) != 0 ||
    3564           1 :         memcmp(hSHP->adBoundsMax, adBoundsMax, 4 * sizeof(double)) != 0)
    3565             :     {
    3566           3 :         bHeaderDirty = true;
    3567           3 :         hSHP->bUpdated = TRUE;
    3568           3 :         memcpy(hSHP->adBoundsMin, adBoundsMin, 4 * sizeof(double));
    3569           3 :         memcpy(hSHP->adBoundsMax, adBoundsMax, 4 * sizeof(double));
    3570             :     }
    3571             : 
    3572           4 :     return OGRERR_NONE;
    3573             : }
    3574             : 
    3575             : /************************************************************************/
    3576             : /*                              TouchLayer()                            */
    3577             : /************************************************************************/
    3578             : 
    3579      218037 : bool OGRShapeLayer::TouchLayer()
    3580             : {
    3581      218037 :     poDS->SetLastUsedLayer(this);
    3582             : 
    3583      218037 :     if (eFileDescriptorsState == FD_OPENED)
    3584      218029 :         return true;
    3585           8 :     if (eFileDescriptorsState == FD_CANNOT_REOPEN)
    3586           0 :         return false;
    3587             : 
    3588           8 :     return ReopenFileDescriptors();
    3589             : }
    3590             : 
    3591             : /************************************************************************/
    3592             : /*                        ReopenFileDescriptors()                       */
    3593             : /************************************************************************/
    3594             : 
    3595          12 : bool OGRShapeLayer::ReopenFileDescriptors()
    3596             : {
    3597          12 :     CPLDebug("SHAPE", "ReopenFileDescriptors(%s)", pszFullName);
    3598             : 
    3599             :     const bool bRealUpdateAccess =
    3600          20 :         bUpdateAccess &&
    3601           8 :         (!poDS->IsZip() || !poDS->GetTemporaryUnzipDir().empty());
    3602             : 
    3603          12 :     if (bHSHPWasNonNULL)
    3604             :     {
    3605          12 :         hSHP = poDS->DS_SHPOpen(pszFullName, bRealUpdateAccess ? "r+" : "r");
    3606             : 
    3607          12 :         if (hSHP == nullptr)
    3608             :         {
    3609           0 :             eFileDescriptorsState = FD_CANNOT_REOPEN;
    3610           0 :             return false;
    3611             :         }
    3612             :     }
    3613             : 
    3614          12 :     if (bHDBFWasNonNULL)
    3615             :     {
    3616          12 :         hDBF = poDS->DS_DBFOpen(pszFullName, bRealUpdateAccess ? "r+" : "r");
    3617             : 
    3618          12 :         if (hDBF == nullptr)
    3619             :         {
    3620           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Cannot reopen %s",
    3621           0 :                      CPLResetExtensionSafe(pszFullName, "dbf").c_str());
    3622           0 :             eFileDescriptorsState = FD_CANNOT_REOPEN;
    3623           0 :             return false;
    3624             :         }
    3625             :     }
    3626             : 
    3627          12 :     eFileDescriptorsState = FD_OPENED;
    3628             : 
    3629          12 :     return true;
    3630             : }
    3631             : 
    3632             : /************************************************************************/
    3633             : /*                        CloseUnderlyingLayer()                        */
    3634             : /************************************************************************/
    3635             : 
    3636        2013 : void OGRShapeLayer::CloseUnderlyingLayer()
    3637             : {
    3638        2013 :     CPLDebug("SHAPE", "CloseUnderlyingLayer(%s)", pszFullName);
    3639             : 
    3640        2013 :     if (hDBF != nullptr)
    3641        2013 :         DBFClose(hDBF);
    3642        2013 :     hDBF = nullptr;
    3643             : 
    3644        2013 :     if (hSHP != nullptr)
    3645        2013 :         SHPClose(hSHP);
    3646        2013 :     hSHP = nullptr;
    3647             : 
    3648             :     // We close QIX and reset the check flag, so that CheckForQIX()
    3649             :     // will retry opening it if necessary when the layer is active again.
    3650        2013 :     if (hQIX != nullptr)
    3651           0 :         SHPCloseDiskTree(hQIX);
    3652        2013 :     hQIX = nullptr;
    3653        2013 :     bCheckedForQIX = false;
    3654             : 
    3655        2013 :     if (hSBN != nullptr)
    3656           0 :         SBNCloseDiskTree(hSBN);
    3657        2013 :     hSBN = nullptr;
    3658        2013 :     bCheckedForSBN = false;
    3659             : 
    3660        2013 :     eFileDescriptorsState = FD_CLOSED;
    3661        2013 : }
    3662             : 
    3663             : /************************************************************************/
    3664             : /*                            AddToFileList()                           */
    3665             : /************************************************************************/
    3666             : 
    3667         140 : void OGRShapeLayer::AddToFileList(CPLStringList &oFileList)
    3668             : {
    3669         140 :     if (!TouchLayer())
    3670           0 :         return;
    3671             : 
    3672         140 :     if (hSHP)
    3673             :     {
    3674          16 :         const char *pszSHPFilename = VSI_SHP_GetFilename(hSHP->fpSHP);
    3675          16 :         oFileList.AddStringDirectly(VSIGetCanonicalFilename(pszSHPFilename));
    3676          32 :         const std::string osSHPExt = CPLGetExtensionSafe(pszSHPFilename);
    3677             :         const std::string osSHXFilename = CPLResetExtensionSafe(
    3678          32 :             pszSHPFilename, (osSHPExt[0] == 's') ? "shx" : "SHX");
    3679             :         oFileList.AddStringDirectly(
    3680          16 :             VSIGetCanonicalFilename(osSHXFilename.c_str()));
    3681             :     }
    3682             : 
    3683         140 :     if (hDBF)
    3684             :     {
    3685         139 :         const char *pszDBFFilename = VSI_SHP_GetFilename(hDBF->fp);
    3686         139 :         oFileList.AddStringDirectly(VSIGetCanonicalFilename(pszDBFFilename));
    3687         139 :         if (hDBF->pszCodePage != nullptr && hDBF->iLanguageDriver == 0)
    3688             :         {
    3689           0 :             const std::string osDBFExt = CPLGetExtensionSafe(pszDBFFilename);
    3690             :             const std::string osCPGFilename = CPLResetExtensionSafe(
    3691           0 :                 pszDBFFilename, (osDBFExt[0] == 'd') ? "cpg" : "CPG");
    3692             :             oFileList.AddStringDirectly(
    3693           0 :                 VSIGetCanonicalFilename(osCPGFilename.c_str()));
    3694             :         }
    3695             :     }
    3696             : 
    3697         140 :     if (hSHP)
    3698             :     {
    3699          16 :         if (GetSpatialRef() != nullptr)
    3700             :         {
    3701             :             OGRShapeGeomFieldDefn *poGeomFieldDefn =
    3702           9 :                 cpl::down_cast<OGRShapeGeomFieldDefn *>(
    3703           9 :                     GetLayerDefn()->GetGeomFieldDefn(0));
    3704             :             oFileList.AddStringDirectly(
    3705           9 :                 VSIGetCanonicalFilename(poGeomFieldDefn->GetPrjFilename()));
    3706             :         }
    3707          16 :         if (CheckForQIX())
    3708             :         {
    3709             :             const std::string osQIXFilename =
    3710           4 :                 CPLResetExtensionSafe(pszFullName, "qix");
    3711             :             oFileList.AddStringDirectly(
    3712           2 :                 VSIGetCanonicalFilename(osQIXFilename.c_str()));
    3713             :         }
    3714          14 :         else if (CheckForSBN())
    3715             :         {
    3716             :             const std::string osSBNFilename =
    3717           2 :                 CPLResetExtensionSafe(pszFullName, "sbn");
    3718             :             oFileList.AddStringDirectly(
    3719           1 :                 VSIGetCanonicalFilename(osSBNFilename.c_str()));
    3720             :             const std::string osSBXFilename =
    3721           2 :                 CPLResetExtensionSafe(pszFullName, "sbx");
    3722             :             oFileList.AddStringDirectly(
    3723           1 :                 VSIGetCanonicalFilename(osSBXFilename.c_str()));
    3724             :         }
    3725             :     }
    3726             : }
    3727             : 
    3728             : /************************************************************************/
    3729             : /*                   UpdateFollowingDeOrRecompression()                 */
    3730             : /************************************************************************/
    3731             : 
    3732           3 : void OGRShapeLayer::UpdateFollowingDeOrRecompression()
    3733             : {
    3734           3 :     CPLAssert(poDS->IsZip());
    3735           6 :     CPLString osDSDir = poDS->GetTemporaryUnzipDir();
    3736           3 :     if (osDSDir.empty())
    3737           0 :         osDSDir = poDS->GetVSIZipPrefixeDir();
    3738             : 
    3739           3 :     if (GetSpatialRef() != nullptr)
    3740             :     {
    3741             :         OGRShapeGeomFieldDefn *poGeomFieldDefn =
    3742           3 :             cpl::down_cast<OGRShapeGeomFieldDefn *>(
    3743           3 :                 GetLayerDefn()->GetGeomFieldDefn(0));
    3744           9 :         poGeomFieldDefn->SetPrjFilename(
    3745           6 :             CPLFormFilenameSafe(
    3746             :                 osDSDir.c_str(),
    3747           3 :                 CPLGetFilename(poGeomFieldDefn->GetPrjFilename().c_str()),
    3748             :                 nullptr)
    3749             :                 .c_str());
    3750             :     }
    3751             : 
    3752           3 :     char *pszNewFullName = CPLStrdup(
    3753           6 :         CPLFormFilenameSafe(osDSDir, CPLGetFilename(pszFullName), nullptr)
    3754             :             .c_str());
    3755           3 :     CPLFree(pszFullName);
    3756           3 :     pszFullName = pszNewFullName;
    3757           3 :     CloseUnderlyingLayer();
    3758           3 : }
    3759             : 
    3760             : /************************************************************************/
    3761             : /*                           Rename()                                   */
    3762             : /************************************************************************/
    3763             : 
    3764           6 : OGRErr OGRShapeLayer::Rename(const char *pszNewName)
    3765             : {
    3766           6 :     if (!TestCapability(OLCRename))
    3767           0 :         return OGRERR_FAILURE;
    3768             : 
    3769           6 :     if (poDS->GetLayerByName(pszNewName) != nullptr)
    3770             :     {
    3771           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer %s already exists",
    3772             :                  pszNewName);
    3773           1 :         return OGRERR_FAILURE;
    3774             :     }
    3775             : 
    3776           5 :     if (!poDS->UncompressIfNeeded())
    3777           0 :         return OGRERR_FAILURE;
    3778             : 
    3779          10 :     CPLStringList oFileList;
    3780           5 :     AddToFileList(oFileList);
    3781             : 
    3782          10 :     const std::string osDirname = CPLGetPathSafe(pszFullName);
    3783          23 :     for (int i = 0; i < oFileList.size(); ++i)
    3784             :     {
    3785             :         const std::string osRenamedFile =
    3786             :             CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
    3787          19 :                                 CPLGetExtensionSafe(oFileList[i]).c_str());
    3788             :         VSIStatBufL sStat;
    3789          19 :         if (VSIStatL(osRenamedFile.c_str(), &sStat) == 0)
    3790             :         {
    3791           1 :             CPLError(CE_Failure, CPLE_AppDefined, "File %s already exists",
    3792             :                      osRenamedFile.c_str());
    3793           1 :             return OGRERR_FAILURE;
    3794             :         }
    3795             :     }
    3796             : 
    3797           4 :     CloseUnderlyingLayer();
    3798             : 
    3799          20 :     for (int i = 0; i < oFileList.size(); ++i)
    3800             :     {
    3801             :         const std::string osRenamedFile =
    3802             :             CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
    3803          16 :                                 CPLGetExtensionSafe(oFileList[i]).c_str());
    3804          16 :         if (VSIRename(oFileList[i], osRenamedFile.c_str()) != 0)
    3805             :         {
    3806           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename %s to %s",
    3807             :                      oFileList[i], osRenamedFile.c_str());
    3808           0 :             return OGRERR_FAILURE;
    3809             :         }
    3810             :     }
    3811             : 
    3812           4 :     if (GetSpatialRef() != nullptr)
    3813             :     {
    3814             :         OGRShapeGeomFieldDefn *poGeomFieldDefn =
    3815           4 :             cpl::down_cast<OGRShapeGeomFieldDefn *>(
    3816           4 :                 GetLayerDefn()->GetGeomFieldDefn(0));
    3817          12 :         poGeomFieldDefn->SetPrjFilename(
    3818           8 :             CPLFormFilenameSafe(
    3819             :                 osDirname.c_str(), pszNewName,
    3820           8 :                 CPLGetExtensionSafe(poGeomFieldDefn->GetPrjFilename().c_str())
    3821             :                     .c_str())
    3822             :                 .c_str());
    3823             :     }
    3824             : 
    3825             :     char *pszNewFullName =
    3826           4 :         CPLStrdup(CPLFormFilenameSafe(osDirname.c_str(), pszNewName,
    3827           8 :                                       CPLGetExtensionSafe(pszFullName).c_str())
    3828             :                       .c_str());
    3829           4 :     CPLFree(pszFullName);
    3830           4 :     pszFullName = pszNewFullName;
    3831             : 
    3832           4 :     if (!ReopenFileDescriptors())
    3833           0 :         return OGRERR_FAILURE;
    3834             : 
    3835           4 :     SetDescription(pszNewName);
    3836           4 :     whileUnsealing(poFeatureDefn)->SetName(pszNewName);
    3837             : 
    3838           4 :     return OGRERR_NONE;
    3839             : }
    3840             : 
    3841             : /************************************************************************/
    3842             : /*                          GetDataset()                                */
    3843             : /************************************************************************/
    3844             : 
    3845          38 : GDALDataset *OGRShapeLayer::GetDataset()
    3846             : {
    3847          38 :     return poDS;
    3848             : }
    3849             : 
    3850             : /************************************************************************/
    3851             : /*                        GetNextArrowArray()                           */
    3852             : /************************************************************************/
    3853             : 
    3854             : // Specialized implementation restricted to situations where only retrieving
    3855             : // of FID values is asked (without filters)
    3856             : // In other cases, fall back to generic implementation.
    3857          46 : int OGRShapeLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
    3858             :                                      struct ArrowArray *out_array)
    3859             : {
    3860          46 :     m_bLastGetNextArrowArrayUsedOptimizedCodePath = false;
    3861          46 :     if (!TouchLayer())
    3862             :     {
    3863           0 :         memset(out_array, 0, sizeof(*out_array));
    3864           0 :         return EIO;
    3865             :     }
    3866             : 
    3867          46 :     if (!hDBF || m_poAttrQuery != nullptr || m_poFilterGeom != nullptr)
    3868             :     {
    3869          21 :         return OGRLayer::GetNextArrowArray(stream, out_array);
    3870             :     }
    3871             : 
    3872             :     // If any field is not ignored, use generic implementation
    3873          25 :     const int nFieldCount = poFeatureDefn->GetFieldCount();
    3874          56 :     for (int i = 0; i < nFieldCount; ++i)
    3875             :     {
    3876          45 :         if (!poFeatureDefn->GetFieldDefn(i)->IsIgnored())
    3877          14 :             return OGRLayer::GetNextArrowArray(stream, out_array);
    3878             :     }
    3879          22 :     if (GetGeomType() != wkbNone &&
    3880          11 :         !poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
    3881           2 :         return OGRLayer::GetNextArrowArray(stream, out_array);
    3882             : 
    3883           9 :     OGRArrowArrayHelper sHelper(poDS, poFeatureDefn,
    3884          18 :                                 m_aosArrowArrayStreamOptions, out_array);
    3885           9 :     if (out_array->release == nullptr)
    3886             :     {
    3887           0 :         return ENOMEM;
    3888             :     }
    3889             : 
    3890           9 :     if (!sHelper.m_bIncludeFID)
    3891           1 :         return OGRLayer::GetNextArrowArray(stream, out_array);
    3892             : 
    3893           8 :     m_bLastGetNextArrowArrayUsedOptimizedCodePath = true;
    3894           8 :     int nCount = 0;
    3895          34 :     while (iNextShapeId < nTotalShapeCount)
    3896             :     {
    3897             :         const bool bIsDeleted =
    3898          28 :             CPL_TO_BOOL(DBFIsRecordDeleted(hDBF, iNextShapeId));
    3899          28 :         if (bIsDeleted)
    3900             :         {
    3901           1 :             ++iNextShapeId;
    3902           1 :             continue;
    3903             :         }
    3904          53 :         if (VSIFEofL(VSI_SHP_GetVSIL(hDBF->fp)) ||
    3905          26 :             VSIFErrorL(VSI_SHP_GetVSIL(hDBF->fp)))
    3906             :         {
    3907           1 :             out_array->release(out_array);
    3908           1 :             memset(out_array, 0, sizeof(*out_array));
    3909           1 :             return EIO;
    3910             :         }
    3911          26 :         sHelper.m_panFIDValues[nCount] = iNextShapeId;
    3912          26 :         ++iNextShapeId;
    3913          26 :         ++nCount;
    3914          26 :         if (nCount == sHelper.m_nMaxBatchSize)
    3915           1 :             break;
    3916             :     }
    3917           7 :     sHelper.Shrink(nCount);
    3918           7 :     if (nCount == 0)
    3919             :     {
    3920           3 :         out_array->release(out_array);
    3921           3 :         memset(out_array, 0, sizeof(*out_array));
    3922             :     }
    3923           7 :     return 0;
    3924             : }
    3925             : 
    3926             : /************************************************************************/
    3927             : /*                        GetMetadataItem()                             */
    3928             : /************************************************************************/
    3929             : 
    3930         801 : const char *OGRShapeLayer::GetMetadataItem(const char *pszName,
    3931             :                                            const char *pszDomain)
    3932             : {
    3933         801 :     if (pszName && pszDomain && EQUAL(pszDomain, "__DEBUG__") &&
    3934          10 :         EQUAL(pszName, "LAST_GET_NEXT_ARROW_ARRAY_USED_OPTIMIZED_CODE_PATH"))
    3935             :     {
    3936          10 :         return m_bLastGetNextArrowArrayUsedOptimizedCodePath ? "YES" : "NO";
    3937             :     }
    3938         791 :     return OGRLayer::GetMetadataItem(pszName, pszDomain);
    3939             : }

Generated by: LCOV version 1.14