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