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

Generated by: LCOV version 1.14