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

Generated by: LCOV version 1.14