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