LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/shape - ogrshapelayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1497 1931 77.5 %
Date: 2025-11-08 17:38:05 Functions: 54 54 100.0 %

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

Generated by: LCOV version 1.14