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

Generated by: LCOV version 1.14