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 9201 : SHPHandle OGRShapeDataSource::DS_SHPOpen(const char *pszShapeFile,
49 : const char *pszAccess)
50 : {
51 : // Do lazy shx loading for /vsicurl/
52 9201 : if (STARTS_WITH(pszShapeFile, "/vsicurl/") && strcmp(pszAccess, "r") == 0)
53 0 : pszAccess = "rl";
54 :
55 : const bool bRestoreSHX =
56 9201 : CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
57 9201 : SHPHandle hSHP = SHPOpenLLEx(
58 : pszShapeFile, pszAccess,
59 9201 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)), bRestoreSHX);
60 :
61 9201 : if (hSHP != nullptr)
62 8474 : SHPSetFastModeReadObject(hSHP, TRUE);
63 9201 : return hSHP;
64 : }
65 :
66 : /************************************************************************/
67 : /* DS_DBFOpen() */
68 : /************************************************************************/
69 :
70 9199 : DBFHandle OGRShapeDataSource::DS_DBFOpen(const char *pszDBFFile,
71 : const char *pszAccess)
72 : {
73 : DBFHandle hDBF =
74 9199 : DBFOpenLL(pszDBFFile, pszAccess,
75 9199 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
76 9199 : return hDBF;
77 : }
78 :
79 : /************************************************************************/
80 : /* OGRShapeDataSource() */
81 : /************************************************************************/
82 :
83 3531 : OGRShapeDataSource::OGRShapeDataSource()
84 : : m_poPool(std::make_unique<OGRLayerPool>()),
85 3531 : m_b2GBLimit(CPLTestBool(CPLGetConfigOption("SHAPE_2GB_LIMIT", "FALSE")))
86 : {
87 3531 : }
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 7048 : OGRShapeDataSource::~OGRShapeDataSource()
109 :
110 : {
111 3524 : OGRShapeDataSource::Close();
112 7048 : }
113 :
114 : /************************************************************************/
115 : /* OGRShapeDataSource::Close() */
116 : /************************************************************************/
117 :
118 5731 : CPLErr OGRShapeDataSource::Close(GDALProgressFunc, void *)
119 : {
120 5731 : CPLErr eErr = CE_None;
121 5731 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
122 : {
123 3524 : eErr = OGRShapeDataSource::FlushCache(true);
124 :
125 7048 : CPLStringList aosFileList;
126 3524 : if (IsMarkedSuppressOnClose())
127 1 : aosFileList = GetFileList();
128 :
129 7048 : std::vector<CPLString> layerNames;
130 3524 : if (!m_osTemporaryUnzipDir.empty())
131 : {
132 6 : layerNames = GetLayerNames();
133 : }
134 3524 : m_apoLayers.clear();
135 3524 : m_poPool.reset();
136 :
137 3524 : RecompressIfNeeded(layerNames);
138 3524 : RemoveLockFile();
139 :
140 : // Free mutex & cond
141 3524 : if (m_poRefreshLockFileMutex)
142 : {
143 0 : CPLDestroyMutex(m_poRefreshLockFileMutex);
144 0 : m_poRefreshLockFileMutex = nullptr;
145 : }
146 3524 : if (m_poRefreshLockFileCond)
147 : {
148 0 : CPLDestroyCond(m_poRefreshLockFileCond);
149 0 : m_poRefreshLockFileCond = nullptr;
150 : }
151 :
152 3524 : if (IsMarkedSuppressOnClose())
153 1 : poDriver->Delete(nullptr, aosFileList.List());
154 :
155 3524 : if (GDALDataset::Close() != CE_None)
156 0 : eErr = CE_Failure;
157 : }
158 :
159 5731 : 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 3527 : bool OGRShapeDataSource::Open(GDALOpenInfo *poOpenInfo, bool bTestOpen,
219 : bool bForceSingleFileDataSource)
220 :
221 : {
222 3527 : CPLAssert(m_apoLayers.empty());
223 :
224 3527 : const char *pszNewName = poOpenInfo->pszFilename;
225 3527 : const bool bUpdate = poOpenInfo->eAccess == GA_Update;
226 3527 : CPLAssert(papszOpenOptions == nullptr);
227 3527 : papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
228 :
229 3527 : eAccess = poOpenInfo->eAccess;
230 :
231 3527 : 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 3527 : if (m_bSingleFileDataSource)
242 526 : return true;
243 :
244 : /* -------------------------------------------------------------------- */
245 : /* Is the given path a directory or a regular file? */
246 : /* -------------------------------------------------------------------- */
247 3001 : 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 3001 : if (!poOpenInfo->bIsDirectory)
261 : {
262 1784 : 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 1783 : m_bSingleFileDataSource = true;
275 :
276 1783 : return true;
277 : }
278 : else
279 : {
280 2434 : const CPLStringList aosCandidates(VSIReadDir(pszNewName));
281 1217 : const int nCandidateCount = aosCandidates.size();
282 1217 : bool bMightBeOldCoverage = false;
283 1217 : std::set<CPLString> osLayerNameSet;
284 :
285 50074 : for (int iCan = 0; iCan < nCandidateCount; iCan++)
286 : {
287 48857 : const char *pszCandidate = aosCandidates[iCan];
288 48857 : 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 48857 : if (EQUAL(pszCandidate, "ARC"))
297 0 : bMightBeOldCoverage = true;
298 :
299 48857 : if (strlen(pszCandidate) < 4 ||
300 43085 : !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".shp"))
301 45120 : continue;
302 :
303 : std::string osFilename =
304 3737 : CPLFormFilenameSafe(pszNewName, pszCandidate, nullptr);
305 :
306 3737 : 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 3737 : m_oVectorLayerName.push_back(std::move(osFilename));
319 : #endif
320 : }
321 :
322 : // Try and .dbf files without apparent associated shapefiles.
323 50074 : for (int iCan = 0; iCan < nCandidateCount; iCan++)
324 : {
325 48857 : const char *pszCandidate = aosCandidates[iCan];
326 48857 : const std::string osLayerNameOri = CPLGetBasenameSafe(pszCandidate);
327 48857 : 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 48857 : if (bMightBeOldCoverage && osLayerNameSet.empty())
336 0 : continue;
337 :
338 48857 : if (strlen(pszCandidate) < 4 ||
339 43085 : !EQUAL(pszCandidate + strlen(pszCandidate) - 4, ".dbf"))
340 44562 : continue;
341 :
342 4295 : if (osLayerNameSet.find(osLayerName) != osLayerNameSet.end())
343 3736 : 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 1217 : const int nDirLayers = static_cast<int>(m_oVectorLayerName.size());
385 : #endif
386 :
387 1217 : CPLErrorReset();
388 :
389 1217 : return nDirLayers > 0 || !bTestOpen;
390 : }
391 : }
392 :
393 : /************************************************************************/
394 : /* OpenFile() */
395 : /************************************************************************/
396 :
397 6169 : bool OGRShapeDataSource::OpenFile(const char *pszNewName, bool bUpdate)
398 :
399 : {
400 12338 : const std::string osExtension = CPLGetExtensionSafe(pszNewName);
401 :
402 6169 : if (!EQUAL(osExtension.c_str(), "shp") &&
403 6916 : !EQUAL(osExtension.c_str(), "shx") &&
404 747 : !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 6169 : bUpdate && (!IsZip() || !GetTemporaryUnzipDir().empty());
419 6169 : CPLErrorReset();
420 6169 : CPLPushErrorHandler(CPLQuietErrorHandler);
421 6169 : SHPHandle hSHP = bRealUpdateAccess ? DS_SHPOpen(pszNewName, "r+")
422 4206 : : DS_SHPOpen(pszNewName, "r");
423 6169 : CPLPopErrorHandler();
424 :
425 : const bool bRestoreSHX =
426 6169 : CPLTestBool(CPLGetConfigOption("SHAPE_RESTORE_SHX", "FALSE"));
427 6170 : 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 6894 : if (hSHP == nullptr &&
437 6894 : (!EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf") ||
438 724 : 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 6166 : 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 6167 : DBFHandle hDBF = nullptr;
455 6892 : if (hSHP != nullptr ||
456 6892 : EQUAL(CPLGetExtensionSafe(pszNewName).c_str(), "dbf"))
457 : {
458 6167 : if (bRealUpdateAccess)
459 : {
460 1963 : hDBF = DS_DBFOpen(pszNewName, "r+");
461 1963 : 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 4204 : hDBF = DS_DBFOpen(pszNewName, "r");
491 : }
492 : }
493 : else
494 : {
495 0 : hDBF = nullptr;
496 : }
497 :
498 6166 : 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 6166 : /* osPrjFilename = */ std::string(), bUpdate, wkbNone);
509 12332 : poLayer->SetModificationDate(
510 6166 : CSLFetchNameValue(papszOpenOptions, "DBF_DATE_LAST_UPDATE"));
511 6166 : poLayer->SetAutoRepack(CPLFetchBool(papszOpenOptions, "AUTO_REPACK", true));
512 6166 : poLayer->SetWriteDBFEOFChar(
513 6166 : CPLFetchBool(papszOpenOptions, "DBF_EOF_CHAR", true));
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Add layer to data source layer list. */
517 : /* -------------------------------------------------------------------- */
518 6166 : AddLayer(std::move(poLayer));
519 :
520 6166 : return true;
521 : }
522 :
523 : /************************************************************************/
524 : /* AddLayer() */
525 : /************************************************************************/
526 :
527 7873 : void OGRShapeDataSource::AddLayer(std::unique_ptr<OGRShapeLayer> poLayer)
528 : {
529 7873 : 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 7873 : if (static_cast<int>(m_apoLayers.size()) ==
537 7880 : 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 7873 : }
544 :
545 : /************************************************************************/
546 : /* LaunderLayerName() */
547 : /************************************************************************/
548 :
549 1190 : static CPLString LaunderLayerName(const char *pszLayerName)
550 : {
551 2380 : std::string osRet(CPLLaunderForFilenameSafe(pszLayerName, nullptr));
552 1190 : 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 2380 : return osRet;
559 : }
560 :
561 : /************************************************************************/
562 : /* ICreateLayer() */
563 : /************************************************************************/
564 :
565 : OGRLayer *
566 1720 : OGRShapeDataSource::ICreateLayer(const char *pszLayerName,
567 : const OGRGeomFieldDefn *poGeomFieldDefn,
568 : CSLConstList papszOptions)
569 :
570 : {
571 : // To ensure that existing layers are created.
572 1720 : GetLayerCount();
573 :
574 1720 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
575 : const auto poSRS =
576 1720 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
577 :
578 : /* -------------------------------------------------------------------- */
579 : /* Check that the layer doesn't already exist. */
580 : /* -------------------------------------------------------------------- */
581 1720 : 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 1718 : 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 1718 : 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 1717 : if (!UncompressIfNeeded())
609 0 : return nullptr;
610 :
611 : /* -------------------------------------------------------------------- */
612 : /* Figure out what type of layer we need. */
613 : /* -------------------------------------------------------------------- */
614 1717 : int nShapeType = -1;
615 :
616 1717 : if (wkbFlatten(eType) == wkbUnknown || eType == wkbLineString)
617 1305 : nShapeType = SHPT_ARC;
618 412 : else if (eType == wkbPoint)
619 46 : nShapeType = SHPT_POINT;
620 366 : else if (eType == wkbPolygon || eType == wkbTriangle)
621 163 : nShapeType = SHPT_POLYGON;
622 203 : else if (eType == wkbMultiPoint)
623 7 : nShapeType = SHPT_MULTIPOINT;
624 196 : else if (eType == wkbPoint25D)
625 8 : nShapeType = SHPT_POINTZ;
626 188 : else if (eType == wkbPointM)
627 2 : nShapeType = SHPT_POINTM;
628 186 : else if (eType == wkbPointZM)
629 2 : nShapeType = SHPT_POINTZ;
630 184 : else if (eType == wkbLineString25D)
631 12 : nShapeType = SHPT_ARCZ;
632 172 : else if (eType == wkbLineStringM)
633 2 : nShapeType = SHPT_ARCM;
634 170 : else if (eType == wkbLineStringZM)
635 2 : nShapeType = SHPT_ARCZ;
636 168 : else if (eType == wkbMultiLineString)
637 12 : nShapeType = SHPT_ARC;
638 156 : else if (eType == wkbMultiLineString25D)
639 5 : nShapeType = SHPT_ARCZ;
640 151 : else if (eType == wkbMultiLineStringM)
641 1 : nShapeType = SHPT_ARCM;
642 150 : else if (eType == wkbMultiLineStringZM)
643 1 : nShapeType = SHPT_ARCZ;
644 149 : else if (eType == wkbPolygon25D || eType == wkbTriangleZ)
645 12 : nShapeType = SHPT_POLYGONZ;
646 137 : else if (eType == wkbPolygonM || eType == wkbTriangleM)
647 2 : nShapeType = SHPT_POLYGONM;
648 135 : else if (eType == wkbPolygonZM || eType == wkbTriangleZM)
649 2 : nShapeType = SHPT_POLYGONZ;
650 133 : else if (eType == wkbMultiPolygon)
651 43 : nShapeType = SHPT_POLYGON;
652 90 : else if (eType == wkbMultiPolygon25D)
653 5 : nShapeType = SHPT_POLYGONZ;
654 85 : else if (eType == wkbMultiPolygonM)
655 1 : nShapeType = SHPT_POLYGONM;
656 84 : else if (eType == wkbMultiPolygonZM)
657 1 : nShapeType = SHPT_POLYGONZ;
658 83 : else if (eType == wkbMultiPoint25D)
659 6 : nShapeType = SHPT_MULTIPOINTZ;
660 77 : else if (eType == wkbMultiPointM)
661 2 : nShapeType = SHPT_MULTIPOINTM;
662 75 : else if (eType == wkbMultiPointZM)
663 2 : nShapeType = SHPT_MULTIPOINTZ;
664 142 : else if (wkbFlatten(eType) == wkbTIN ||
665 69 : wkbFlatten(eType) == wkbPolyhedralSurface)
666 4 : nShapeType = SHPT_MULTIPATCH;
667 69 : else if (eType == wkbNone)
668 65 : nShapeType = SHPT_NULL;
669 :
670 : /* -------------------------------------------------------------------- */
671 : /* Has the application overridden this with a special creation */
672 : /* option? */
673 : /* -------------------------------------------------------------------- */
674 1717 : const char *pszOverride = CSLFetchNameValue(papszOptions, "SHPT");
675 :
676 1717 : 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 1717 : 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 3426 : std::string osFilenameWithoutExt;
795 :
796 1713 : if (m_bSingleFileDataSource && m_apoLayers.empty())
797 : {
798 1046 : const std::string osPath = CPLGetPathSafe(GetDescription());
799 523 : const std::string osFBasename = CPLGetBasenameSafe(GetDescription());
800 :
801 : osFilenameWithoutExt =
802 523 : CPLFormFilenameSafe(osPath.c_str(), osFBasename.c_str(), nullptr);
803 : }
804 1190 : 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 1174 : const std::string osDir(m_osTemporaryUnzipDir.empty()
819 1171 : ? std::string(GetDescription())
820 1174 : : m_osTemporaryUnzipDir);
821 2348 : osFilenameWithoutExt = CPLFormFilenameSafe(
822 3522 : osDir.c_str(), LaunderLayerName(pszLayerName).c_str(), nullptr);
823 : }
824 :
825 : /* -------------------------------------------------------------------- */
826 : /* Create the shapefile. */
827 : /* -------------------------------------------------------------------- */
828 : const bool l_b2GBLimit =
829 1713 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "2GB_LIMIT", "FALSE"));
830 :
831 1713 : SHPHandle hSHP = nullptr;
832 :
833 1713 : if (nShapeType != SHPT_NULL)
834 : {
835 : const std::string osFilename =
836 1648 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
837 :
838 1648 : hSHP = SHPCreateLL(osFilename.c_str(), nShapeType,
839 : const_cast<SAHooks *>(VSI_SHP_GetHook(l_b2GBLimit)));
840 :
841 1648 : if (hSHP == nullptr)
842 : {
843 5 : return nullptr;
844 : }
845 :
846 1643 : SHPSetFastModeReadObject(hSHP, TRUE);
847 : }
848 :
849 : /* -------------------------------------------------------------------- */
850 : /* Has a specific LDID been specified by the caller? */
851 : /* -------------------------------------------------------------------- */
852 1708 : const char *pszLDID = CSLFetchNameValue(papszOptions, "ENCODING");
853 :
854 : /* -------------------------------------------------------------------- */
855 : /* Create a DBF file. */
856 : /* -------------------------------------------------------------------- */
857 : const std::string osDBFFilename =
858 3416 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "dbf");
859 :
860 1708 : DBFHandle hDBF = DBFCreateLL(
861 : osDBFFilename.c_str(), (pszLDID != nullptr) ? pszLDID : "LDID/87",
862 1708 : const_cast<SAHooks *>(VSI_SHP_GetHook(m_b2GBLimit)));
863 :
864 1708 : 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 3414 : std::string osPrjFilename;
877 3414 : auto poSRSClone = OGRSpatialReferenceRefCountedPtr::makeClone(poSRS);
878 1707 : if (poSRSClone)
879 : {
880 : osPrjFilename =
881 234 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "prj");
882 :
883 234 : char *pszWKT = nullptr;
884 234 : VSILFILE *fp = nullptr;
885 234 : const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
886 464 : if (poSRSClone->exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE &&
887 230 : (fp = VSIFOpenL(osPrjFilename.c_str(), "wt")) != nullptr)
888 : {
889 230 : VSIFWriteL(pszWKT, strlen(pszWKT), 1, fp);
890 230 : VSIFCloseL(fp);
891 : }
892 :
893 234 : CPLFree(pszWKT);
894 : }
895 :
896 : /* -------------------------------------------------------------------- */
897 : /* Create the layer object. */
898 : /* -------------------------------------------------------------------- */
899 : // OGRShapeLayer constructor expects a filename with an extension (that
900 : // could be random actually), otherwise this is going to cause problems with
901 : // layer names that have a dot (not speaking about the one before the shp)
902 : const std::string osSHPFilename =
903 3414 : CPLFormFilenameSafe(nullptr, osFilenameWithoutExt.c_str(), "shp");
904 :
905 : auto poLayer = std::make_unique<OGRShapeLayer>(
906 1707 : this, osSHPFilename.c_str(), hSHP, hDBF, poSRSClone.get(),
907 0 : /* bSRSSet = */ true, osPrjFilename,
908 5121 : /* bUpdate = */ true, eType);
909 :
910 1707 : poLayer->SetResizeAtClose(CPLFetchBool(papszOptions, "RESIZE", false));
911 1707 : poLayer->CreateSpatialIndexAtClose(
912 1707 : CPLFetchBool(papszOptions, "SPATIAL_INDEX", false));
913 1707 : poLayer->SetModificationDate(
914 : CSLFetchNameValue(papszOptions, "DBF_DATE_LAST_UPDATE"));
915 1707 : poLayer->SetAutoRepack(CPLFetchBool(papszOptions, "AUTO_REPACK", true));
916 1707 : poLayer->SetWriteDBFEOFChar(
917 1707 : CPLFetchBool(papszOptions, "DBF_EOF_CHAR", true));
918 :
919 : /* -------------------------------------------------------------------- */
920 : /* Add layer to data source layer list. */
921 : /* -------------------------------------------------------------------- */
922 1707 : AddLayer(std::move(poLayer));
923 :
924 1707 : return m_apoLayers.back().get();
925 : }
926 :
927 : /************************************************************************/
928 : /* TestCapability() */
929 : /************************************************************************/
930 :
931 872 : int OGRShapeDataSource::TestCapability(const char *pszCap) const
932 :
933 : {
934 872 : if (EQUAL(pszCap, ODsCCreateLayer))
935 416 : return eAccess == GA_Update &&
936 416 : !(m_bIsZip && m_bSingleLayerZip && m_apoLayers.size() == 1);
937 664 : else if (EQUAL(pszCap, ODsCDeleteLayer))
938 18 : return eAccess == GA_Update && !(m_bIsZip && m_bSingleLayerZip);
939 646 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
940 20 : return true;
941 626 : else if (EQUAL(pszCap, ODsCZGeometries))
942 20 : return true;
943 606 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
944 6 : return eAccess == GA_Update;
945 :
946 600 : return false;
947 : }
948 :
949 : /************************************************************************/
950 : /* GetLayerCount() */
951 : /************************************************************************/
952 :
953 609340 : int OGRShapeDataSource::GetLayerCount() const
954 :
955 : {
956 : #ifndef IMMEDIATE_OPENING
957 609340 : if (!m_oVectorLayerName.empty())
958 : {
959 4666 : for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
960 : {
961 4296 : const char *pszFilename = m_oVectorLayerName[i].c_str();
962 4296 : const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
963 :
964 4296 : bool bFound = false;
965 635458 : for (auto &poLayer : m_apoLayers)
966 : {
967 632408 : if (poLayer->GetName() == osLayerName)
968 : {
969 1246 : bFound = true;
970 1246 : break;
971 : }
972 : }
973 4296 : if (bFound)
974 1246 : continue;
975 :
976 3050 : if (!const_cast<OGRShapeDataSource *>(this)->OpenFile(
977 3050 : pszFilename, eAccess == GA_Update))
978 : {
979 1 : CPLError(CE_Failure, CPLE_OpenFailed,
980 : "Failed to open file %s."
981 : "It may be corrupt or read-only file accessed in "
982 : "update mode.",
983 : pszFilename);
984 : }
985 : }
986 370 : m_oVectorLayerName.resize(0);
987 : }
988 : #endif
989 :
990 609340 : return static_cast<int>(m_apoLayers.size());
991 : }
992 :
993 : /************************************************************************/
994 : /* GetLayer() */
995 : /************************************************************************/
996 :
997 552448 : const OGRLayer *OGRShapeDataSource::GetLayer(int iLayer) const
998 :
999 : {
1000 : // To ensure that existing layers are created.
1001 552448 : GetLayerCount();
1002 :
1003 552448 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1004 14 : return nullptr;
1005 :
1006 552434 : return m_apoLayers[iLayer].get();
1007 : }
1008 :
1009 : /************************************************************************/
1010 : /* GetLayerByName() */
1011 : /************************************************************************/
1012 :
1013 5283 : OGRLayer *OGRShapeDataSource::GetLayerByName(const char *pszLayerNameIn)
1014 : {
1015 : #ifndef IMMEDIATE_OPENING
1016 5283 : if (!m_oVectorLayerName.empty())
1017 : {
1018 257268 : for (auto &poLayer : m_apoLayers)
1019 : {
1020 255928 : if (strcmp(poLayer->GetName(), pszLayerNameIn) == 0)
1021 : {
1022 1081 : return poLayer.get();
1023 : }
1024 : }
1025 :
1026 1440 : for (int j = 0; j < 2; j++)
1027 : {
1028 252134 : for (size_t i = 0; i < m_oVectorLayerName.size(); i++)
1029 : {
1030 252034 : const char *pszFilename = m_oVectorLayerName[i].c_str();
1031 252034 : const std::string osLayerName = CPLGetBasenameSafe(pszFilename);
1032 :
1033 252034 : if (j == 0)
1034 : {
1035 251924 : if (osLayerName != pszLayerNameIn)
1036 250679 : continue;
1037 : }
1038 : else
1039 : {
1040 110 : if (!EQUAL(osLayerName.c_str(), pszLayerNameIn))
1041 20 : continue;
1042 : }
1043 :
1044 1335 : if (!OpenFile(pszFilename, eAccess == GA_Update))
1045 : {
1046 1 : CPLError(CE_Failure, CPLE_OpenFailed,
1047 : "Failed to open file %s. "
1048 : "It may be corrupt or read-only file accessed in "
1049 : "update mode.",
1050 : pszFilename);
1051 1 : return nullptr;
1052 : }
1053 :
1054 1334 : return m_apoLayers.back().get();
1055 : }
1056 : }
1057 :
1058 5 : return nullptr;
1059 : }
1060 : #endif
1061 :
1062 2862 : return GDALDataset::GetLayerByName(pszLayerNameIn);
1063 : }
1064 :
1065 : /************************************************************************/
1066 : /* ExecuteSQL() */
1067 : /* */
1068 : /* We override this to provide special handling of CREATE */
1069 : /* SPATIAL INDEX commands. Support forms are: */
1070 : /* */
1071 : /* CREATE SPATIAL INDEX ON layer_name [DEPTH n] */
1072 : /* DROP SPATIAL INDEX ON layer_name */
1073 : /* REPACK layer_name */
1074 : /* RECOMPUTE EXTENT ON layer_name */
1075 : /************************************************************************/
1076 :
1077 1022 : OGRLayer *OGRShapeDataSource::ExecuteSQL(const char *pszStatement,
1078 : OGRGeometry *poSpatialFilter,
1079 : const char *pszDialect)
1080 :
1081 : {
1082 1022 : if (EQUAL(pszStatement, "UNCOMPRESS"))
1083 : {
1084 0 : CPL_IGNORE_RET_VAL(UncompressIfNeeded());
1085 0 : return nullptr;
1086 : }
1087 :
1088 1022 : if (EQUAL(pszStatement, "RECOMPRESS"))
1089 : {
1090 0 : RecompressIfNeeded(GetLayerNames());
1091 0 : return nullptr;
1092 : }
1093 : /* ==================================================================== */
1094 : /* Handle command to drop a spatial index. */
1095 : /* ==================================================================== */
1096 1022 : if (STARTS_WITH_CI(pszStatement, "REPACK "))
1097 : {
1098 : OGRShapeLayer *poLayer =
1099 17 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
1100 :
1101 17 : if (poLayer != nullptr)
1102 : {
1103 17 : if (poLayer->Repack() != OGRERR_NONE)
1104 : {
1105 1 : CPLError(CE_Failure, CPLE_AppDefined,
1106 : "REPACK of layer '%s' failed.", pszStatement + 7);
1107 : }
1108 : }
1109 : else
1110 : {
1111 0 : CPLError(CE_Failure, CPLE_AppDefined,
1112 : "No such layer as '%s' in REPACK.", pszStatement + 7);
1113 : }
1114 17 : return nullptr;
1115 : }
1116 :
1117 : /* ==================================================================== */
1118 : /* Handle command to shrink columns to their minimum size. */
1119 : /* ==================================================================== */
1120 1005 : if (STARTS_WITH_CI(pszStatement, "RESIZE "))
1121 : {
1122 : OGRShapeLayer *poLayer =
1123 1 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 7));
1124 :
1125 1 : if (poLayer != nullptr)
1126 : {
1127 1 : poLayer->ResizeDBF();
1128 : }
1129 : else
1130 : {
1131 0 : CPLError(CE_Failure, CPLE_AppDefined,
1132 : "No such layer as '%s' in RESIZE.", pszStatement + 7);
1133 : }
1134 1 : return nullptr;
1135 : }
1136 :
1137 : /* ==================================================================== */
1138 : /* Handle command to recompute extent */
1139 : /* ==================================================================== */
1140 1004 : if (STARTS_WITH_CI(pszStatement, "RECOMPUTE EXTENT ON "))
1141 : {
1142 : OGRShapeLayer *poLayer =
1143 6 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 20));
1144 :
1145 6 : if (poLayer != nullptr)
1146 : {
1147 5 : poLayer->RecomputeExtent();
1148 : }
1149 : else
1150 : {
1151 1 : CPLError(CE_Failure, CPLE_AppDefined,
1152 : "No such layer as '%s' in RECOMPUTE EXTENT.",
1153 : pszStatement + 20);
1154 : }
1155 6 : return nullptr;
1156 : }
1157 :
1158 : /* ==================================================================== */
1159 : /* Handle command to drop a spatial index. */
1160 : /* ==================================================================== */
1161 998 : if (STARTS_WITH_CI(pszStatement, "DROP SPATIAL INDEX ON "))
1162 : {
1163 : OGRShapeLayer *poLayer =
1164 3 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(pszStatement + 22));
1165 :
1166 3 : if (poLayer != nullptr)
1167 : {
1168 3 : poLayer->DropSpatialIndex();
1169 : }
1170 : else
1171 : {
1172 0 : CPLError(CE_Failure, CPLE_AppDefined,
1173 : "No such layer as '%s' in DROP SPATIAL INDEX.",
1174 : pszStatement + 22);
1175 : }
1176 3 : return nullptr;
1177 : }
1178 :
1179 : /* ==================================================================== */
1180 : /* Handle all commands except spatial index creation generically. */
1181 : /* ==================================================================== */
1182 995 : if (!STARTS_WITH_CI(pszStatement, "CREATE SPATIAL INDEX ON "))
1183 : {
1184 979 : char **papszTokens = CSLTokenizeString(pszStatement);
1185 979 : if (CSLCount(papszTokens) >= 4 &&
1186 468 : (EQUAL(papszTokens[0], "CREATE") ||
1187 440 : EQUAL(papszTokens[0], "DROP")) &&
1188 1447 : EQUAL(papszTokens[1], "INDEX") && EQUAL(papszTokens[2], "ON"))
1189 : {
1190 : OGRShapeLayer *poLayer =
1191 38 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[3]));
1192 38 : if (poLayer != nullptr)
1193 38 : poLayer->InitializeIndexSupport(poLayer->GetFullName());
1194 : }
1195 979 : CSLDestroy(papszTokens);
1196 :
1197 979 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter,
1198 979 : pszDialect);
1199 : }
1200 :
1201 : /* -------------------------------------------------------------------- */
1202 : /* Parse into keywords. */
1203 : /* -------------------------------------------------------------------- */
1204 16 : char **papszTokens = CSLTokenizeString(pszStatement);
1205 :
1206 32 : if (CSLCount(papszTokens) < 5 || !EQUAL(papszTokens[0], "CREATE") ||
1207 16 : !EQUAL(papszTokens[1], "SPATIAL") || !EQUAL(papszTokens[2], "INDEX") ||
1208 48 : !EQUAL(papszTokens[3], "ON") || CSLCount(papszTokens) > 7 ||
1209 16 : (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5], "DEPTH")))
1210 : {
1211 0 : CSLDestroy(papszTokens);
1212 0 : CPLError(CE_Failure, CPLE_AppDefined,
1213 : "Syntax error in CREATE SPATIAL INDEX command.\n"
1214 : "Was '%s'\n"
1215 : "Should be of form 'CREATE SPATIAL INDEX ON <table> "
1216 : "[DEPTH <n>]'",
1217 : pszStatement);
1218 0 : return nullptr;
1219 : }
1220 :
1221 : /* -------------------------------------------------------------------- */
1222 : /* Get depth if provided. */
1223 : /* -------------------------------------------------------------------- */
1224 16 : const int nDepth = CSLCount(papszTokens) == 7 ? atoi(papszTokens[6]) : 0;
1225 :
1226 : /* -------------------------------------------------------------------- */
1227 : /* What layer are we operating on. */
1228 : /* -------------------------------------------------------------------- */
1229 : OGRShapeLayer *poLayer =
1230 16 : cpl::down_cast<OGRShapeLayer *>(GetLayerByName(papszTokens[4]));
1231 :
1232 16 : if (poLayer == nullptr)
1233 : {
1234 0 : CPLError(CE_Failure, CPLE_AppDefined, "Layer %s not recognised.",
1235 0 : papszTokens[4]);
1236 0 : CSLDestroy(papszTokens);
1237 0 : return nullptr;
1238 : }
1239 :
1240 16 : CSLDestroy(papszTokens);
1241 :
1242 16 : poLayer->CreateSpatialIndex(nDepth);
1243 16 : return nullptr;
1244 : }
1245 :
1246 : /************************************************************************/
1247 : /* GetExtensionsForDeletion() */
1248 : /************************************************************************/
1249 :
1250 694 : const char *const *OGRShapeDataSource::GetExtensionsForDeletion()
1251 : {
1252 : static const char *const apszExtensions[] = {
1253 : "shp", "shx", "dbf", "sbn", "sbx", "prj",
1254 : "idm", "ind", "qix", "cpg", "shp.xml",
1255 : "qpj", // QGIS projection file
1256 : nullptr};
1257 694 : return apszExtensions;
1258 : }
1259 :
1260 : /************************************************************************/
1261 : /* DeleteLayer() */
1262 : /************************************************************************/
1263 :
1264 548 : OGRErr OGRShapeDataSource::DeleteLayer(int iLayer)
1265 :
1266 : {
1267 : /* -------------------------------------------------------------------- */
1268 : /* Verify we are in update mode. */
1269 : /* -------------------------------------------------------------------- */
1270 548 : if (eAccess != GA_Update)
1271 : {
1272 1 : CPLError(CE_Failure, CPLE_NoWriteAccess,
1273 : "Data source %s opened read-only. "
1274 : "Layer %d cannot be deleted.",
1275 1 : GetDescription(), iLayer);
1276 :
1277 1 : return OGRERR_FAILURE;
1278 : }
1279 :
1280 : // To ensure that existing layers are created.
1281 547 : GetLayerCount();
1282 :
1283 547 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1284 : {
1285 2 : CPLError(CE_Failure, CPLE_AppDefined,
1286 : "Layer %d not in legal range of 0 to %d.", iLayer,
1287 2 : static_cast<int>(m_apoLayers.size()) - 1);
1288 2 : return OGRERR_FAILURE;
1289 : }
1290 :
1291 545 : if (m_bIsZip && m_bSingleLayerZip)
1292 : {
1293 3 : CPLError(CE_Failure, CPLE_NotSupported,
1294 : ".shz does not support layer deletion");
1295 3 : return OGRERR_FAILURE;
1296 : }
1297 :
1298 542 : if (!UncompressIfNeeded())
1299 0 : return OGRERR_FAILURE;
1300 :
1301 542 : const std::string osLayerFilename = m_apoLayers[iLayer]->GetFullName();
1302 :
1303 542 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1304 :
1305 : const char *const *papszExtensions =
1306 542 : OGRShapeDataSource::GetExtensionsForDeletion();
1307 7046 : for (int iExt = 0; papszExtensions[iExt] != nullptr; iExt++)
1308 : {
1309 : const std::string osFile = CPLResetExtensionSafe(
1310 13008 : osLayerFilename.c_str(), papszExtensions[iExt]);
1311 : VSIStatBufL sStatBuf;
1312 6504 : if (VSIStatL(osFile.c_str(), &sStatBuf) == 0)
1313 1621 : VSIUnlink(osFile.c_str());
1314 : }
1315 :
1316 542 : return OGRERR_NONE;
1317 : }
1318 :
1319 : /************************************************************************/
1320 : /* SetLastUsedLayer() */
1321 : /************************************************************************/
1322 :
1323 242686 : void OGRShapeDataSource::SetLastUsedLayer(OGRShapeLayer *poLayer)
1324 : {
1325 : // We could remove that check and things would still work in
1326 : // 99.99% cases.
1327 : // The only rationale for that test is to avoid breaking applications that
1328 : // would deal with layers of the same datasource in different threads. In
1329 : // GDAL < 1.9.0, this would work in most cases I can imagine as shapefile
1330 : // layers are pretty much independent from each others (although it has
1331 : // never been guaranteed to be a valid use case, and the shape driver is
1332 : // likely more the exception than the rule in permitting accessing layers
1333 : // from different threads !) Anyway the LRU list mechanism leaves the door
1334 : // open to concurrent accesses to it so when the datasource has not many
1335 : // layers, we don't try to build the LRU list to avoid concurrency issues. I
1336 : // haven't bothered making the analysis of how a mutex could be used to
1337 : // protect that (my intuition is that it would need to be placed at the
1338 : // beginning of OGRShapeLayer::TouchLayer() ).
1339 485372 : if (static_cast<int>(m_apoLayers.size()) <
1340 242686 : m_poPool->GetMaxSimultaneouslyOpened())
1341 229243 : return;
1342 :
1343 13443 : m_poPool->SetLastUsedLayer(poLayer);
1344 : }
1345 :
1346 : /************************************************************************/
1347 : // GetFileList() */
1348 : /************************************************************************/
1349 :
1350 35 : char **OGRShapeDataSource::GetFileList()
1351 : {
1352 35 : if (m_bIsZip)
1353 : {
1354 1 : return CSLAddString(nullptr, GetDescription());
1355 : }
1356 68 : CPLStringList oFileList;
1357 34 : GetLayerCount();
1358 175 : for (auto &poLayer : m_apoLayers)
1359 : {
1360 141 : poLayer->AddToFileList(oFileList);
1361 : }
1362 34 : return oFileList.StealList();
1363 : }
1364 :
1365 : /************************************************************************/
1366 : // RefreshLockFile() */
1367 : /************************************************************************/
1368 :
1369 0 : void OGRShapeDataSource::RefreshLockFile(void *_self)
1370 : {
1371 0 : OGRShapeDataSource *self = static_cast<OGRShapeDataSource *>(_self);
1372 0 : CPLAssert(self->m_psLockFile);
1373 0 : CPLAcquireMutex(self->m_poRefreshLockFileMutex, 1000);
1374 0 : self->m_bRefreshLockFileThreadStarted = true;
1375 0 : CPLCondSignal(self->m_poRefreshLockFileCond);
1376 0 : unsigned int nInc = 0;
1377 0 : while (!(self->m_bExitRefreshLockFileThread))
1378 : {
1379 0 : auto ret = CPLCondTimedWait(self->m_poRefreshLockFileCond,
1380 : self->m_poRefreshLockFileMutex,
1381 : self->m_dfRefreshLockDelay);
1382 0 : if (ret == COND_TIMED_WAIT_TIME_OUT)
1383 : {
1384 0 : CPLAssert(self->m_psLockFile);
1385 0 : VSIFSeekL(self->m_psLockFile, 0, SEEK_SET);
1386 0 : CPLString osTime;
1387 0 : nInc++;
1388 : osTime.Printf(CPL_FRMT_GUIB ", %u\n",
1389 0 : static_cast<GUIntBig>(time(nullptr)), nInc);
1390 0 : VSIFWriteL(osTime.data(), 1, osTime.size(), self->m_psLockFile);
1391 0 : VSIFFlushL(self->m_psLockFile);
1392 : }
1393 : }
1394 0 : CPLReleaseMutex(self->m_poRefreshLockFileMutex);
1395 0 : }
1396 :
1397 : /************************************************************************/
1398 : // RemoveLockFile() */
1399 : /************************************************************************/
1400 :
1401 3530 : void OGRShapeDataSource::RemoveLockFile()
1402 : {
1403 3530 : if (!m_psLockFile)
1404 3530 : return;
1405 :
1406 : // Ask the thread to terminate
1407 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1408 0 : m_bExitRefreshLockFileThread = true;
1409 0 : CPLCondSignal(m_poRefreshLockFileCond);
1410 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1411 0 : CPLJoinThread(m_hRefreshLockFileThread);
1412 0 : m_hRefreshLockFileThread = nullptr;
1413 :
1414 : // Close and remove lock file
1415 0 : VSIFCloseL(m_psLockFile);
1416 0 : m_psLockFile = nullptr;
1417 0 : CPLString osLockFile(GetDescription());
1418 0 : osLockFile += ".gdal.lock";
1419 0 : VSIUnlink(osLockFile);
1420 : }
1421 :
1422 : /************************************************************************/
1423 : // UncompressIfNeeded() */
1424 : /************************************************************************/
1425 :
1426 74612 : bool OGRShapeDataSource::UncompressIfNeeded()
1427 : {
1428 74612 : if (eAccess != GA_Update || !m_bIsZip || !m_osTemporaryUnzipDir.empty())
1429 74606 : return true;
1430 :
1431 6 : GetLayerCount();
1432 :
1433 0 : auto returnError = [this]()
1434 : {
1435 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot uncompress %s",
1436 0 : GetDescription());
1437 0 : return false;
1438 6 : };
1439 :
1440 6 : if (m_apoLayers.size() > 1)
1441 : {
1442 0 : CPLString osLockFile(GetDescription());
1443 0 : osLockFile += ".gdal.lock";
1444 : VSIStatBufL sStat;
1445 0 : if (VSIStatL(osLockFile, &sStat) == 0 &&
1446 0 : sStat.st_mtime > time(nullptr) - 2 * knREFRESH_LOCK_FILE_DELAY_SEC)
1447 : {
1448 0 : CPLError(CE_Failure, CPLE_AppDefined,
1449 : "Cannot edit %s. Another task is editing it",
1450 0 : GetDescription());
1451 0 : return false;
1452 : }
1453 0 : if (!m_poRefreshLockFileMutex)
1454 : {
1455 0 : m_poRefreshLockFileMutex = CPLCreateMutex();
1456 0 : if (!m_poRefreshLockFileMutex)
1457 0 : return false;
1458 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1459 : }
1460 0 : if (!m_poRefreshLockFileCond)
1461 : {
1462 0 : m_poRefreshLockFileCond = CPLCreateCond();
1463 0 : if (!m_poRefreshLockFileCond)
1464 0 : return false;
1465 : }
1466 0 : auto f = VSIFOpenL(osLockFile, "wb");
1467 0 : if (!f)
1468 : {
1469 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create lock file");
1470 0 : return false;
1471 : }
1472 0 : m_psLockFile = f;
1473 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1474 0 : m_bExitRefreshLockFileThread = false;
1475 0 : m_bRefreshLockFileThreadStarted = false;
1476 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1477 : // Config option mostly for testing purposes
1478 : // coverity[tainted_data]
1479 0 : m_dfRefreshLockDelay = CPLAtof(CPLGetConfigOption(
1480 : "OGR_SHAPE_LOCK_DELAY",
1481 : CPLSPrintf("%d", knREFRESH_LOCK_FILE_DELAY_SEC)));
1482 0 : m_hRefreshLockFileThread =
1483 0 : CPLCreateJoinableThread(OGRShapeDataSource::RefreshLockFile, this);
1484 0 : if (!m_hRefreshLockFileThread)
1485 : {
1486 0 : VSIFCloseL(m_psLockFile);
1487 0 : m_psLockFile = nullptr;
1488 0 : VSIUnlink(osLockFile);
1489 : }
1490 : else
1491 : {
1492 0 : CPLAcquireMutex(m_poRefreshLockFileMutex, 1000);
1493 0 : while (!m_bRefreshLockFileThreadStarted)
1494 : {
1495 0 : CPLCondWait(m_poRefreshLockFileCond, m_poRefreshLockFileMutex);
1496 : }
1497 0 : CPLReleaseMutex(m_poRefreshLockFileMutex);
1498 : }
1499 : }
1500 :
1501 12 : CPLString osVSIZipDirname(GetVSIZipPrefixeDir());
1502 6 : vsi_l_offset nTotalUncompressedSize = 0;
1503 12 : CPLStringList aosFiles(VSIReadDir(osVSIZipDirname));
1504 20 : for (int i = 0; i < aosFiles.size(); i++)
1505 : {
1506 14 : const char *pszFilename = aosFiles[i];
1507 14 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1508 : {
1509 : const CPLString osSrcFile(
1510 28 : CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
1511 : VSIStatBufL sStat;
1512 14 : if (VSIStatL(osSrcFile, &sStat) == 0)
1513 : {
1514 14 : nTotalUncompressedSize += sStat.st_size;
1515 : }
1516 : }
1517 : }
1518 :
1519 12 : CPLString osTemporaryDir(GetDescription());
1520 6 : osTemporaryDir += "_tmp_uncompressed";
1521 :
1522 : const char *pszUseVsimem =
1523 6 : CPLGetConfigOption("OGR_SHAPE_USE_VSIMEM_FOR_TEMP", "AUTO");
1524 12 : if (EQUAL(pszUseVsimem, "YES") ||
1525 6 : (EQUAL(pszUseVsimem, "AUTO") && nTotalUncompressedSize > 0 &&
1526 : nTotalUncompressedSize <
1527 3 : static_cast<GUIntBig>(CPLGetUsablePhysicalRAM() / 10)))
1528 : {
1529 3 : osTemporaryDir = VSIMemGenerateHiddenFilename("shapedriver");
1530 : }
1531 6 : CPLDebug("Shape", "Uncompressing to %s", osTemporaryDir.c_str());
1532 :
1533 6 : VSIRmdirRecursive(osTemporaryDir);
1534 6 : if (VSIMkdir(osTemporaryDir, 0755) != 0)
1535 0 : return returnError();
1536 20 : for (int i = 0; i < aosFiles.size(); i++)
1537 : {
1538 14 : const char *pszFilename = aosFiles[i];
1539 14 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1540 : {
1541 : const CPLString osSrcFile(
1542 14 : CPLFormFilenameSafe(osVSIZipDirname, pszFilename, nullptr));
1543 : const CPLString osDestFile(
1544 14 : CPLFormFilenameSafe(osTemporaryDir, pszFilename, nullptr));
1545 14 : if (CPLCopyFile(osDestFile, osSrcFile) != 0)
1546 : {
1547 0 : VSIRmdirRecursive(osTemporaryDir);
1548 0 : return returnError();
1549 : }
1550 : }
1551 : }
1552 :
1553 6 : m_osTemporaryUnzipDir = std::move(osTemporaryDir);
1554 :
1555 9 : for (auto &poLayer : m_apoLayers)
1556 : {
1557 3 : poLayer->UpdateFollowingDeOrRecompression();
1558 : }
1559 :
1560 6 : return true;
1561 : }
1562 :
1563 : /************************************************************************/
1564 : // RecompressIfNeeded() */
1565 : /************************************************************************/
1566 :
1567 3524 : bool OGRShapeDataSource::RecompressIfNeeded(
1568 : const std::vector<CPLString> &layerNames)
1569 : {
1570 3524 : if (eAccess != GA_Update || !m_bIsZip || m_osTemporaryUnzipDir.empty())
1571 3518 : return true;
1572 :
1573 0 : auto returnError = [this]()
1574 : {
1575 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot recompress %s",
1576 0 : GetDescription());
1577 0 : RemoveLockFile();
1578 0 : return false;
1579 6 : };
1580 :
1581 12 : CPLStringList aosFiles(VSIReadDir(m_osTemporaryUnzipDir));
1582 12 : CPLString osTmpZip(m_osTemporaryUnzipDir + ".zip");
1583 6 : VSIUnlink(osTmpZip);
1584 18 : CPLString osTmpZipWithVSIZip("/vsizip/{" + osTmpZip + '}');
1585 :
1586 12 : std::map<CPLString, int> oMapLayerOrder;
1587 12 : for (size_t i = 0; i < layerNames.size(); i++)
1588 6 : oMapLayerOrder[layerNames[i]] = static_cast<int>(i);
1589 :
1590 12 : std::vector<CPLString> sortedFiles;
1591 6 : vsi_l_offset nTotalUncompressedSize = 0;
1592 36 : for (int i = 0; i < aosFiles.size(); i++)
1593 : {
1594 30 : sortedFiles.emplace_back(aosFiles[i]);
1595 : const CPLString osSrcFile(
1596 60 : CPLFormFilenameSafe(m_osTemporaryUnzipDir, aosFiles[i], nullptr));
1597 : VSIStatBufL sStat;
1598 30 : if (VSIStatL(osSrcFile, &sStat) == 0)
1599 : {
1600 30 : nTotalUncompressedSize += sStat.st_size;
1601 : }
1602 : }
1603 :
1604 : // Sort files by their layer orders, and then for files of the same layer,
1605 : // make shp appear first, and then by filename order
1606 6 : std::sort(sortedFiles.begin(), sortedFiles.end(),
1607 204 : [&oMapLayerOrder](const CPLString &a, const CPLString &b)
1608 : {
1609 51 : int iA = INT_MAX;
1610 : auto oIterA =
1611 51 : oMapLayerOrder.find(CPLGetBasenameSafe(a).c_str());
1612 51 : if (oIterA != oMapLayerOrder.end())
1613 41 : iA = oIterA->second;
1614 51 : int iB = INT_MAX;
1615 : auto oIterB =
1616 51 : oMapLayerOrder.find(CPLGetBasenameSafe(b).c_str());
1617 51 : if (oIterB != oMapLayerOrder.end())
1618 42 : iB = oIterB->second;
1619 51 : if (iA < iB)
1620 7 : return true;
1621 44 : if (iA > iB)
1622 8 : return false;
1623 36 : if (iA != INT_MAX)
1624 : {
1625 34 : if (EQUAL(CPLGetExtensionSafe(a).c_str(), "shp"))
1626 6 : return true;
1627 28 : if (EQUAL(CPLGetExtensionSafe(b).c_str(), "shp"))
1628 10 : return false;
1629 : }
1630 20 : return a < b;
1631 : });
1632 :
1633 : CPLConfigOptionSetter oZIP64Setter(
1634 : "CPL_CREATE_ZIP64",
1635 12 : nTotalUncompressedSize < 4000U * 1000 * 1000 ? "NO" : "YES", true);
1636 :
1637 : /* Maintain a handle on the ZIP opened */
1638 6 : VSILFILE *fpZIP = VSIFOpenExL(osTmpZipWithVSIZip, "wb", true);
1639 6 : if (fpZIP == nullptr)
1640 : {
1641 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
1642 : osTmpZipWithVSIZip.c_str(), VSIGetLastErrorMsg());
1643 0 : return returnError();
1644 : }
1645 :
1646 36 : for (const auto &osFilename : sortedFiles)
1647 : {
1648 30 : const char *pszFilename = osFilename.c_str();
1649 30 : if (!EQUAL(pszFilename, ".") && !EQUAL(pszFilename, ".."))
1650 : {
1651 26 : const CPLString osSrcFile(CPLFormFilenameSafe(
1652 26 : m_osTemporaryUnzipDir, pszFilename, nullptr));
1653 : const CPLString osDestFile(
1654 26 : CPLFormFilenameSafe(osTmpZipWithVSIZip, pszFilename, nullptr));
1655 26 : if (CPLCopyFile(osDestFile, osSrcFile) != 0)
1656 : {
1657 0 : VSIFCloseL(fpZIP);
1658 0 : return returnError();
1659 : }
1660 : }
1661 : }
1662 :
1663 6 : VSIFCloseL(fpZIP);
1664 :
1665 : const bool bOverwrite =
1666 6 : CPLTestBool(CPLGetConfigOption("OGR_SHAPE_PACK_IN_PLACE",
1667 : #ifdef _WIN32
1668 : "YES"
1669 : #else
1670 : "NO"
1671 : #endif
1672 : ));
1673 6 : if (bOverwrite)
1674 : {
1675 0 : VSILFILE *fpTarget = nullptr;
1676 0 : for (int i = 0; i < 10; i++)
1677 : {
1678 0 : fpTarget = VSIFOpenL(GetDescription(), "rb+");
1679 0 : if (fpTarget)
1680 0 : break;
1681 0 : CPLSleep(0.1);
1682 : }
1683 0 : if (!fpTarget)
1684 0 : return returnError();
1685 0 : bool bCopyOK = CopyInPlace(fpTarget, osTmpZip);
1686 0 : VSIFCloseL(fpTarget);
1687 0 : VSIUnlink(osTmpZip);
1688 0 : if (!bCopyOK)
1689 : {
1690 0 : return returnError();
1691 : }
1692 : }
1693 : else
1694 : {
1695 12 : if (VSIUnlink(GetDescription()) != 0 ||
1696 6 : CPLMoveFile(GetDescription(), osTmpZip) != 0)
1697 : {
1698 0 : return returnError();
1699 : }
1700 : }
1701 :
1702 6 : VSIRmdirRecursive(m_osTemporaryUnzipDir);
1703 6 : m_osTemporaryUnzipDir.clear();
1704 :
1705 6 : for (auto &poLayer : m_apoLayers)
1706 : {
1707 0 : poLayer->UpdateFollowingDeOrRecompression();
1708 : }
1709 :
1710 6 : RemoveLockFile();
1711 :
1712 6 : return true;
1713 : }
1714 :
1715 : /************************************************************************/
1716 : /* CopyInPlace() */
1717 : /************************************************************************/
1718 :
1719 3 : bool OGRShapeDataSource::CopyInPlace(VSILFILE *fpTarget,
1720 : const CPLString &osSourceFilename)
1721 : {
1722 3 : return CPL_TO_BOOL(VSIOverwriteFile(fpTarget, osSourceFilename.c_str()));
1723 : }
|