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