1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: Implements OGRShapeDataSource class.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 1999, Les Technologies SoftMap Inc.
9 * Copyright (c) 2008-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 "cpl_port.h"
31 #include "ogrshape.h"
32
33 #include <algorithm>
34 #include <cstddef>
35 #include <cstdlib>
36 #include <cstring>
37 #include <memory>
38 #include <set>
39 #include <vector>
40
41 #include "cpl_conv.h"
42 #include "cpl_error.h"
43 #include "cpl_string.h"
44 #include "cpl_vsi.h"
45 #include "cpl_vsi_error.h"
46 #include "gdal.h"
47 #include "gdal_priv.h"
48 #include "ogr_core.h"
49 #include "ogr_geometry.h"
50 #include "ogr_spatialref.h"
51 #include "ogrlayerpool.h"
52 #include "ogrsf_frmts.h"
53 #include "shapefil.h"
54 #include "shp_vsi.h"
55
56 // #define IMMEDIATE_OPENING 1
57
58 CPL_CVSID("$Id: ogrshapedatasource.cpp 74eca1110adf6e47b87fee110ad9bf5f322fef64 2021-03-15 14:38:32 +0100 Even Rouault $")
59
60 constexpr int knREFRESH_LOCK_FILE_DELAY_SEC = 10;
61
62 /************************************************************************/
63 /* DS_SHPOpen() */
64 /************************************************************************/
65
DS_SHPOpen(const char * pszShapeFile,const char * pszAccess)66 SHPHandle OGRShapeDataSource::DS_SHPOpen( const char * pszShapeFile,
67 const char * pszAccess )
68 {
69 // Do lazy shx loading for /vsicurl/
70 if( STARTS_WITH(pszShapeFile, "/vsicurl/") &&
71 strcmp(pszAccess, "r") == 0 )
72 pszAccess = "rl";
73
74 const bool bRestoreSHX =
75 CPLTestBool( CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE") );
76 SHPHandle hSHP =
77 SHPOpenLLEx( pszShapeFile, pszAccess,
78 const_cast<SAHooks *>(VSI_SHP_GetHook(b2GBLimit)),
79 bRestoreSHX );
80
81 if( hSHP != nullptr )
82 SHPSetFastModeReadObject( hSHP, TRUE );
83 return hSHP;
84 }
85
86 /************************************************************************/
87 /* DS_DBFOpen() */
88 /************************************************************************/
89
DS_DBFOpen(const char * pszDBFFile,const char * pszAccess)90 DBFHandle OGRShapeDataSource::DS_DBFOpen( const char * pszDBFFile,
91 const char * pszAccess )
92 {
93 DBFHandle hDBF =
94 DBFOpenLL( pszDBFFile, pszAccess,
95 const_cast<SAHooks *>(VSI_SHP_GetHook(b2GBLimit)) );
96 return hDBF;
97 }
98
99 /************************************************************************/
100 /* OGRShapeDataSource() */
101 /************************************************************************/
102
OGRShapeDataSource()103 OGRShapeDataSource::OGRShapeDataSource() :
104 papoLayers(nullptr),
105 nLayers(0),
106 pszName(nullptr),
107 bDSUpdate(false),
108 bSingleFileDataSource(false),
109 poPool(new OGRLayerPool()),
110 b2GBLimit(CPLTestBool(CPLGetConfigOption("SHAPE_2GB_LIMIT", "FALSE")))
111 {}
112
113 /************************************************************************/
114 /* GetLayerNames() */
115 /************************************************************************/
116
GetLayerNames() const117 std::vector<CPLString> OGRShapeDataSource::GetLayerNames() const
118 {
119 std::vector<CPLString> res;
120 const_cast<OGRShapeDataSource*>(this)->GetLayerCount();
121 for( int i = 0; i < nLayers; i++ )
122 {
123 res.emplace_back(papoLayers[i]->GetName());
124 }
125 return res;
126 }
127
128 /************************************************************************/
129 /* ~OGRShapeDataSource() */
130 /************************************************************************/
131
~OGRShapeDataSource()132 OGRShapeDataSource::~OGRShapeDataSource()
133
134 {
135 std::vector<CPLString> layerNames;
136 if( !m_osTemporaryUnzipDir.empty() )
137 {
138 layerNames = GetLayerNames();
139 }
140 for( int i = 0; i < nLayers; i++ )
141 {
142 CPLAssert( nullptr != papoLayers[i] );
143
144 delete papoLayers[i];
145 }
146 CPLFree( papoLayers );
147 nLayers = 0;
148 papoLayers = nullptr;
149
150 delete poPool;
151
152 RecompressIfNeeded(layerNames);
153 RemoveLockFile();
154
155 // Free mutex & cond
156 if( m_poRefreshLockFileMutex )
157 {
158 CPLDestroyMutex(m_poRefreshLockFileMutex);
159 m_poRefreshLockFileMutex = nullptr;
160 }
161 if( m_poRefreshLockFileCond )
162 {
163 CPLDestroyCond(m_poRefreshLockFileCond);
164 m_poRefreshLockFileCond = nullptr;
165 }
166
167 CPLFree( pszName );
168 }
169
170 /************************************************************************/
171 /* OpenZip() */
172 /************************************************************************/
173
OpenZip(GDALOpenInfo * poOpenInfo,const char * pszOriFilename)174 bool OGRShapeDataSource::OpenZip( GDALOpenInfo* poOpenInfo,
175 const char* pszOriFilename )
176 {
177 if( !Open(poOpenInfo, true) )
178 return false;
179 CPLFree(pszName);
180 pszName = CPLStrdup(pszOriFilename);
181 m_bIsZip = true;
182 m_bSingleLayerZip = EQUAL(CPLGetExtension(pszOriFilename), "shz");
183
184 if( !m_bSingleLayerZip )
185 {
186 CPLString osLockFile(pszName);
187 osLockFile += ".gdal.lock";
188 VSIStatBufL sStat;
189 if( VSIStatL(osLockFile, &sStat) == 0 &&
190 sStat.st_mtime < time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC )
191 {
192 CPLDebug("Shape", "Deleting stalled %s", osLockFile.c_str());
193 VSIUnlink(osLockFile);
194 }
195 }
196
197 return true;
198 }
199
200 /************************************************************************/
201 /* CreateZip() */
202 /************************************************************************/
203
CreateZip(const char * pszOriFilename)204 bool OGRShapeDataSource::CreateZip( const char* pszOriFilename )
205 {
206 CPLAssert( nLayers == 0 );
207 pszName = CPLStrdup( pszOriFilename );
208
209 void* hZIP = CPLCreateZip(pszName, nullptr);
210 if( !hZIP )
211 return false;
212 if( CPLCloseZip(hZIP) != CE_None )
213 return false;
214 bDSUpdate = true;
215 m_bIsZip = true;
216 m_bSingleLayerZip = EQUAL(CPLGetExtension(pszOriFilename), "shz");
217 return true;
218 }
219
220 /************************************************************************/
221 /* Open() */
222 /************************************************************************/
223
Open(GDALOpenInfo * poOpenInfo,bool bTestOpen,bool bForceSingleFileDataSource)224 bool OGRShapeDataSource::Open( GDALOpenInfo* poOpenInfo,
225 bool bTestOpen, bool bForceSingleFileDataSource )
226
227 {
228 CPLAssert( nLayers == 0 );
229
230 const char * pszNewName = poOpenInfo->pszFilename;
231 const bool bUpdate = poOpenInfo->eAccess == GA_Update;
232 CPLAssert( papszOpenOptions == nullptr );
233 papszOpenOptions = CSLDuplicate( poOpenInfo->papszOpenOptions );
234
235 pszName = CPLStrdup( pszNewName );
236
237 bDSUpdate = bUpdate;
238
239 bSingleFileDataSource = CPL_TO_BOOL(bForceSingleFileDataSource);
240
241 /* -------------------------------------------------------------------- */
242 /* If bSingleFileDataSource is TRUE we don't try to do anything */
243 /* else. */
244 /* This is only utilized when the OGRShapeDriver::Create() */
245 /* method wants to create a stub OGRShapeDataSource for a */
246 /* single shapefile. The driver will take care of creating the */
247 /* file by calling ICreateLayer(). */
248 /* -------------------------------------------------------------------- */
249 if( bSingleFileDataSource )
250 return true;
251
252 /* -------------------------------------------------------------------- */
253 /* Is the given path a directory or a regular file? */
254 /* -------------------------------------------------------------------- */
255 if( !poOpenInfo->bStatOK )
256 {
257 if( !bTestOpen )
258 CPLError( CE_Failure, CPLE_AppDefined,
259 "%s is neither a file or directory, Shape access failed.",
260 pszNewName );
261
262 return false;
263 }
264
265 /* -------------------------------------------------------------------- */
266 /* Build a list of filenames we figure are Shape files. */
267 /* -------------------------------------------------------------------- */
268 if( !poOpenInfo->bIsDirectory )
269 {
270 if( !OpenFile( pszNewName, bUpdate ) )
271 {
272 if( !bTestOpen )
273 CPLError(
274 CE_Failure, CPLE_OpenFailed,
275 "Failed to open shapefile %s. "
276 "It may be corrupt or read-only file accessed in "
277 "update mode.",
278 pszNewName );
279
280 return false;
281 }
282
283 bSingleFileDataSource = true;
284
285 return true;
286 }
287 else
288 {
289 char **papszCandidates = VSIReadDir( pszNewName );
290 const int nCandidateCount = CSLCount( papszCandidates );
291 bool bMightBeOldCoverage = false;
292 std::set<CPLString> osLayerNameSet;
293
294 for( int iCan = 0; iCan < nCandidateCount; iCan++ )
295 {
296 const char *pszCandidate = papszCandidates[iCan];
297 const char *pszLayerName = CPLGetBasename(pszCandidate);
298 CPLString osLayerName(pszLayerName);
299 #ifdef WIN32
300 // On Windows, as filenames are case insensitive, a shapefile layer
301 // can be made of foo.shp and FOO.DBF, so to detect unique layer
302 // names, put them upper case in the unique set used for detection.
303 osLayerName.toupper();
304 #endif
305
306 if( EQUAL(pszCandidate,"ARC") )
307 bMightBeOldCoverage = true;
308
309 if( strlen(pszCandidate) < 4
310 || !EQUAL(pszCandidate+strlen(pszCandidate)-4,".shp") )
311 continue;
312
313 char *pszFilename =
314 CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, nullptr));
315
316 osLayerNameSet.insert(osLayerName);
317 #ifdef IMMEDIATE_OPENING
318 if( !OpenFile( pszFilename, bUpdate )
319 && !bTestOpen )
320 {
321 CPLError(
322 CE_Failure, CPLE_OpenFailed,
323 "Failed to open shapefile %s. "
324 "It may be corrupt or read-only file accessed in "
325 "update mode.",
326 pszFilename );
327 CPLFree( pszFilename );
328 CSLDestroy( papszCandidates );
329 return false;
330 }
331 #else
332 oVectorLayerName.push_back(pszFilename);
333 #endif
334 CPLFree( pszFilename );
335 }
336
337 // Try and .dbf files without apparent associated shapefiles.
338 for( int iCan = 0; iCan < nCandidateCount; iCan++ )
339 {
340 const char *pszCandidate = papszCandidates[iCan];
341 const char *pszLayerName = CPLGetBasename(pszCandidate);
342 CPLString osLayerName(pszLayerName);
343 #ifdef WIN32
344 osLayerName.toupper();
345 #endif
346
347 // We don't consume .dbf files in a directory that looks like
348 // an old style Arc/Info (for PC?) that unless we found at least
349 // some shapefiles. See Bug 493.
350 if( bMightBeOldCoverage && osLayerNameSet.empty() )
351 continue;
352
353 if( strlen(pszCandidate) < 4
354 || !EQUAL(pszCandidate+strlen(pszCandidate)-4, ".dbf") )
355 continue;
356
357 if( osLayerNameSet.find(osLayerName) != osLayerNameSet.end() )
358 continue;
359
360 // We don't want to access .dbf files with an associated .tab
361 // file, or it will never get recognised as a mapinfo dataset.
362 bool bFoundTAB = false;
363 for( int iCan2 = 0; iCan2 < nCandidateCount; iCan2++ )
364 {
365 const char *pszCandidate2 = papszCandidates[iCan2];
366
367 if( EQUALN(pszCandidate2, pszLayerName, strlen(pszLayerName))
368 && EQUAL(pszCandidate2 + strlen(pszLayerName), ".tab") )
369 bFoundTAB = true;
370 }
371
372 if( bFoundTAB )
373 continue;
374
375 char *pszFilename =
376 CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, nullptr));
377
378 osLayerNameSet.insert(osLayerName);
379
380 #ifdef IMMEDIATE_OPENING
381 if( !OpenFile( pszFilename, bUpdate )
382 && !bTestOpen )
383 {
384 CPLError(
385 CE_Failure, CPLE_OpenFailed,
386 "Failed to open dbf file %s. "
387 "It may be corrupt or read-only file accessed in "
388 "update mode.",
389 pszFilename );
390 CPLFree( pszFilename );
391 CSLDestroy( papszCandidates );
392 return false;
393 }
394 #else
395 oVectorLayerName.push_back(pszFilename);
396 #endif
397 CPLFree( pszFilename );
398 }
399
400 CSLDestroy( papszCandidates );
401
402 #ifdef IMMEDIATE_OPENING
403 const int nDirLayers = nLayers;
404 #else
405 const int nDirLayers = static_cast<int>(oVectorLayerName.size());
406 #endif
407
408 CPLErrorReset();
409
410 return nDirLayers > 0 || !bTestOpen;
411 }
412 }
413
414 /************************************************************************/
415 /* OpenFile() */
416 /************************************************************************/
417
OpenFile(const char * pszNewName,bool bUpdate)418 bool OGRShapeDataSource::OpenFile( const char *pszNewName, bool bUpdate )
419
420 {
421 const char *pszExtension = CPLGetExtension( pszNewName );
422
423 if( !EQUAL(pszExtension,"shp") && !EQUAL(pszExtension,"shx")
424 && !EQUAL(pszExtension,"dbf") )
425 return false;
426
427 /* -------------------------------------------------------------------- */
428 /* SHPOpen() should include better (CPL based) error reporting, */
429 /* and we should be trying to distinguish at this point whether */
430 /* failure is a result of trying to open a non-shapefile, or */
431 /* whether it was a shapefile and we want to report the error */
432 /* up. */
433 /* */
434 /* Care is taken to suppress the error and only reissue it if */
435 /* we think it is appropriate. */
436 /* -------------------------------------------------------------------- */
437 const bool bRealUpdateAccess = bUpdate &&
438 (!IsZip() || !GetTemporaryUnzipDir().empty());
439 CPLErrorReset();
440 CPLPushErrorHandler( CPLQuietErrorHandler );
441 SHPHandle hSHP = bRealUpdateAccess ?
442 DS_SHPOpen( pszNewName, "r+" ) :
443 DS_SHPOpen( pszNewName, "r" );
444 CPLPopErrorHandler();
445
446 const bool bRestoreSHX =
447 CPLTestBool( CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE") );
448 if( bRestoreSHX && EQUAL(CPLGetExtension(pszNewName),"dbf") &&
449 CPLGetLastErrorMsg()[0] != '\0' )
450 {
451 CPLString osMsg = CPLGetLastErrorMsg();
452
453 CPLError( CE_Warning, CPLE_AppDefined, "%s", osMsg.c_str() );
454 }
455 else
456 {
457 if( hSHP == nullptr
458 && (!EQUAL(CPLGetExtension(pszNewName),"dbf")
459 || strstr(CPLGetLastErrorMsg(),".shp") == nullptr) )
460 {
461 CPLString osMsg = CPLGetLastErrorMsg();
462
463 CPLError( CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str() );
464
465 return false;
466 }
467 CPLErrorReset();
468 }
469
470 /* -------------------------------------------------------------------- */
471 /* Open the .dbf file, if it exists. To open a dbf file, the */
472 /* filename has to either refer to a successfully opened shp */
473 /* file or has to refer to the actual .dbf file. */
474 /* -------------------------------------------------------------------- */
475 DBFHandle hDBF = nullptr;
476 if( hSHP != nullptr || EQUAL(CPLGetExtension(pszNewName), "dbf") )
477 {
478 if( bRealUpdateAccess )
479 {
480 hDBF = DS_DBFOpen( pszNewName, "r+" );
481 if( hSHP != nullptr && hDBF == nullptr )
482 {
483 for( int i = 0; i < 2; i++ )
484 {
485 VSIStatBufL sStat;
486 const char* pszDBFName =
487 CPLResetExtension(pszNewName,
488 (i == 0 ) ? "dbf" : "DBF");
489 VSILFILE* fp = nullptr;
490 if( VSIStatExL( pszDBFName, &sStat,
491 VSI_STAT_EXISTS_FLAG) == 0 )
492 {
493 fp = VSIFOpenL(pszDBFName, "r+");
494 if( fp == nullptr )
495 {
496 CPLError(
497 CE_Failure, CPLE_OpenFailed,
498 "%s exists, "
499 "but cannot be opened in update mode",
500 pszDBFName );
501 SHPClose(hSHP);
502 return false;
503 }
504 VSIFCloseL(fp);
505 break;
506 }
507 }
508 }
509 }
510 else
511 {
512 hDBF = DS_DBFOpen( pszNewName, "r" );
513 }
514 }
515 else
516 {
517 hDBF = nullptr;
518 }
519
520 if( hDBF == nullptr && hSHP == nullptr )
521 return false;
522
523 /* -------------------------------------------------------------------- */
524 /* Create the layer object. */
525 /* -------------------------------------------------------------------- */
526 OGRShapeLayer *poLayer =
527 new OGRShapeLayer( this, pszNewName, hSHP, hDBF, nullptr, false, bUpdate,
528 wkbNone );
529 poLayer->SetModificationDate(
530 CSLFetchNameValue( papszOpenOptions, "DBF_DATE_LAST_UPDATE" ) );
531 poLayer->SetAutoRepack(
532 CPLFetchBool( papszOpenOptions, "AUTO_REPACK", true ) );
533 poLayer->SetWriteDBFEOFChar(
534 CPLFetchBool( papszOpenOptions, "DBF_EOF_CHAR", true ) );
535
536 /* -------------------------------------------------------------------- */
537 /* Add layer to data source layer list. */
538 /* -------------------------------------------------------------------- */
539 AddLayer(poLayer);
540
541 return true;
542 }
543
544 /************************************************************************/
545 /* AddLayer() */
546 /************************************************************************/
547
AddLayer(OGRShapeLayer * poLayer)548 void OGRShapeDataSource::AddLayer( OGRShapeLayer* poLayer )
549 {
550 papoLayers = reinterpret_cast<OGRShapeLayer **>(
551 CPLRealloc( papoLayers, sizeof(OGRShapeLayer *) * (nLayers+1) ) );
552 papoLayers[nLayers++] = poLayer;
553
554 // If we reach the limit, then register all the already opened layers
555 // Technically this code would not be necessary if there was not the
556 // following initial test in SetLastUsedLayer() :
557 // if (nLayers < MAX_SIMULTANEOUSLY_OPENED_LAYERS)
558 // return;
559 if( nLayers == poPool->GetMaxSimultaneouslyOpened() &&
560 poPool->GetSize() == 0 )
561 {
562 for( int i = 0; i < nLayers; i++ )
563 poPool->SetLastUsedLayer(papoLayers[i]);
564 }
565 }
566
567 /************************************************************************/
568 /* LaunderLayerName() */
569 /************************************************************************/
570
LaunderLayerName(const char * pszLayerName)571 static CPLString LaunderLayerName(const char* pszLayerName)
572 {
573 std::string osRet(CPLLaunderForFilename(pszLayerName, nullptr));
574 if( osRet != pszLayerName )
575 {
576 CPLError(CE_Warning, CPLE_AppDefined,
577 "Invalid layer name for a shapefile: %s. Laundered to %s.",
578 pszLayerName, osRet.c_str());
579 }
580 return osRet;
581 }
582
583 /************************************************************************/
584 /* ICreateLayer() */
585 /************************************************************************/
586
587 OGRLayer *
ICreateLayer(const char * pszLayerName,OGRSpatialReference * poSRS,OGRwkbGeometryType eType,char ** papszOptions)588 OGRShapeDataSource::ICreateLayer( const char * pszLayerName,
589 OGRSpatialReference *poSRS,
590 OGRwkbGeometryType eType,
591 char ** papszOptions )
592
593 {
594 // To ensure that existing layers are created.
595 GetLayerCount();
596
597 /* -------------------------------------------------------------------- */
598 /* Check that the layer doesn't already exist. */
599 /* -------------------------------------------------------------------- */
600 if (GetLayerByName(pszLayerName) != nullptr)
601 {
602 CPLError( CE_Failure, CPLE_AppDefined, "Layer '%s' already exists",
603 pszLayerName);
604 return nullptr;
605 }
606
607 /* -------------------------------------------------------------------- */
608 /* Verify we are in update mode. */
609 /* -------------------------------------------------------------------- */
610 if( !bDSUpdate )
611 {
612 CPLError( CE_Failure, CPLE_NoWriteAccess,
613 "Data source %s opened read-only. "
614 "New layer %s cannot be created.",
615 pszName, pszLayerName );
616
617 return nullptr;
618 }
619
620 if( m_bIsZip && m_bSingleLayerZip && nLayers == 1 )
621 {
622 CPLError( CE_Failure, CPLE_NotSupported,
623 ".shz only supports one single layer");
624 return nullptr;
625 }
626
627 if( !UncompressIfNeeded() )
628 return nullptr;
629
630 /* -------------------------------------------------------------------- */
631 /* Figure out what type of layer we need. */
632 /* -------------------------------------------------------------------- */
633 int nShapeType = -1;
634
635 if( wkbFlatten(eType) == wkbUnknown || eType == wkbLineString )
636 nShapeType = SHPT_ARC;
637 else if( eType == wkbPoint )
638 nShapeType = SHPT_POINT;
639 else if( eType == wkbPolygon || eType == wkbTriangle )
640 nShapeType = SHPT_POLYGON;
641 else if( eType == wkbMultiPoint )
642 nShapeType = SHPT_MULTIPOINT;
643 else if( eType == wkbPoint25D )
644 nShapeType = SHPT_POINTZ;
645 else if( eType == wkbPointM )
646 nShapeType = SHPT_POINTM;
647 else if( eType == wkbPointZM )
648 nShapeType = SHPT_POINTZ;
649 else if( eType == wkbLineString25D )
650 nShapeType = SHPT_ARCZ;
651 else if( eType == wkbLineStringM )
652 nShapeType = SHPT_ARCM;
653 else if( eType == wkbLineStringZM )
654 nShapeType = SHPT_ARCZ;
655 else if( eType == wkbMultiLineString )
656 nShapeType = SHPT_ARC;
657 else if( eType == wkbMultiLineString25D )
658 nShapeType = SHPT_ARCZ;
659 else if( eType == wkbMultiLineStringM )
660 nShapeType = SHPT_ARCM;
661 else if( eType == wkbMultiLineStringZM )
662 nShapeType = SHPT_ARCZ;
663 else if( eType == wkbPolygon25D || eType == wkbTriangleZ )
664 nShapeType = SHPT_POLYGONZ;
665 else if( eType == wkbPolygonM || eType == wkbTriangleM )
666 nShapeType = SHPT_POLYGONM;
667 else if( eType == wkbPolygonZM || eType == wkbTriangleZM )
668 nShapeType = SHPT_POLYGONZ;
669 else if( eType == wkbMultiPolygon )
670 nShapeType = SHPT_POLYGON;
671 else if( eType == wkbMultiPolygon25D )
672 nShapeType = SHPT_POLYGONZ;
673 else if( eType == wkbMultiPolygonM )
674 nShapeType = SHPT_POLYGONM;
675 else if( eType == wkbMultiPolygonZM )
676 nShapeType = SHPT_POLYGONZ;
677 else if( eType == wkbMultiPoint25D )
678 nShapeType = SHPT_MULTIPOINTZ;
679 else if( eType == wkbMultiPointM )
680 nShapeType = SHPT_MULTIPOINTM;
681 else if( eType == wkbMultiPointZM )
682 nShapeType = SHPT_MULTIPOINTZ;
683 else if( wkbFlatten(eType) == wkbTIN ||
684 wkbFlatten(eType) == wkbPolyhedralSurface )
685 nShapeType = SHPT_MULTIPATCH;
686 else if( eType == wkbNone )
687 nShapeType = SHPT_NULL;
688
689 /* -------------------------------------------------------------------- */
690 /* Has the application overridden this with a special creation */
691 /* option? */
692 /* -------------------------------------------------------------------- */
693 const char *pszOverride = CSLFetchNameValue( papszOptions, "SHPT" );
694
695 if( pszOverride == nullptr )
696 {
697 /* ignore */;
698 }
699 else if( EQUAL(pszOverride,"POINT") )
700 {
701 nShapeType = SHPT_POINT;
702 eType = wkbPoint;
703 }
704 else if( EQUAL(pszOverride,"ARC") )
705 {
706 nShapeType = SHPT_ARC;
707 eType = wkbLineString;
708 }
709 else if( EQUAL(pszOverride,"POLYGON") )
710 {
711 nShapeType = SHPT_POLYGON;
712 eType = wkbPolygon;
713 }
714 else if( EQUAL(pszOverride,"MULTIPOINT") )
715 {
716 nShapeType = SHPT_MULTIPOINT;
717 eType = wkbMultiPoint;
718 }
719 else if( EQUAL(pszOverride,"POINTZ") )
720 {
721 nShapeType = SHPT_POINTZ;
722 eType = wkbPoint25D;
723 }
724 else if( EQUAL(pszOverride,"ARCZ") )
725 {
726 nShapeType = SHPT_ARCZ;
727 eType = wkbLineString25D;
728 }
729 else if( EQUAL(pszOverride,"POLYGONZ") )
730 {
731 nShapeType = SHPT_POLYGONZ;
732 eType = wkbPolygon25D;
733 }
734 else if( EQUAL(pszOverride,"MULTIPOINTZ") )
735 {
736 nShapeType = SHPT_MULTIPOINTZ;
737 eType = wkbMultiPoint25D;
738 }
739 else if( EQUAL(pszOverride,"POINTM") )
740 {
741 nShapeType = SHPT_POINTM;
742 eType = wkbPointM;
743 }
744 else if( EQUAL(pszOverride,"ARCM") )
745 {
746 nShapeType = SHPT_ARCM;
747 eType = wkbLineStringM;
748 }
749 else if( EQUAL(pszOverride,"POLYGONM") )
750 {
751 nShapeType = SHPT_POLYGONM;
752 eType = wkbPolygonM;
753 }
754 else if( EQUAL(pszOverride,"MULTIPOINTM") )
755 {
756 nShapeType = SHPT_MULTIPOINTM;
757 eType = wkbMultiPointM;
758 }
759 else if( EQUAL(pszOverride,"POINTZM") )
760 {
761 nShapeType = SHPT_POINTZ;
762 eType = wkbPointZM;
763 }
764 else if( EQUAL(pszOverride,"ARCZM") )
765 {
766 nShapeType = SHPT_ARCZ;
767 eType = wkbLineStringZM;
768 }
769 else if( EQUAL(pszOverride,"POLYGONZM") )
770 {
771 nShapeType = SHPT_POLYGONZ;
772 eType = wkbPolygonZM;
773 }
774 else if( EQUAL(pszOverride,"MULTIPOINTZM") )
775 {
776 nShapeType = SHPT_MULTIPOINTZ;
777 eType = wkbMultiPointZM;
778 }
779 else if( EQUAL(pszOverride,"MULTIPATCH") )
780 {
781 nShapeType = SHPT_MULTIPATCH;
782 eType = wkbUnknown; // not ideal...
783 }
784 else if( EQUAL(pszOverride,"NONE") || EQUAL(pszOverride,"NULL") )
785 {
786 nShapeType = SHPT_NULL;
787 eType = wkbNone;
788 }
789 else
790 {
791 CPLError( CE_Failure, CPLE_NotSupported,
792 "Unknown SHPT value of `%s' passed to Shapefile layer"
793 "creation. Creation aborted.",
794 pszOverride );
795
796 return nullptr;
797 }
798
799 if( nShapeType == -1 )
800 {
801 CPLError( CE_Failure, CPLE_NotSupported,
802 "Geometry type of `%s' not supported in shapefiles. "
803 "Type can be overridden with a layer creation option "
804 "of SHPT=POINT/ARC/POLYGON/MULTIPOINT/POINTZ/ARCZ/POLYGONZ/"
805 "MULTIPOINTZ/MULTIPATCH.",
806 OGRGeometryTypeToName(eType) );
807 return nullptr;
808 }
809
810 /* -------------------------------------------------------------------- */
811 /* What filename do we use, excluding the extension? */
812 /* -------------------------------------------------------------------- */
813 char *pszFilenameWithoutExt = nullptr;
814
815 if( bSingleFileDataSource && nLayers == 0 )
816 {
817 char *pszPath = CPLStrdup(CPLGetPath(pszName));
818 char *pszFBasename = CPLStrdup(CPLGetBasename(pszName));
819
820 pszFilenameWithoutExt =
821 CPLStrdup(CPLFormFilename(pszPath, pszFBasename, nullptr));
822
823 CPLFree( pszFBasename );
824 CPLFree( pszPath );
825 }
826 else if( bSingleFileDataSource )
827 {
828 // This is a very weird use case : the user creates/open a datasource
829 // made of a single shapefile 'foo.shp' and wants to add a new layer
830 // to it, 'bar'. So we create a new shapefile 'bar.shp' in the same
831 // directory as 'foo.shp'
832 // So technically, we will not be any longer a single file
833 // datasource ... Ahem ahem.
834 char *pszPath = CPLStrdup(CPLGetPath(pszName));
835 pszFilenameWithoutExt =
836 CPLStrdup(CPLFormFilename(pszPath, LaunderLayerName(pszLayerName).c_str(), nullptr));
837 CPLFree( pszPath );
838 }
839 else
840 {
841 CPLString osDir( m_osTemporaryUnzipDir.empty() ? pszName : m_osTemporaryUnzipDir );
842 pszFilenameWithoutExt =
843 CPLStrdup(CPLFormFilename(osDir, LaunderLayerName(pszLayerName).c_str(), nullptr));
844 }
845
846 /* -------------------------------------------------------------------- */
847 /* Create the shapefile. */
848 /* -------------------------------------------------------------------- */
849 const bool l_b2GBLimit =
850 CPLTestBool(CSLFetchNameValueDef( papszOptions, "2GB_LIMIT", "FALSE" ));
851
852 SHPHandle hSHP = nullptr;
853
854 if( nShapeType != SHPT_NULL )
855 {
856 char *pszFilename =
857 CPLStrdup(CPLFormFilename( nullptr, pszFilenameWithoutExt, "shp" ));
858
859 hSHP = SHPCreateLL(
860 pszFilename, nShapeType,
861 const_cast<SAHooks *>(VSI_SHP_GetHook(l_b2GBLimit)) );
862
863 if( hSHP == nullptr )
864 {
865 CPLFree( pszFilename );
866 CPLFree( pszFilenameWithoutExt );
867 return nullptr;
868 }
869
870 SHPSetFastModeReadObject( hSHP, TRUE );
871
872 CPLFree( pszFilename );
873 }
874
875 /* -------------------------------------------------------------------- */
876 /* Has a specific LDID been specified by the caller? */
877 /* -------------------------------------------------------------------- */
878 const char *pszLDID = CSLFetchNameValue( papszOptions, "ENCODING" );
879
880 /* -------------------------------------------------------------------- */
881 /* Create a DBF file. */
882 /* -------------------------------------------------------------------- */
883 char *pszFilename =
884 CPLStrdup(CPLFormFilename( nullptr, pszFilenameWithoutExt, "dbf" ));
885
886 DBFHandle hDBF =
887 DBFCreateLL( pszFilename, (pszLDID != nullptr) ? pszLDID : "LDID/87",
888 const_cast<SAHooks *>(VSI_SHP_GetHook(b2GBLimit)) );
889
890 if( hDBF == nullptr )
891 {
892 CPLError( CE_Failure, CPLE_OpenFailed,
893 "Failed to open Shape DBF file `%s'.",
894 pszFilename );
895 CPLFree( pszFilename );
896 CPLFree( pszFilenameWithoutExt );
897 SHPClose(hSHP);
898 return nullptr;
899 }
900
901 CPLFree( pszFilename );
902
903 /* -------------------------------------------------------------------- */
904 /* Create the .prj file, if required. */
905 /* -------------------------------------------------------------------- */
906 if( poSRS != nullptr )
907 {
908 CPLString osPrjFile =
909 CPLFormFilename( nullptr, pszFilenameWithoutExt, "prj");
910
911 poSRS = poSRS->Clone();
912 poSRS->morphToESRI();
913
914 char *pszWKT = nullptr;
915 VSILFILE *fp = nullptr;
916 if( poSRS->exportToWkt( &pszWKT ) == OGRERR_NONE
917 && (fp = VSIFOpenL( osPrjFile, "wt" )) != nullptr )
918 {
919 VSIFWriteL( pszWKT, strlen(pszWKT), 1, fp );
920 VSIFCloseL( fp );
921 }
922
923 CPLFree( pszWKT );
924
925 poSRS->morphFromESRI();
926 }
927
928 /* -------------------------------------------------------------------- */
929 /* Create the layer object. */
930 /* -------------------------------------------------------------------- */
931 // OGRShapeLayer constructor expects a filename with an extension (that
932 // could be random actually), otherwise this is going to cause problems with
933 // layer names that have a dot (not speaking about the one before the shp)
934 pszFilename =
935 CPLStrdup(CPLFormFilename( nullptr, pszFilenameWithoutExt, "shp" ));
936
937 OGRShapeLayer *poLayer =
938 new OGRShapeLayer( this, pszFilename, hSHP, hDBF, poSRS,
939 true, true, eType );
940 if( poSRS != nullptr )
941 {
942 poSRS->Release();
943 }
944
945 CPLFree( pszFilenameWithoutExt );
946 CPLFree( pszFilename );
947
948 poLayer->SetResizeAtClose(
949 CPLFetchBool( papszOptions, "RESIZE", false ) );
950 poLayer->CreateSpatialIndexAtClose(
951 CPLFetchBool( papszOptions, "SPATIAL_INDEX", false ) );
952 poLayer->SetModificationDate(
953 CSLFetchNameValue( papszOptions, "DBF_DATE_LAST_UPDATE" ) );
954 poLayer->SetAutoRepack(
955 CPLFetchBool( papszOptions, "AUTO_REPACK", true ) );
956 poLayer->SetWriteDBFEOFChar(
957 CPLFetchBool( papszOptions, "DBF_EOF_CHAR", true ) );
958
959 /* -------------------------------------------------------------------- */
960 /* Add layer to data source layer list. */
961 /* -------------------------------------------------------------------- */
962 AddLayer(poLayer);
963
964 return poLayer;
965 }
966
967 /************************************************************************/
968 /* TestCapability() */
969 /************************************************************************/
970
TestCapability(const char * pszCap)971 int OGRShapeDataSource::TestCapability( const char * pszCap )
972
973 {
974 if( EQUAL(pszCap,ODsCCreateLayer) )
975 return bDSUpdate && !(m_bIsZip && m_bSingleLayerZip && nLayers == 1);
976 if( EQUAL(pszCap,ODsCDeleteLayer) )
977 return bDSUpdate && !(m_bIsZip && m_bSingleLayerZip);
978 if( EQUAL(pszCap,ODsCMeasuredGeometries) )
979 return TRUE;
980 if( EQUAL(pszCap,ODsCRandomLayerWrite) )
981 return bDSUpdate;
982
983 return FALSE;
984 }
985
986 /************************************************************************/
987 /* GetLayerCount() */
988 /************************************************************************/
989
GetLayerCount()990 int OGRShapeDataSource::GetLayerCount()
991
992 {
993 #ifndef IMMEDIATE_OPENING
994 if( !oVectorLayerName.empty() )
995 {
996 for( size_t i = 0; i < oVectorLayerName.size(); i++ )
997 {
998 const char* pszFilename = oVectorLayerName[i].c_str();
999 const char* pszLayerName = CPLGetBasename(pszFilename);
1000
1001 int j = 0; // Used after for.
1002 for( ; j < nLayers; j++ )
1003 {
1004 if( strcmp(papoLayers[j]->GetName(), pszLayerName) == 0 )
1005 break;
1006 }
1007 if( j < nLayers )
1008 continue;
1009
1010 if( !OpenFile( pszFilename, bDSUpdate ) )
1011 {
1012 CPLError( CE_Failure, CPLE_OpenFailed,
1013 "Failed to open file %s."
1014 "It may be corrupt or read-only file accessed in "
1015 "update mode.",
1016 pszFilename );
1017 }
1018 }
1019 oVectorLayerName.resize(0);
1020 }
1021 #endif
1022
1023 return nLayers;
1024 }
1025
1026 /************************************************************************/
1027 /* GetLayer() */
1028 /************************************************************************/
1029
GetLayer(int iLayer)1030 OGRLayer *OGRShapeDataSource::GetLayer( int iLayer )
1031
1032 {
1033 // To ensure that existing layers are created.
1034 GetLayerCount();
1035
1036 if( iLayer < 0 || iLayer >= nLayers )
1037 return nullptr;
1038
1039 return papoLayers[iLayer];
1040 }
1041
1042 /************************************************************************/
1043 /* GetLayerByName() */
1044 /************************************************************************/
1045
GetLayerByName(const char * pszLayerNameIn)1046 OGRLayer *OGRShapeDataSource::GetLayerByName( const char * pszLayerNameIn )
1047 {
1048 #ifndef IMMEDIATE_OPENING
1049 if( !oVectorLayerName.empty() )
1050 {
1051 for( int j = 0; j < nLayers; j++ )
1052 {
1053 if (strcmp(papoLayers[j]->GetName(), pszLayerNameIn) == 0)
1054 {
1055 return papoLayers[j];
1056 }
1057 }
1058
1059 for( int j = 0; j < 2; j++ )
1060 {
1061 for( size_t i = 0; i < oVectorLayerName.size(); i++ )
1062 {
1063 const char* pszFilename = oVectorLayerName[i].c_str();
1064 const char* pszLayerName = CPLGetBasename(pszFilename);
1065
1066 if( j == 0 )
1067 {
1068 if(strcmp(pszLayerName, pszLayerNameIn) != 0)
1069 continue;
1070 }
1071 else
1072 {
1073 if( !EQUAL(pszLayerName, pszLayerNameIn) )
1074 continue;
1075 }
1076
1077 if( !OpenFile( pszFilename, bDSUpdate ) )
1078 {
1079 CPLError( CE_Failure, CPLE_OpenFailed,
1080 "Failed to open file %s. "
1081 "It may be corrupt or read-only file accessed in "
1082 "update mode.",
1083 pszFilename );
1084 return nullptr;
1085 }
1086
1087 return papoLayers[nLayers - 1];
1088 }
1089 }
1090
1091 return nullptr;
1092 }
1093 #endif
1094
1095 return OGRDataSource::GetLayerByName(pszLayerNameIn);
1096 }
1097
1098 /************************************************************************/
1099 /* ExecuteSQL() */
1100 /* */
1101 /* We override this to provide special handling of CREATE */
1102 /* SPATIAL INDEX commands. Support forms are: */
1103 /* */
1104 /* CREATE SPATIAL INDEX ON layer_name [DEPTH n] */
1105 /* DROP SPATIAL INDEX ON layer_name */
1106 /* REPACK layer_name */
1107 /* RECOMPUTE EXTENT ON layer_name */
1108 /************************************************************************/
1109
ExecuteSQL(const char * pszStatement,OGRGeometry * poSpatialFilter,const char * pszDialect)1110 OGRLayer * OGRShapeDataSource::ExecuteSQL( const char *pszStatement,
1111 OGRGeometry *poSpatialFilter,
1112 const char *pszDialect )
1113
1114 {
1115 if( EQUAL(pszStatement, "UNCOMPRESS") )
1116 {
1117 UncompressIfNeeded();
1118 return nullptr;
1119 }
1120
1121 if( EQUAL(pszStatement, "RECOMPRESS") )
1122 {
1123 RecompressIfNeeded(GetLayerNames());
1124 return nullptr;
1125 }
1126 /* ==================================================================== */
1127 /* Handle command to drop a spatial index. */
1128 /* ==================================================================== */
1129 if( STARTS_WITH_CI(pszStatement, "REPACK ") )
1130 {
1131 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1132 GetLayerByName( pszStatement + 7 ));
1133
1134 if( poLayer != nullptr )
1135 {
1136 if( poLayer->Repack() != OGRERR_NONE )
1137 {
1138 CPLError( CE_Failure, CPLE_AppDefined,
1139 "REPACK of layer '%s' failed.",
1140 pszStatement + 7 );
1141 }
1142 }
1143 else
1144 {
1145 CPLError( CE_Failure, CPLE_AppDefined,
1146 "No such layer as '%s' in REPACK.",
1147 pszStatement + 7 );
1148 }
1149 return nullptr;
1150 }
1151
1152 /* ==================================================================== */
1153 /* Handle command to shrink columns to their minimum size. */
1154 /* ==================================================================== */
1155 if( STARTS_WITH_CI(pszStatement, "RESIZE ") )
1156 {
1157 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1158 GetLayerByName( pszStatement + 7 ));
1159
1160 if( poLayer != nullptr )
1161 {
1162 poLayer->ResizeDBF();
1163 }
1164 else
1165 {
1166 CPLError( CE_Failure, CPLE_AppDefined,
1167 "No such layer as '%s' in RESIZE.",
1168 pszStatement + 7 );
1169 }
1170 return nullptr;
1171 }
1172
1173 /* ==================================================================== */
1174 /* Handle command to recompute extent */
1175 /* ==================================================================== */
1176 if( STARTS_WITH_CI(pszStatement, "RECOMPUTE EXTENT ON ") )
1177 {
1178 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1179 GetLayerByName( pszStatement + 20 ));
1180
1181 if( poLayer != nullptr )
1182 {
1183 poLayer->RecomputeExtent();
1184 }
1185 else
1186 {
1187 CPLError( CE_Failure, CPLE_AppDefined,
1188 "No such layer as '%s' in RECOMPUTE EXTENT.",
1189 pszStatement + 20 );
1190 }
1191 return nullptr;
1192 }
1193
1194 /* ==================================================================== */
1195 /* Handle command to drop a spatial index. */
1196 /* ==================================================================== */
1197 if( STARTS_WITH_CI(pszStatement, "DROP SPATIAL INDEX ON ") )
1198 {
1199 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1200 GetLayerByName( pszStatement + 22 ));
1201
1202 if( poLayer != nullptr )
1203 {
1204 poLayer->DropSpatialIndex();
1205 }
1206 else
1207 {
1208 CPLError( CE_Failure, CPLE_AppDefined,
1209 "No such layer as '%s' in DROP SPATIAL INDEX.",
1210 pszStatement + 22 );
1211 }
1212 return nullptr;
1213 }
1214
1215 /* ==================================================================== */
1216 /* Handle all commands except spatial index creation generically. */
1217 /* ==================================================================== */
1218 if( !STARTS_WITH_CI(pszStatement, "CREATE SPATIAL INDEX ON ") )
1219 {
1220 char **papszTokens = CSLTokenizeString( pszStatement );
1221 if( CSLCount(papszTokens) >=4
1222 && (EQUAL(papszTokens[0],"CREATE") || EQUAL(papszTokens[0],"DROP"))
1223 && EQUAL(papszTokens[1],"INDEX")
1224 && EQUAL(papszTokens[2],"ON") )
1225 {
1226 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1227 GetLayerByName(papszTokens[3]));
1228 if( poLayer != nullptr )
1229 poLayer->InitializeIndexSupport( poLayer->GetFullName() );
1230 }
1231 CSLDestroy( papszTokens );
1232
1233 return OGRDataSource::ExecuteSQL( pszStatement, poSpatialFilter,
1234 pszDialect );
1235 }
1236
1237 /* -------------------------------------------------------------------- */
1238 /* Parse into keywords. */
1239 /* -------------------------------------------------------------------- */
1240 char **papszTokens = CSLTokenizeString( pszStatement );
1241
1242 if( CSLCount(papszTokens) < 5
1243 || !EQUAL(papszTokens[0],"CREATE")
1244 || !EQUAL(papszTokens[1],"SPATIAL")
1245 || !EQUAL(papszTokens[2],"INDEX")
1246 || !EQUAL(papszTokens[3],"ON")
1247 || CSLCount(papszTokens) > 7
1248 || (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5],"DEPTH")) )
1249 {
1250 CSLDestroy( papszTokens );
1251 CPLError( CE_Failure, CPLE_AppDefined,
1252 "Syntax error in CREATE SPATIAL INDEX command.\n"
1253 "Was '%s'\n"
1254 "Should be of form 'CREATE SPATIAL INDEX ON <table> "
1255 "[DEPTH <n>]'",
1256 pszStatement );
1257 return nullptr;
1258 }
1259
1260 /* -------------------------------------------------------------------- */
1261 /* Get depth if provided. */
1262 /* -------------------------------------------------------------------- */
1263 const int nDepth =
1264 CSLCount(papszTokens) == 7 ? atoi(papszTokens[6]) : 0;
1265
1266 /* -------------------------------------------------------------------- */
1267 /* What layer are we operating on. */
1268 /* -------------------------------------------------------------------- */
1269 OGRShapeLayer *poLayer = cpl::down_cast<OGRShapeLayer *>(
1270 GetLayerByName(papszTokens[4]));
1271
1272 if( poLayer == nullptr )
1273 {
1274 CPLError( CE_Failure, CPLE_AppDefined,
1275 "Layer %s not recognised.",
1276 papszTokens[4] );
1277 CSLDestroy( papszTokens );
1278 return nullptr;
1279 }
1280
1281 CSLDestroy( papszTokens );
1282
1283 poLayer->CreateSpatialIndex( nDepth );
1284 return nullptr;
1285 }
1286
1287 /************************************************************************/
1288 /* GetExtensionsForDeletion() */
1289 /************************************************************************/
1290
GetExtensionsForDeletion()1291 const char* const* OGRShapeDataSource::GetExtensionsForDeletion()
1292 {
1293 static const char * const apszExtensions[] =
1294 { "shp", "shx", "dbf", "sbn", "sbx", "prj", "idm", "ind",
1295 "qix", "cpg",
1296 "qpj", // QGIS projection file
1297 nullptr };
1298 return apszExtensions;
1299 }
1300
1301 /************************************************************************/
1302 /* DeleteLayer() */
1303 /************************************************************************/
1304
DeleteLayer(int iLayer)1305 OGRErr OGRShapeDataSource::DeleteLayer( int iLayer )
1306
1307 {
1308 /* -------------------------------------------------------------------- */
1309 /* Verify we are in update mode. */
1310 /* -------------------------------------------------------------------- */
1311 if( !bDSUpdate )
1312 {
1313 CPLError( CE_Failure, CPLE_NoWriteAccess,
1314 "Data source %s opened read-only. "
1315 "Layer %d cannot be deleted.",
1316 pszName, iLayer );
1317
1318 return OGRERR_FAILURE;
1319 }
1320
1321 // To ensure that existing layers are created.
1322 GetLayerCount();
1323
1324 if( iLayer < 0 || iLayer >= nLayers )
1325 {
1326 CPLError( CE_Failure, CPLE_AppDefined,
1327 "Layer %d not in legal range of 0 to %d.",
1328 iLayer, nLayers-1 );
1329 return OGRERR_FAILURE;
1330 }
1331
1332 if( m_bIsZip && m_bSingleLayerZip )
1333 {
1334 CPLError( CE_Failure, CPLE_NotSupported,
1335 ".shz does not support layer deletion");
1336 return OGRERR_FAILURE;
1337 }
1338
1339 if( !UncompressIfNeeded() )
1340 return OGRERR_FAILURE;
1341
1342 OGRShapeLayer* poLayerToDelete = papoLayers[iLayer];
1343
1344 char * const pszFilename = CPLStrdup(poLayerToDelete->GetFullName());
1345
1346 delete poLayerToDelete;
1347
1348 while( iLayer < nLayers - 1 )
1349 {
1350 papoLayers[iLayer] = papoLayers[iLayer+1];
1351 iLayer++;
1352 }
1353
1354 nLayers--;
1355
1356 const char * const* papszExtensions =
1357 OGRShapeDataSource::GetExtensionsForDeletion();
1358 for( int iExt = 0; papszExtensions[iExt] != nullptr; iExt++ )
1359 {
1360 const char *pszFile = CPLResetExtension(pszFilename,
1361 papszExtensions[iExt]);
1362 VSIStatBufL sStatBuf;
1363 if( VSIStatL( pszFile, &sStatBuf ) == 0 )
1364 VSIUnlink( pszFile );
1365 }
1366
1367 CPLFree( pszFilename );
1368
1369 return OGRERR_NONE;
1370 }
1371
1372 /************************************************************************/
1373 /* SetLastUsedLayer() */
1374 /************************************************************************/
1375
SetLastUsedLayer(OGRShapeLayer * poLayer)1376 void OGRShapeDataSource::SetLastUsedLayer( OGRShapeLayer* poLayer )
1377 {
1378 // We could remove that check and things would still work in
1379 // 99.99% cases.
1380 // The only rationale for that test is to avoid breaking applications that
1381 // would deal with layers of the same datasource in different threads. In
1382 // GDAL < 1.9.0, this would work in most cases I can imagine as shapefile
1383 // layers are pretty much independent from each others (although it has
1384 // never been guaranteed to be a valid use case, and the shape driver is
1385 // likely more the exception than the rule in permitting accessing layers
1386 // from different threads !) Anyway the LRU list mechanism leaves the door
1387 // open to concurrent accesses to it so when the datasource has not many
1388 // layers, we don't try to build the LRU list to avoid concurrency issues. I
1389 // haven't bothered making the analysis of how a mutex could be used to
1390 // protect that (my intuition is that it would need to be placed at the
1391 // beginning of OGRShapeLayer::TouchLayer() ).
1392 if (nLayers < poPool->GetMaxSimultaneouslyOpened())
1393 return;
1394
1395 poPool->SetLastUsedLayer(poLayer);
1396 }
1397
1398 /************************************************************************/
1399 // GetFileList() */
1400 /************************************************************************/
1401
GetFileList()1402 char** OGRShapeDataSource::GetFileList()
1403 {
1404 if( m_bIsZip )
1405 {
1406 return CSLAddString(nullptr, pszName);
1407 }
1408 CPLStringList oFileList;
1409 GetLayerCount();
1410 for( int i = 0; i < nLayers; i++ )
1411 {
1412 OGRShapeLayer* poLayer = papoLayers[i];
1413 poLayer->AddToFileList(oFileList);
1414 }
1415 return oFileList.StealList();
1416 }
1417
1418 /************************************************************************/
1419 // RefreshLockFile() */
1420 /************************************************************************/
1421
RefreshLockFile(void * _self)1422 void OGRShapeDataSource::RefreshLockFile(void* _self)
1423 {
1424 OGRShapeDataSource* self = static_cast<OGRShapeDataSource*>(_self);
1425 CPLAssert(self->m_psLockFile);
1426 CPLAcquireMutex(self->m_poRefreshLockFileMutex, 1000);
1427 CPLCondSignal(self->m_poRefreshLockFileCond);
1428 unsigned int nInc = 0;
1429 while(!(self->m_bExitRefreshLockFileThread))
1430 {
1431 auto ret = CPLCondTimedWait(self->m_poRefreshLockFileCond,
1432 self->m_poRefreshLockFileMutex,
1433 self->m_dfRefreshLockDelay);
1434 if( ret == COND_TIMED_WAIT_TIME_OUT )
1435 {
1436 CPLAssert(self->m_psLockFile);
1437 VSIFSeekL(self->m_psLockFile, 0, SEEK_SET);
1438 CPLString osTime;
1439 nInc++;
1440 osTime.Printf(CPL_FRMT_GUIB ", %u\n",
1441 static_cast<GUIntBig>(time(nullptr)),
1442 nInc);
1443 VSIFWriteL(osTime.data(), 1, osTime.size(), self->m_psLockFile);
1444 VSIFFlushL(self->m_psLockFile);
1445 }
1446 }
1447 CPLReleaseMutex(self->m_poRefreshLockFileMutex);
1448 }
1449
1450 /************************************************************************/
1451 // RemoveLockFile() */
1452 /************************************************************************/
1453
RemoveLockFile()1454 void OGRShapeDataSource::RemoveLockFile()
1455 {
1456 if( !m_psLockFile )
1457 return;
1458
1459 // Ask the thread to terminate
1460 CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1461 m_bExitRefreshLockFileThread = true;
1462 CPLCondSignal(m_poRefreshLockFileCond);
1463 CPLReleaseMutex(m_poRefreshLockFileMutex);
1464 CPLJoinThread(m_hRefreshLockFileThread);
1465 m_hRefreshLockFileThread = nullptr;
1466
1467 // Close and remove lock file
1468 VSIFCloseL(m_psLockFile);
1469 m_psLockFile = nullptr;
1470 CPLString osLockFile(pszName);
1471 osLockFile += ".gdal.lock";
1472 VSIUnlink(osLockFile);
1473 }
1474
1475 /************************************************************************/
1476 // UncompressIfNeeded() */
1477 /************************************************************************/
1478
UncompressIfNeeded()1479 bool OGRShapeDataSource::UncompressIfNeeded()
1480 {
1481 if( !bDSUpdate || !m_bIsZip || !m_osTemporaryUnzipDir.empty() )
1482 return true;
1483
1484 GetLayerCount();
1485
1486 auto returnError = [this]()
1487 {
1488 CPLError(CE_Failure, CPLE_AppDefined, "Cannot uncompress %s", pszName);
1489 return false;
1490 };
1491
1492 if( nLayers > 1 )
1493 {
1494 CPLString osLockFile(pszName);
1495 osLockFile += ".gdal.lock";
1496 VSIStatBufL sStat;
1497 if( VSIStatL(osLockFile, &sStat) == 0 &&
1498 sStat.st_mtime > time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC )
1499 {
1500 CPLError(CE_Failure, CPLE_AppDefined,
1501 "Cannot edit %s. Another task is editing it", pszName);
1502 return false;
1503 }
1504 if( !m_poRefreshLockFileMutex )
1505 {
1506 m_poRefreshLockFileMutex = CPLCreateMutex();
1507 if( !m_poRefreshLockFileMutex )
1508 return false;
1509 CPLReleaseMutex(m_poRefreshLockFileMutex);
1510 }
1511 if( !m_poRefreshLockFileCond )
1512 {
1513 m_poRefreshLockFileCond = CPLCreateCond();
1514 if( !m_poRefreshLockFileCond )
1515 return false;
1516 }
1517 auto f = VSIFOpenL(osLockFile, "wb");
1518 if( !f )
1519 {
1520 CPLError(CE_Failure, CPLE_AppDefined,
1521 "Cannot create lock file");
1522 return false;
1523 }
1524 m_psLockFile = f;
1525 m_bExitRefreshLockFileThread = false;
1526 // Config option mostly for testing purposes
1527 // coverity[tainted_data]
1528 m_dfRefreshLockDelay = CPLAtof(
1529 CPLGetConfigOption("OGR_SHAPE_LOCK_DELAY",
1530 CPLSPrintf("%d", knREFRESH_LOCK_FILE_DELAY_SEC)));
1531 m_hRefreshLockFileThread = CPLCreateJoinableThread(
1532 OGRShapeDataSource::RefreshLockFile, this);
1533 if( !m_hRefreshLockFileThread )
1534 {
1535 VSIFCloseL(m_psLockFile);
1536 m_psLockFile = nullptr;
1537 VSIUnlink(osLockFile);
1538 }
1539 else
1540 {
1541 CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1542 CPLCondWait(m_poRefreshLockFileCond, m_poRefreshLockFileMutex);
1543 CPLReleaseMutex(m_poRefreshLockFileMutex);
1544 }
1545 }
1546
1547 CPLString osVSIZipDirname(GetVSIZipPrefixeDir());
1548 vsi_l_offset nTotalUncompressedSize = 0;
1549 CPLStringList aosFiles( VSIReadDir( osVSIZipDirname ));
1550 for( int i = 0; i < aosFiles.size(); i++ )
1551 {
1552 const char* pszFilename = aosFiles[i];
1553 if( !EQUAL(pszFilename, ".") && !EQUAL(pszFilename, "..") )
1554 {
1555 CPLString osSrcFile( CPLFormFilename(
1556 osVSIZipDirname, pszFilename, nullptr) );
1557 VSIStatBufL sStat;
1558 if( VSIStatL(osSrcFile, &sStat) == 0 )
1559 {
1560 nTotalUncompressedSize += sStat.st_size;
1561 }
1562 }
1563 }
1564
1565 CPLString osTemporaryDir(pszName);
1566 osTemporaryDir += "_tmp_uncompressed";
1567
1568 const char* pszUseVsimem = CPLGetConfigOption("OGR_SHAPE_USE_VSIMEM_FOR_TEMP", "AUTO");
1569 if( EQUAL(pszUseVsimem, "YES") ||
1570 (EQUAL(pszUseVsimem, "AUTO") &&
1571 nTotalUncompressedSize > 0 &&
1572 nTotalUncompressedSize < static_cast<GUIntBig>(CPLGetUsablePhysicalRAM() / 10)) )
1573 {
1574 osTemporaryDir = CPLSPrintf("/vsimem/_shapedriver/%p", this);
1575 }
1576 CPLDebug("Shape", "Uncompressing to %s", osTemporaryDir.c_str());
1577
1578 VSIRmdirRecursive(osTemporaryDir);
1579 if( VSIMkdir(osTemporaryDir, 0755) != 0 )
1580 return returnError();
1581 for( int i = 0; i < aosFiles.size(); i++ )
1582 {
1583 const char* pszFilename = aosFiles[i];
1584 if( !EQUAL(pszFilename, ".") && !EQUAL(pszFilename, "..") )
1585 {
1586 CPLString osSrcFile( CPLFormFilename(
1587 osVSIZipDirname, pszFilename, nullptr) );
1588 CPLString osDestFile( CPLFormFilename(
1589 osTemporaryDir, pszFilename, nullptr) );
1590 if( CPLCopyFile(osDestFile, osSrcFile) != 0 )
1591 {
1592 VSIRmdirRecursive(osTemporaryDir);
1593 return returnError();
1594 }
1595 }
1596 }
1597
1598 m_osTemporaryUnzipDir = osTemporaryDir;
1599
1600 for( int i = 0; i < nLayers; i++ )
1601 {
1602 OGRShapeLayer* poLayer = papoLayers[i];
1603 poLayer->UpdateFollowingDeOrRecompression();
1604 }
1605
1606 return true;
1607 }
1608
1609 /************************************************************************/
1610 // RecompressIfNeeded() */
1611 /************************************************************************/
1612
RecompressIfNeeded(const std::vector<CPLString> & layerNames)1613 bool OGRShapeDataSource::RecompressIfNeeded(const std::vector<CPLString>& layerNames)
1614 {
1615 if( !bDSUpdate || !m_bIsZip || m_osTemporaryUnzipDir.empty() )
1616 return true;
1617
1618 auto returnError = [this]()
1619 {
1620 CPLError(CE_Failure, CPLE_AppDefined, "Cannot recompress %s", pszName);
1621 RemoveLockFile();
1622 return false;
1623 };
1624
1625 CPLStringList aosFiles( VSIReadDir( m_osTemporaryUnzipDir ));
1626 CPLString osTmpZip(m_osTemporaryUnzipDir + ".zip");
1627 VSIUnlink(osTmpZip);
1628 CPLString osTmpZipWithVSIZip( "/vsizip/{" + osTmpZip + '}' );
1629
1630 std::map<CPLString, int> oMapLayerOrder;
1631 for( size_t i = 0; i < layerNames.size(); i++ )
1632 oMapLayerOrder[layerNames[i]] = static_cast<int>(i);
1633
1634 std::vector<CPLString> sortedFiles;
1635 vsi_l_offset nTotalUncompressedSize = 0;
1636 for(int i = 0; i < aosFiles.size(); i++ )
1637 {
1638 sortedFiles.emplace_back(aosFiles[i]);
1639 CPLString osSrcFile( CPLFormFilename(
1640 m_osTemporaryUnzipDir, aosFiles[i], nullptr) );
1641 VSIStatBufL sStat;
1642 if( VSIStatL(osSrcFile, &sStat) == 0 )
1643 {
1644 nTotalUncompressedSize += sStat.st_size;
1645 }
1646 }
1647
1648 // Sort files by their layer orders, and then for files of the same layer,
1649 // make shp appear first, and then by filename order
1650 std::sort(sortedFiles.begin(), sortedFiles.end(),
1651 [&oMapLayerOrder](const CPLString& a, const CPLString& b)
1652 {
1653 int iA = INT_MAX;
1654 auto oIterA = oMapLayerOrder.find(CPLGetBasename(a));
1655 if( oIterA != oMapLayerOrder.end() )
1656 iA = oIterA->second;
1657 int iB = INT_MAX;
1658 auto oIterB = oMapLayerOrder.find(CPLGetBasename(b));
1659 if( oIterB != oMapLayerOrder.end() )
1660 iB = oIterB->second;
1661 if( iA < iB )
1662 return true;
1663 if( iA > iB )
1664 return false;
1665 if( iA != INT_MAX )
1666 {
1667 const char* pszExtA = CPLGetExtension(a);
1668 const char* pszExtB = CPLGetExtension(b);
1669 if( EQUAL(pszExtA, "shp") )
1670 return true;
1671 if( EQUAL(pszExtB, "shp") )
1672 return false;
1673 }
1674 return a < b;
1675 }
1676 );
1677
1678 CPLConfigOptionSetter oZIP64Setter(
1679 "CPL_CREATE_ZIP64",
1680 nTotalUncompressedSize < 4000U * 1000 * 1000 ? "NO" : "YES", true);
1681
1682 /* Maintain a handle on the ZIP opened */
1683 VSILFILE* fpZIP = VSIFOpenExL(osTmpZipWithVSIZip, "wb", true);
1684 if (fpZIP == nullptr)
1685 {
1686 CPLError(CE_Failure, CPLE_FileIO,
1687 "Cannot create %s: %s", osTmpZipWithVSIZip.c_str(),
1688 VSIGetLastErrorMsg());
1689 return returnError();
1690 }
1691
1692 for( const auto& osFilename: sortedFiles )
1693 {
1694 const char* pszFilename = osFilename.c_str();
1695 if( !EQUAL(pszFilename, ".") && !EQUAL(pszFilename, "..") )
1696 {
1697 CPLString osSrcFile( CPLFormFilename(
1698 m_osTemporaryUnzipDir, pszFilename, nullptr) );
1699 CPLString osDestFile( CPLFormFilename(
1700 osTmpZipWithVSIZip, pszFilename, nullptr) );
1701 if( CPLCopyFile(osDestFile, osSrcFile) != 0 )
1702 {
1703 VSIFCloseL(fpZIP);
1704 return returnError();
1705 }
1706 }
1707 }
1708
1709 VSIFCloseL(fpZIP);
1710
1711 const bool bOverwrite =
1712 CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
1713 #ifdef WIN32
1714 "YES"
1715 #else
1716 "NO"
1717 #endif
1718 ));
1719 if( bOverwrite )
1720 {
1721 VSILFILE* fpTarget = nullptr;
1722 for( int i = 0; i < 10; i++ )
1723 {
1724 fpTarget = VSIFOpenL(pszName, "rb+");
1725 if( fpTarget )
1726 break;
1727 CPLSleep(0.1);
1728 }
1729 if( !fpTarget )
1730 return returnError();
1731 bool bCopyOK = CopyInPlace(fpTarget, osTmpZip);
1732 VSIFCloseL(fpTarget);
1733 VSIUnlink(osTmpZip);
1734 if( !bCopyOK )
1735 {
1736 return returnError();
1737 }
1738 }
1739 else
1740 {
1741 if( VSIUnlink(pszName) != 0 ||
1742 CPLMoveFile(pszName, osTmpZip) != 0 )
1743 {
1744 return returnError();
1745 }
1746 }
1747
1748 VSIRmdirRecursive(m_osTemporaryUnzipDir);
1749 m_osTemporaryUnzipDir.clear();
1750
1751 for( int i = 0; i < nLayers; i++ )
1752 {
1753 OGRShapeLayer* poLayer = papoLayers[i];
1754 poLayer->UpdateFollowingDeOrRecompression();
1755 }
1756
1757 RemoveLockFile();
1758
1759 return true;
1760 }
1761
1762 /************************************************************************/
1763 /* CopyInPlace() */
1764 /************************************************************************/
1765
CopyInPlace(VSILFILE * fpTarget,const CPLString & osSourceFilename)1766 bool OGRShapeDataSource::CopyInPlace( VSILFILE* fpTarget,
1767 const CPLString& osSourceFilename )
1768 {
1769 return CPL_TO_BOOL(VSIOverwriteFile(fpTarget, osSourceFilename.c_str()));
1770 }
1771