• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15171299595

21 May 2025 07:51PM UTC coverage: 70.949% (+0.03%) from 70.921%
15171299595

Pull #12392

github

web-flow
Merge 405a716f5 into ce2393a45
Pull Request #12392: GDALOverviews: Limit external file size in GDALRegenerateOverviewsMultiBand

174 of 189 new or added lines in 2 files covered. (92.06%)

22859 existing lines in 88 files now uncovered.

568261 of 800943 relevant lines covered (70.95%)

235911.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.61
/gcore/gdaldriver.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Implementation of GDALDriver class (and C wrappers)
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1998, 2000, Frank Warmerdam
9
 * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15
#include "gdal.h"
16
#include "gdal_priv.h"
17
#include "gdal_rat.h"
18
#include "gdalalgorithm.h"
19

20
#include <cerrno>
21
#include <cstdlib>
22
#include <cstring>
23
#include <set>
24
#include <sys/stat.h>
25

26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
#include "cpl_minixml.h"
29
#include "cpl_progress.h"
30
#include "cpl_string.h"
31
#include "cpl_vsi.h"
32
#include "ograpispy.h"
33
#include "ogr_core.h"
34
#include "ogrsf_frmts.h"
35

36
/************************************************************************/
37
/*                             GDALDriver()                             */
38
/************************************************************************/
39

40
GDALDriver::GDALDriver() = default;
41

42
/************************************************************************/
43
/*                            ~GDALDriver()                             */
44
/************************************************************************/
45

46
GDALDriver::~GDALDriver()
437,545✔
47

48
{
49
    if (pfnUnloadDriver != nullptr)
248,048✔
50
        pfnUnloadDriver(this);
6,874✔
51
}
437,545✔
52

53
/************************************************************************/
54
/*                         GDALCreateDriver()                           */
55
/************************************************************************/
56

57
/**
58
 * \brief Create a GDALDriver.
59
 *
60
 * Creates a driver in the GDAL heap.
61
 */
62

63
GDALDriverH CPL_STDCALL GDALCreateDriver()
224✔
64
{
65
    return new GDALDriver();
224✔
66
}
67

68
/************************************************************************/
69
/*                         GDALDestroyDriver()                          */
70
/************************************************************************/
71

72
/**
73
 * \brief Destroy a GDALDriver.
74
 *
75
 * This is roughly equivalent to deleting the driver, but is guaranteed
76
 * to take place in the GDAL heap.  It is important this that function
77
 * not be called on a driver that is registered with the GDALDriverManager.
78
 *
79
 * @param hDriver the driver to destroy.
80
 */
81

82
void CPL_STDCALL GDALDestroyDriver(GDALDriverH hDriver)
×
83

84
{
85
    if (hDriver != nullptr)
×
86
        delete GDALDriver::FromHandle(hDriver);
×
87
}
×
88

89
/************************************************************************/
90
/*                               Open()                                 */
91
/************************************************************************/
92

93
//! @cond Doxygen_Suppress
94

95
GDALDataset *GDALDriver::Open(GDALOpenInfo *poOpenInfo, bool bSetOpenOptions)
432,344✔
96
{
97

98
    GDALDataset *poDS = nullptr;
432,344✔
99
    pfnOpen = GetOpenCallback();
432,344✔
100
    if (pfnOpen != nullptr)
432,251✔
101
    {
102
        poDS = pfnOpen(poOpenInfo);
432,301✔
103
    }
UNCOV
104
    else if (pfnOpenWithDriverArg != nullptr)
×
105
    {
106
        poDS = pfnOpenWithDriverArg(this, poOpenInfo);
3✔
107
    }
108

109
    if (poDS)
432,436✔
110
    {
111
        // Only set GDAL_OF_THREAD_SAFE if the driver itself has set it in
112
        // poDS->nOpenFlags
113
        int nOpenFlags = poOpenInfo->nOpenFlags &
54,427✔
114
                         ~(GDAL_OF_FROM_GDALOPEN | GDAL_OF_THREAD_SAFE);
115
        if (poDS->nOpenFlags & GDAL_OF_THREAD_SAFE)
54,427✔
116
            nOpenFlags |= GDAL_OF_THREAD_SAFE;
905✔
117
        poDS->nOpenFlags = nOpenFlags;
54,427✔
118

119
        if (strlen(poDS->GetDescription()) == 0)
54,427✔
120
            poDS->SetDescription(poOpenInfo->pszFilename);
11,345✔
121

122
        if (poDS->poDriver == nullptr)
54,410✔
123
            poDS->poDriver = this;
50,538✔
124

125
        if (poDS->papszOpenOptions == nullptr && bSetOpenOptions)
54,410✔
126
        {
127
            poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
29✔
128
        }
129

130
        if (!(poOpenInfo->nOpenFlags & GDAL_OF_INTERNAL))
54,410✔
131
        {
132
            if (CPLGetPID() != GDALGetResponsiblePIDForCurrentThread())
44,953✔
133
                CPLDebug(
6✔
134
                    "GDAL",
135
                    "GDALOpen(%s, this=%p) succeeds as "
136
                    "%s (pid=%d, responsiblePID=%d).",
137
                    poOpenInfo->pszFilename, poDS, GetDescription(),
6✔
138
                    static_cast<int>(CPLGetPID()),
6✔
139
                    static_cast<int>(GDALGetResponsiblePIDForCurrentThread()));
6✔
140
            else
141
                CPLDebug("GDAL", "GDALOpen(%s, this=%p) succeeds as %s.",
44,952✔
142
                         poOpenInfo->pszFilename, poDS, GetDescription());
44,958✔
143

144
            poDS->AddToDatasetOpenList();
45,027✔
145
        }
146
    }
147

148
    return poDS;
432,469✔
149
}
150

151
//! @endcond
152

153
/************************************************************************/
154
/*                               Create()                               */
155
/************************************************************************/
156

157
/**
158
 * \brief Create a new dataset with this driver.
159
 *
160
 * What argument values are legal for particular drivers is driver specific,
161
 * and there is no way to query in advance to establish legal values.
162
 *
163
 * That function will try to validate the creation option list passed to the
164
 * driver with the GDALValidateCreationOptions() method. This check can be
165
 * disabled by defining the configuration option
166
 * GDAL_VALIDATE_CREATION_OPTIONS=NO.
167
 *
168
 * After you have finished working with the returned dataset, it is
169
 * <b>required</b> to close it with GDALClose(). This does not only close the
170
 * file handle, but also ensures that all the data and metadata has been written
171
 * to the dataset (GDALFlushCache() is not sufficient for that purpose).
172
 *
173
 * In GDAL 2, the arguments nXSize, nYSize and nBands can be passed to 0 when
174
 * creating a vector-only dataset for a compatible driver.
175
 *
176
 * Equivalent of the C function GDALCreate().
177
 *
178
 * @param pszFilename the name of the dataset to create.  UTF-8 encoded.
179
 * @param nXSize width of created raster in pixels.
180
 * @param nYSize height of created raster in pixels.
181
 * @param nBands number of bands.
182
 * @param eType type of raster.
183
 * @param papszOptions list of driver specific control parameters.
184
 * The APPEND_SUBDATASET=YES option can be
185
 * specified to avoid prior destruction of existing dataset.
186
 *
187
 * @return NULL on failure, or a new GDALDataset.
188
 */
189

190
GDALDataset *GDALDriver::Create(const char *pszFilename, int nXSize, int nYSize,
21,734✔
191
                                int nBands, GDALDataType eType,
192
                                CSLConstList papszOptions)
193

194
{
195
    /* -------------------------------------------------------------------- */
196
    /*      Does this format support creation.                              */
197
    /* -------------------------------------------------------------------- */
198
    pfnCreate = GetCreateCallback();
21,734✔
199
    if (CPL_UNLIKELY(pfnCreate == nullptr && pfnCreateEx == nullptr &&
21,734✔
200
                     pfnCreateVectorOnly == nullptr))
201
    {
202
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
203
                 "GDALDriver::Create() ... no create method implemented"
204
                 " for this format.");
205

206
        return nullptr;
1✔
207
    }
208
    /* -------------------------------------------------------------------- */
209
    /*      Do some rudimentary argument checking.                          */
210
    /* -------------------------------------------------------------------- */
211
    if (CPL_UNLIKELY(nBands < 0))
21,733✔
212
    {
213
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
214
                 "Attempt to create dataset with %d bands is illegal,"
215
                 "Must be >= 0.",
216
                 nBands);
217
        return nullptr;
1✔
218
    }
219

220
    if (CPL_UNLIKELY(GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
21,732✔
221
                     GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
222
                     (nXSize < 1 || nYSize < 1)))
21,732✔
223
    {
224
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
225
                 "Attempt to create %dx%d dataset is illegal,"
226
                 "sizes must be larger than zero.",
227
                 nXSize, nYSize);
228
        return nullptr;
1✔
229
    }
230

231
    if (CPL_UNLIKELY(nBands != 0 &&
21,731✔
232
                     (eType == GDT_Unknown || eType == GDT_TypeCount)))
233
    {
234
        CPLError(CE_Failure, CPLE_IllegalArg,
1✔
235
                 "Illegal GDT_Unknown/GDT_TypeCount argument");
236
        return nullptr;
1✔
237
    }
238

239
    /* -------------------------------------------------------------------- */
240
    /*      Make sure we cleanup if there is an existing dataset of this    */
241
    /*      name.  But even if that seems to fail we will continue since    */
242
    /*      it might just be a corrupt file or something.                   */
243
    /* -------------------------------------------------------------------- */
244
    if (!CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false))
21,730✔
245
    {
246
        // Someone issuing Create("foo.tif") on a
247
        // memory driver doesn't expect files with those names to be deleted
248
        // on a file system...
249
        // This is somewhat messy. Ideally there should be a way for the
250
        // driver to overload the default behavior
251
        if (!EQUAL(GetDescription(), "MEM") &&
21,711✔
252
            !EQUAL(GetDescription(), "Memory") &&
34,243✔
253
            // ogr2ogr -f PostgreSQL might reach the Delete method of the
254
            // PostgisRaster driver which is undesirable
255
            !EQUAL(GetDescription(), "PostgreSQL"))
12,532✔
256
        {
257
            QuietDelete(pszFilename);
12,530✔
258
        }
259
    }
260

261
    /* -------------------------------------------------------------------- */
262
    /*      Validate creation options.                                      */
263
    /* -------------------------------------------------------------------- */
264
    if (CPLTestBool(
21,730✔
265
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
266
        GDALValidateCreationOptions(this, papszOptions);
21,730✔
267

268
    /* -------------------------------------------------------------------- */
269
    /*      Proceed with creation.                                          */
270
    /* -------------------------------------------------------------------- */
271
    CPLDebug("GDAL", "GDALDriver::Create(%s,%s,%d,%d,%d,%s,%p)",
43,460✔
272
             GetDescription(), pszFilename, nXSize, nYSize, nBands,
21,730✔
273
             GDALGetDataTypeName(eType), papszOptions);
274

275
    GDALDataset *poDS = nullptr;
21,730✔
276
    if (pfnCreateEx != nullptr)
21,730✔
277
    {
278
        poDS = pfnCreateEx(this, pszFilename, nXSize, nYSize, nBands, eType,
×
279
                           const_cast<char **>(papszOptions));
280
    }
281
    else if (pfnCreate != nullptr)
21,730✔
282
    {
283
        poDS = pfnCreate(pszFilename, nXSize, nYSize, nBands, eType,
21,730✔
284
                         const_cast<char **>(papszOptions));
285
    }
286
    else if (nBands < 1)
×
287
    {
288
        poDS = pfnCreateVectorOnly(this, pszFilename,
×
289
                                   const_cast<char **>(papszOptions));
290
    }
291

292
    if (poDS != nullptr)
21,730✔
293
    {
294
        if (poDS->GetDescription() == nullptr ||
40,984✔
295
            strlen(poDS->GetDescription()) == 0)
20,492✔
296
            poDS->SetDescription(pszFilename);
17,921✔
297

298
        if (poDS->poDriver == nullptr)
20,492✔
299
            poDS->poDriver = this;
19,862✔
300

301
        poDS->AddToDatasetOpenList();
20,492✔
302
    }
303

304
    return poDS;
21,730✔
305
}
306

307
/************************************************************************/
308
/*                             GDALCreate()                             */
309
/************************************************************************/
310

311
/**
312
 * \brief Create a new dataset with this driver.
313
 *
314
 * @see GDALDriver::Create()
315
 */
316

317
GDALDatasetH CPL_DLL CPL_STDCALL GDALCreate(GDALDriverH hDriver,
18,044✔
318
                                            const char *pszFilename, int nXSize,
319
                                            int nYSize, int nBands,
320
                                            GDALDataType eBandType,
321
                                            CSLConstList papszOptions)
322

323
{
324
    VALIDATE_POINTER1(hDriver, "GDALCreate", nullptr);
18,044✔
325

326
    GDALDatasetH hDS = GDALDriver::FromHandle(hDriver)->Create(
18,044✔
327
        pszFilename, nXSize, nYSize, nBands, eBandType, papszOptions);
328

329
#ifdef OGRAPISPY_ENABLED
330
    if (nBands < 1)
18,044✔
331
    {
332
        OGRAPISpyCreateDataSource(hDriver, pszFilename,
3,265✔
333
                                  const_cast<char **>(papszOptions), hDS);
334
    }
335
#endif
336

337
    return hDS;
18,044✔
338
}
339

340
/************************************************************************/
341
/*                       CreateMultiDimensional()                       */
342
/************************************************************************/
343

344
/**
345
 * \brief Create a new multidimensional dataset with this driver.
346
 *
347
 * Only drivers that advertise the GDAL_DCAP_MULTIDIM_RASTER capability and
348
 * implement the pfnCreateMultiDimensional method might return a non nullptr
349
 * GDALDataset.
350
 *
351
 * This is the same as the C function GDALCreateMultiDimensional().
352
 *
353
 * @param pszFilename  the name of the dataset to create.  UTF-8 encoded.
354
 * @param papszRootGroupOptions driver specific options regarding the creation
355
 *                              of the root group. Might be nullptr.
356
 * @param papszOptions driver specific options regarding the creation
357
 *                     of the dataset. Might be nullptr.
358
 * @return a new dataset, or nullptr in case of failure.
359
 *
360
 * @since GDAL 3.1
361
 */
362

363
GDALDataset *
364
GDALDriver::CreateMultiDimensional(const char *pszFilename,
465✔
365
                                   CSLConstList papszRootGroupOptions,
366
                                   CSLConstList papszOptions)
367

368
{
369
    /* -------------------------------------------------------------------- */
370
    /*      Does this format support creation.                              */
371
    /* -------------------------------------------------------------------- */
372
    pfnCreateMultiDimensional = GetCreateMultiDimensionalCallback();
465✔
373
    if (pfnCreateMultiDimensional == nullptr)
465✔
374
    {
375
        CPLError(CE_Failure, CPLE_NotSupported,
×
376
                 "GDALDriver::CreateMultiDimensional() ... "
377
                 "no CreateMultiDimensional method implemented "
378
                 "for this format.");
379

380
        return nullptr;
×
381
    }
382

383
    /* -------------------------------------------------------------------- */
384
    /*      Validate creation options.                                      */
385
    /* -------------------------------------------------------------------- */
386
    if (CPLTestBool(
465✔
387
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
388
    {
389
        const char *pszOptionList =
390
            GetMetadataItem(GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST);
465✔
391
        CPLString osDriver;
930✔
392
        osDriver.Printf("driver %s", GetDescription());
465✔
393
        GDALValidateOptions(pszOptionList, papszOptions, "creation option",
465✔
394
                            osDriver);
395
    }
396

397
    auto poDstDS = pfnCreateMultiDimensional(pszFilename, papszRootGroupOptions,
465✔
398
                                             papszOptions);
399

400
    if (poDstDS != nullptr)
465✔
401
    {
402
        if (poDstDS->GetDescription() == nullptr ||
926✔
403
            strlen(poDstDS->GetDescription()) == 0)
463✔
404
            poDstDS->SetDescription(pszFilename);
91✔
405

406
        if (poDstDS->poDriver == nullptr)
463✔
407
            poDstDS->poDriver = this;
461✔
408
    }
409

410
    return poDstDS;
465✔
411
}
412

413
/************************************************************************/
414
/*                       GDALCreateMultiDimensional()                   */
415
/************************************************************************/
416

417
/** \brief Create a new multidimensional dataset with this driver.
418
 *
419
 * This is the same as the C++ method GDALDriver::CreateMultiDimensional().
420
 */
421
GDALDatasetH GDALCreateMultiDimensional(GDALDriverH hDriver,
423✔
422
                                        const char *pszName,
423
                                        CSLConstList papszRootGroupOptions,
424
                                        CSLConstList papszOptions)
425
{
426
    VALIDATE_POINTER1(hDriver, __func__, nullptr);
423✔
427
    VALIDATE_POINTER1(pszName, __func__, nullptr);
423✔
428
    return GDALDataset::ToHandle(
423✔
429
        GDALDriver::FromHandle(hDriver)->CreateMultiDimensional(
430
            pszName, papszRootGroupOptions, papszOptions));
423✔
431
}
432

433
/************************************************************************/
434
/*                  DefaultCreateCopyMultiDimensional()                 */
435
/************************************************************************/
436

437
//! @cond Doxygen_Suppress
438

439
CPLErr GDALDriver::DefaultCreateCopyMultiDimensional(
19✔
440
    GDALDataset *poSrcDS, GDALDataset *poDstDS, bool bStrict,
441
    CSLConstList papszOptions, GDALProgressFunc pfnProgress,
442
    void *pProgressData)
443
{
444
    if (pfnProgress == nullptr)
19✔
445
        pfnProgress = GDALDummyProgress;
2✔
446

447
    auto poSrcRG = poSrcDS->GetRootGroup();
38✔
448
    if (!poSrcRG)
19✔
449
        return CE_Failure;
×
450
    auto poDstRG = poDstDS->GetRootGroup();
38✔
451
    if (!poDstRG)
19✔
452
        return CE_Failure;
×
453
    GUInt64 nCurCost = 0;
19✔
454
    return poDstRG->CopyFrom(poDstRG, poSrcDS, poSrcRG, bStrict, nCurCost,
38✔
455
                             poSrcRG->GetTotalCopyCost(), pfnProgress,
456
                             pProgressData, papszOptions)
19✔
457
               ? CE_None
19✔
458
               : CE_Failure;
19✔
459
}
460

461
//! @endcond
462

463
/************************************************************************/
464
/*                          DefaultCopyMasks()                          */
465
/************************************************************************/
466

467
//! @cond Doxygen_Suppress
468
CPLErr GDALDriver::DefaultCopyMasks(GDALDataset *poSrcDS, GDALDataset *poDstDS,
6,329✔
469
                                    int bStrict)
470

471
{
472
    return DefaultCopyMasks(poSrcDS, poDstDS, bStrict, nullptr, nullptr,
6,329✔
473
                            nullptr);
6,329✔
474
}
475

476
CPLErr GDALDriver::DefaultCopyMasks(GDALDataset *poSrcDS, GDALDataset *poDstDS,
8,264✔
477
                                    int bStrict, CSLConstList /*papszOptions*/,
478
                                    GDALProgressFunc pfnProgress,
479
                                    void *pProgressData)
480

481
{
482
    if (pfnProgress == nullptr)
8,264✔
483
        pfnProgress = GDALDummyProgress;
6,329✔
484

485
    int nBands = poSrcDS->GetRasterCount();
8,264✔
486
    if (nBands == 0)
8,264✔
487
        return CE_None;
×
488

489
    /* -------------------------------------------------------------------- */
490
    /*      Try to copy mask if it seems appropriate.                       */
491
    /* -------------------------------------------------------------------- */
492
    const char *papszOptions[2] = {"COMPRESSED=YES", nullptr};
8,264✔
493
    CPLErr eErr = CE_None;
8,264✔
494

495
    int nTotalBandsWithMask = 0;
8,264✔
496
    for (int iBand = 0; iBand < nBands; ++iBand)
29,040✔
497
    {
498
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
20,776✔
499

500
        int nMaskFlags = poSrcBand->GetMaskFlags();
20,776✔
501
        if (!(nMaskFlags &
20,776✔
502
              (GMF_ALL_VALID | GMF_PER_DATASET | GMF_ALPHA | GMF_NODATA)))
503
        {
504
            nTotalBandsWithMask++;
7✔
505
        }
506
    }
507

508
    int iBandWithMask = 0;
8,264✔
509
    for (int iBand = 0; eErr == CE_None && iBand < nBands; ++iBand)
29,040✔
510
    {
511
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
20,776✔
512

513
        int nMaskFlags = poSrcBand->GetMaskFlags();
20,776✔
514
        if (eErr == CE_None && !(nMaskFlags & (GMF_ALL_VALID | GMF_PER_DATASET |
20,776✔
515
                                               GMF_ALPHA | GMF_NODATA)))
516
        {
517
            GDALRasterBand *poDstBand = poDstDS->GetRasterBand(iBand + 1);
7✔
518
            if (poDstBand != nullptr)
7✔
519
            {
520
                eErr = poDstBand->CreateMaskBand(nMaskFlags);
7✔
521
                if (eErr == CE_None)
7✔
522
                {
523
                    // coverity[divide_by_zero]
524
                    void *pScaledData = GDALCreateScaledProgress(
14✔
525
                        double(iBandWithMask) / nTotalBandsWithMask,
7✔
526
                        double(iBandWithMask + 1) / nTotalBandsWithMask,
7✔
527
                        pfnProgress, pProgressData);
528
                    eErr = GDALRasterBandCopyWholeRaster(
14✔
529
                        poSrcBand->GetMaskBand(), poDstBand->GetMaskBand(),
7✔
530
                        papszOptions, GDALScaledProgress, pScaledData);
531
                    GDALDestroyScaledProgress(pScaledData);
7✔
532
                }
533
                else if (!bStrict)
×
534
                {
535
                    eErr = CE_None;
×
536
                }
537
            }
538
        }
539
    }
540

541
    /* -------------------------------------------------------------------- */
542
    /*      Try to copy a per-dataset mask if we have one.                  */
543
    /* -------------------------------------------------------------------- */
544
    const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
8,264✔
545
    if (eErr == CE_None &&
8,264✔
546
        !(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
8,264✔
547
        (nMaskFlags & GMF_PER_DATASET))
12✔
548
    {
549
        eErr = poDstDS->CreateMaskBand(nMaskFlags);
8✔
550
        if (eErr == CE_None)
8✔
551
        {
552
            eErr = GDALRasterBandCopyWholeRaster(
8✔
553
                poSrcDS->GetRasterBand(1)->GetMaskBand(),
8✔
554
                poDstDS->GetRasterBand(1)->GetMaskBand(), papszOptions,
8✔
555
                pfnProgress, pProgressData);
556
        }
557
        else if (!bStrict)
×
558
        {
559
            eErr = CE_None;
×
560
        }
561
    }
562

563
    return eErr;
8,264✔
564
}
565

566
/************************************************************************/
567
/*                         DefaultCreateCopy()                          */
568
/************************************************************************/
569

570
GDALDataset *GDALDriver::DefaultCreateCopy(const char *pszFilename,
1,342✔
571
                                           GDALDataset *poSrcDS, int bStrict,
572
                                           CSLConstList papszOptions,
573
                                           GDALProgressFunc pfnProgress,
574
                                           void *pProgressData)
575

576
{
577
    if (pfnProgress == nullptr)
1,342✔
578
        pfnProgress = GDALDummyProgress;
×
579

580
    CPLErrorReset();
1,342✔
581

582
    /* -------------------------------------------------------------------- */
583
    /*      Use multidimensional raster API if available.                   */
584
    /* -------------------------------------------------------------------- */
585
    auto poSrcGroup = poSrcDS->GetRootGroup();
2,684✔
586
    if (poSrcGroup != nullptr && GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
1,342✔
587
    {
588
        CPLStringList aosDatasetCO;
34✔
589
        for (const char *pszOption : cpl::Iterate(papszOptions))
24✔
590
        {
591
            if (!STARTS_WITH_CI(pszOption, "ARRAY:"))
7✔
592
                aosDatasetCO.AddString(pszOption);
×
593
        }
594
        auto poDstDS = std::unique_ptr<GDALDataset>(
595
            CreateMultiDimensional(pszFilename, nullptr, aosDatasetCO.List()));
34✔
596
        if (!poDstDS)
17✔
597
            return nullptr;
×
598
        auto poDstGroup = poDstDS->GetRootGroup();
34✔
599
        if (!poDstGroup)
17✔
600
            return nullptr;
×
601
        if (DefaultCreateCopyMultiDimensional(
17✔
602
                poSrcDS, poDstDS.get(), CPL_TO_BOOL(bStrict), papszOptions,
17✔
603
                pfnProgress, pProgressData) != CE_None)
17✔
604
            return nullptr;
×
605
        return poDstDS.release();
17✔
606
    }
607

608
    /* -------------------------------------------------------------------- */
609
    /*      Validate that we can create the output as requested.            */
610
    /* -------------------------------------------------------------------- */
611
    const int nXSize = poSrcDS->GetRasterXSize();
1,325✔
612
    const int nYSize = poSrcDS->GetRasterYSize();
1,325✔
613
    const int nBands = poSrcDS->GetRasterCount();
1,325✔
614

615
    CPLDebug("GDAL", "Using default GDALDriver::CreateCopy implementation.");
1,325✔
616

617
    const int nLayerCount = poSrcDS->GetLayerCount();
1,325✔
618
    if (nBands == 0 && nLayerCount == 0 &&
1,348✔
619
        GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
23✔
620
    {
621
        CPLError(CE_Failure, CPLE_NotSupported,
16✔
622
                 "GDALDriver::DefaultCreateCopy does not support zero band");
623
        return nullptr;
16✔
624
    }
625
    if (poSrcDS->GetDriver() != nullptr &&
1,309✔
626
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
1,253✔
627
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
1,246✔
628
        GetMetadataItem(GDAL_DCAP_RASTER) == nullptr &&
2,565✔
629
        GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)
3✔
630
    {
631
        CPLError(CE_Failure, CPLE_NotSupported,
3✔
632
                 "Source driver is raster-only whereas output driver is "
633
                 "vector-only");
634
        return nullptr;
3✔
635
    }
636
    else if (poSrcDS->GetDriver() != nullptr &&
1,306✔
637
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) ==
1,250✔
638
                 nullptr &&
7✔
639
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) !=
7✔
640
                 nullptr &&
7✔
641
             GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
2,556✔
642
             GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
×
643
    {
644
        CPLError(CE_Failure, CPLE_NotSupported,
×
645
                 "Source driver is vector-only whereas output driver is "
646
                 "raster-only");
647
        return nullptr;
×
648
    }
649

650
    if (!pfnProgress(0.0, nullptr, pProgressData))
1,306✔
651
    {
652
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
2✔
653
        return nullptr;
2✔
654
    }
655

656
    /* -------------------------------------------------------------------- */
657
    /*      Propagate some specific structural metadata as options if it    */
658
    /*      appears to be supported by the target driver and the caller     */
659
    /*      didn't provide values.                                          */
660
    /* -------------------------------------------------------------------- */
661
    char **papszCreateOptions = CSLDuplicate(papszOptions);
1,304✔
662
    const char *const apszOptItems[] = {"NBITS", "IMAGE_STRUCTURE", "PIXELTYPE",
1,304✔
663
                                        "IMAGE_STRUCTURE", nullptr};
664

665
    for (int iOptItem = 0; nBands > 0 && apszOptItems[iOptItem] != nullptr;
3,880✔
666
         iOptItem += 2)
2,576✔
667
    {
668
        // does the source have this metadata item on the first band?
669
        auto poBand = poSrcDS->GetRasterBand(1);
2,576✔
670
        poBand->EnablePixelTypeSignedByteWarning(false);
2,576✔
671
        const char *pszValue = poBand->GetMetadataItem(
5,152✔
672
            apszOptItems[iOptItem], apszOptItems[iOptItem + 1]);
2,576✔
673
        poBand->EnablePixelTypeSignedByteWarning(true);
2,576✔
674

675
        if (pszValue == nullptr)
2,576✔
676
            continue;
2,575✔
677

678
        // Do not override provided value.
679
        if (CSLFetchNameValue(papszCreateOptions, pszValue) != nullptr)
1✔
680
            continue;
×
681

682
        // Does this appear to be a supported creation option on this driver?
683
        const char *pszOptionList =
684
            GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
1✔
685

686
        if (pszOptionList == nullptr ||
1✔
687
            strstr(pszOptionList, apszOptItems[iOptItem]) == nullptr)
1✔
688
            continue;
×
689

690
        papszCreateOptions = CSLSetNameValue(papszCreateOptions,
1✔
691
                                             apszOptItems[iOptItem], pszValue);
1✔
692
    }
693

694
    /* -------------------------------------------------------------------- */
695
    /*      Create destination dataset.                                     */
696
    /* -------------------------------------------------------------------- */
697
    GDALDataType eType = GDT_Unknown;
1,304✔
698

699
    if (nBands > 0)
1,304✔
700
        eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1,288✔
701
    GDALDataset *poDstDS =
702
        Create(pszFilename, nXSize, nYSize, nBands, eType, papszCreateOptions);
1,304✔
703

704
    CSLDestroy(papszCreateOptions);
1,304✔
705

706
    if (poDstDS == nullptr)
1,304✔
707
        return nullptr;
316✔
708

709
    int nDstBands = poDstDS->GetRasterCount();
988✔
710
    CPLErr eErr = CE_None;
988✔
711
    if (nDstBands != nBands)
988✔
712
    {
713
        if (GetMetadataItem(GDAL_DCAP_RASTER) != nullptr)
×
714
        {
715
            // Should not happen for a well-behaved driver.
716
            CPLError(
×
717
                CE_Failure, CPLE_AppDefined,
718
                "Output driver created only %d bands whereas %d were expected",
719
                nDstBands, nBands);
720
            eErr = CE_Failure;
×
721
        }
722
        nDstBands = 0;
×
723
    }
724

725
    /* -------------------------------------------------------------------- */
726
    /*      Try setting the projection and geotransform if it seems         */
727
    /*      suitable.                                                       */
728
    /* -------------------------------------------------------------------- */
729
    double adfGeoTransform[6] = {};
988✔
730

731
    if (nDstBands == 0 && !bStrict)
988✔
732
        CPLTurnFailureIntoWarning(true);
4✔
733

734
    if (eErr == CE_None &&
1,976✔
735
        poSrcDS->GetGeoTransform(adfGeoTransform) == CE_None
988✔
736
        // TODO(schwehr): The default value check should be a function.
737
        && (adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 ||
1,980✔
738
            adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 ||
4✔
739
            adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0))
4✔
740
    {
741
        eErr = poDstDS->SetGeoTransform(adfGeoTransform);
878✔
742
        if (!bStrict)
878✔
743
            eErr = CE_None;
533✔
744
    }
745

746
    if (eErr == CE_None)
988✔
747
    {
748
        const auto poSrcSRS = poSrcDS->GetSpatialRef();
979✔
749
        if (poSrcSRS && !poSrcSRS->IsEmpty())
979✔
750
        {
751
            eErr = poDstDS->SetSpatialRef(poSrcSRS);
817✔
752
            if (!bStrict)
817✔
753
                eErr = CE_None;
504✔
754
        }
755
    }
756

757
    /* -------------------------------------------------------------------- */
758
    /*      Copy GCPs.                                                      */
759
    /* -------------------------------------------------------------------- */
760
    if (poSrcDS->GetGCPCount() > 0 && eErr == CE_None)
988✔
761
    {
762
        eErr = poDstDS->SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
2✔
763
                                poSrcDS->GetGCPProjection());
764
        if (!bStrict)
2✔
765
            eErr = CE_None;
1✔
766
    }
767

768
    if (nDstBands == 0 && !bStrict)
988✔
769
        CPLTurnFailureIntoWarning(false);
4✔
770

771
    /* -------------------------------------------------------------------- */
772
    /*      Copy metadata.                                                  */
773
    /* -------------------------------------------------------------------- */
774
    DefaultCopyMetadata(poSrcDS, poDstDS, papszOptions, nullptr);
988✔
775

776
    /* -------------------------------------------------------------------- */
777
    /*      Loop copying bands.                                             */
778
    /* -------------------------------------------------------------------- */
779
    for (int iBand = 0; eErr == CE_None && iBand < nDstBands; ++iBand)
2,527✔
780
    {
781
        GDALRasterBand *const poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
1,539✔
782
        GDALRasterBand *const poDstBand = poDstDS->GetRasterBand(iBand + 1);
1,539✔
783

784
        /* --------------------------------------------------------------------
785
         */
786
        /*      Do we need to copy a colortable. */
787
        /* --------------------------------------------------------------------
788
         */
789
        GDALColorTable *const poCT = poSrcBand->GetColorTable();
1,539✔
790
        if (poCT != nullptr)
1,539✔
791
            poDstBand->SetColorTable(poCT);
29✔
792

793
        /* --------------------------------------------------------------------
794
         */
795
        /*      Do we need to copy other metadata?  Most of this is */
796
        /*      non-critical, so lets not bother folks if it fails are we */
797
        /*      are not in strict mode. */
798
        /* --------------------------------------------------------------------
799
         */
800
        if (!bStrict)
1,539✔
801
            CPLTurnFailureIntoWarning(true);
928✔
802

803
        if (strlen(poSrcBand->GetDescription()) > 0)
1,539✔
804
            poDstBand->SetDescription(poSrcBand->GetDescription());
52✔
805

806
        if (CSLCount(poSrcBand->GetMetadata()) > 0)
1,539✔
807
            poDstBand->SetMetadata(poSrcBand->GetMetadata());
112✔
808

809
        int bSuccess = FALSE;
1,539✔
810
        double dfValue = poSrcBand->GetOffset(&bSuccess);
1,539✔
811
        if (bSuccess && dfValue != 0.0)
1,539✔
812
            poDstBand->SetOffset(dfValue);
5✔
813

814
        dfValue = poSrcBand->GetScale(&bSuccess);
1,539✔
815
        if (bSuccess && dfValue != 1.0)
1,539✔
816
            poDstBand->SetScale(dfValue);
4✔
817

818
        GDALCopyNoDataValue(poDstBand, poSrcBand);
1,539✔
819

820
        if (poSrcBand->GetColorInterpretation() != GCI_Undefined &&
2,527✔
821
            poSrcBand->GetColorInterpretation() !=
988✔
822
                poDstBand->GetColorInterpretation())
988✔
823
            poDstBand->SetColorInterpretation(
773✔
824
                poSrcBand->GetColorInterpretation());
773✔
825

826
        char **papszCatNames = poSrcBand->GetCategoryNames();
1,539✔
827
        if (nullptr != papszCatNames)
1,539✔
828
            poDstBand->SetCategoryNames(papszCatNames);
1✔
829

830
        // Only copy RAT if it is of reasonable size to fit in memory
831
        GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
1,539✔
832
        if (poRAT != nullptr && static_cast<GIntBig>(poRAT->GetColumnCount()) *
1,541✔
833
                                        poRAT->GetRowCount() <
2✔
834
                                    1024 * 1024)
835
        {
836
            poDstBand->SetDefaultRAT(poRAT);
2✔
837
        }
838

839
        if (!bStrict)
1,539✔
840
        {
841
            CPLTurnFailureIntoWarning(false);
928✔
842
        }
843
        else
844
        {
845
            eErr = CPLGetLastErrorType();
611✔
846
        }
847
    }
848

849
    /* -------------------------------------------------------------------- */
850
    /*      Copy image data.                                                */
851
    /* -------------------------------------------------------------------- */
852
    if (eErr == CE_None && nDstBands > 0)
988✔
853
    {
854
        const char *const apszCopyRasterOptionsSkipHoles[] = {"SKIP_HOLES=YES",
943✔
855
                                                              nullptr};
856
        const bool bSkipHoles = CPLTestBool(
943✔
857
            CSLFetchNameValueDef(papszOptions, "SKIP_HOLES", "FALSE"));
858
        eErr = GDALDatasetCopyWholeRaster(
943✔
859
            poSrcDS, poDstDS,
860
            bSkipHoles ? apszCopyRasterOptionsSkipHoles : nullptr, pfnProgress,
861
            pProgressData);
862
    }
863

864
    /* -------------------------------------------------------------------- */
865
    /*      Should we copy some masks over?                                 */
866
    /* -------------------------------------------------------------------- */
867
    if (eErr == CE_None && nDstBands > 0)
988✔
868
        eErr = DefaultCopyMasks(poSrcDS, poDstDS, eErr);
926✔
869

870
    /* -------------------------------------------------------------------- */
871
    /*      Copy vector layers                                              */
872
    /* -------------------------------------------------------------------- */
873
    if (eErr == CE_None)
988✔
874
    {
875
        if (nLayerCount > 0 && poDstDS->TestCapability(ODsCCreateLayer))
939✔
876
        {
877
            for (int iLayer = 0; iLayer < nLayerCount; ++iLayer)
20✔
878
            {
879
                OGRLayer *poLayer = poSrcDS->GetLayer(iLayer);
10✔
880

881
                if (poLayer == nullptr)
10✔
882
                    continue;
×
883

884
                poDstDS->CopyLayer(poLayer, poLayer->GetName(), nullptr);
10✔
885
            }
886
        }
887
    }
888

889
    /* -------------------------------------------------------------------- */
890
    /*      Try to cleanup the output dataset if the translation failed.    */
891
    /* -------------------------------------------------------------------- */
892
    if (eErr != CE_None)
988✔
893
    {
894
        delete poDstDS;
49✔
895
        if (!CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false))
49✔
896
        {
897
            // Only delete if creating a new file
898
            Delete(pszFilename);
49✔
899
        }
900
        return nullptr;
49✔
901
    }
902
    else
903
    {
904
        CPLErrorReset();
939✔
905
    }
906

907
    return poDstDS;
939✔
908
}
909

910
/************************************************************************/
911
/*                       DefaultCopyMetadata()                          */
912
/************************************************************************/
913

914
void GDALDriver::DefaultCopyMetadata(GDALDataset *poSrcDS, GDALDataset *poDstDS,
5,607✔
915
                                     CSLConstList papszOptions,
916
                                     CSLConstList papszExcludedDomains)
917
{
918
    const char *pszCopySrcMDD =
919
        CSLFetchNameValueDef(papszOptions, "COPY_SRC_MDD", "AUTO");
5,607✔
920
    char **papszSrcMDD = CSLFetchNameValueMultiple(papszOptions, "SRC_MDD");
5,607✔
921
    if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
5,607✔
922
        papszSrcMDD)
923
    {
924
        if ((!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
4✔
925
             CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0) &&
2✔
926
            CSLFindString(papszExcludedDomains, "") < 0 &&
11,054✔
927
            CSLFindString(papszExcludedDomains, "_DEFAULT_") < 0)
5,445✔
928
        {
929
            if (poSrcDS->GetMetadata() != nullptr)
5,445✔
930
                poDstDS->SetMetadata(poSrcDS->GetMetadata());
774✔
931
        }
932

933
        /* -------------------------------------------------------------------- */
934
        /*      Copy transportable special domain metadata.                     */
935
        /*      It would be nice to copy geolocation, but it is pretty fragile. */
936
        /* -------------------------------------------------------------------- */
937
        constexpr const char *apszDefaultDomains[] = {
5,605✔
938
            "RPC", "xml:XMP", "json:ISIS3", "json:VICAR"};
939
        for (const char *pszDomain : apszDefaultDomains)
28,025✔
940
        {
941
            if ((!papszSrcMDD || CSLFindString(papszSrcMDD, pszDomain) >= 0) &&
44,824✔
942
                CSLFindString(papszExcludedDomains, pszDomain) < 0)
22,404✔
943
            {
944
                char **papszMD = poSrcDS->GetMetadata(pszDomain);
22,404✔
945
                if (papszMD)
22,404✔
946
                    poDstDS->SetMetadata(papszMD, pszDomain);
5✔
947
            }
948
        }
949

950
        if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
5,605✔
951
            papszSrcMDD)
952
        {
953
            for (const char *pszDomain :
24✔
954
                 CPLStringList(poSrcDS->GetMetadataDomainList()))
30✔
955
            {
956
                if (pszDomain[0] != 0 &&
36✔
957
                    (!papszSrcMDD ||
12✔
958
                     CSLFindString(papszSrcMDD, pszDomain) >= 0))
12✔
959
                {
960
                    bool bCanCopy = true;
10✔
961
                    if (CSLFindString(papszExcludedDomains, pszDomain) >= 0)
10✔
962
                    {
963
                        bCanCopy = false;
×
964
                    }
965
                    else
966
                    {
967
                        for (const char *pszOtherDomain : apszDefaultDomains)
50✔
968
                        {
969
                            if (EQUAL(pszDomain, pszOtherDomain))
40✔
970
                            {
971
                                bCanCopy = false;
×
972
                                break;
×
973
                            }
974
                        }
975
                        if (!papszSrcMDD)
10✔
976
                        {
977
                            constexpr const char *const apszReservedDomains[] =
6✔
978
                                {"IMAGE_STRUCTURE", "DERIVED_SUBDATASETS"};
979
                            for (const char *pszOtherDomain :
6✔
980
                                 apszReservedDomains)
12✔
981
                            {
982
                                if (EQUAL(pszDomain, pszOtherDomain))
10✔
983
                                {
984
                                    bCanCopy = false;
4✔
985
                                    break;
4✔
986
                                }
987
                            }
988
                        }
989
                    }
990
                    if (bCanCopy)
10✔
991
                    {
992
                        poDstDS->SetMetadata(poSrcDS->GetMetadata(pszDomain),
6✔
993
                                             pszDomain);
6✔
994
                    }
995
                }
996
            }
997
        }
998
    }
999
    CSLDestroy(papszSrcMDD);
5,607✔
1000
}
5,607✔
1001

1002
/************************************************************************/
1003
/*                      QuietDeleteForCreateCopy()                      */
1004
/************************************************************************/
1005

1006
CPLErr GDALDriver::QuietDeleteForCreateCopy(const char *pszFilename,
11,375✔
1007
                                            GDALDataset *poSrcDS)
1008
{
1009
    // Someone issuing CreateCopy("foo.tif") on a
1010
    // memory driver doesn't expect files with those names to be deleted
1011
    // on a file system...
1012
    // This is somewhat messy. Ideally there should be a way for the
1013
    // driver to overload the default behavior
1014
    if (!EQUAL(GetDescription(), "MEM") && !EQUAL(GetDescription(), "Memory") &&
22,220✔
1015
        // Also exclude database formats for which there's no file list
1016
        // and whose opening might be slow (GeoRaster in particular)
1017
        !EQUAL(GetDescription(), "GeoRaster") &&
33,065✔
1018
        !EQUAL(GetDescription(), "PostGISRaster"))
10,845✔
1019
    {
1020
        /* --------------------------------------------------------------------
1021
         */
1022
        /*      Establish list of files of output dataset if it already
1023
         * exists. */
1024
        /* --------------------------------------------------------------------
1025
         */
1026
        std::set<std::string> oSetExistingDestFiles;
21,654✔
1027
        {
1028
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
21,654✔
1029
            const char *const apszAllowedDrivers[] = {GetDescription(),
10,827✔
1030
                                                      nullptr};
10,827✔
1031
            auto poExistingOutputDS =
1032
                std::unique_ptr<GDALDataset>(GDALDataset::Open(
1033
                    pszFilename, GDAL_OF_RASTER, apszAllowedDrivers));
21,654✔
1034
            if (poExistingOutputDS)
10,827✔
1035
            {
1036
                for (const char *pszFileInList :
672✔
1037
                     CPLStringList(poExistingOutputDS->GetFileList()))
640✔
1038
                {
1039
                    oSetExistingDestFiles.insert(
1040
                        CPLString(pszFileInList).replaceAll('\\', '/'));
336✔
1041
                }
1042
            }
1043
        }
1044

1045
        /* --------------------------------------------------------------------
1046
         */
1047
        /*      Check if the source dataset shares some files with the dest
1048
         * one.*/
1049
        /* --------------------------------------------------------------------
1050
         */
1051
        std::set<std::string> oSetExistingDestFilesFoundInSource;
21,654✔
1052
        if (!oSetExistingDestFiles.empty())
10,827✔
1053
        {
1054
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
608✔
1055
            // We need to reopen in a temporary dataset for the particular
1056
            // case of overwritten a .tif.ovr file from a .tif
1057
            // If we probe the file list of the .tif, it will then open the
1058
            // .tif.ovr !
1059
            const char *const apszAllowedDrivers[] = {
304✔
1060
                poSrcDS->GetDriver() ? poSrcDS->GetDriver()->GetDescription()
304✔
1061
                                     : nullptr,
1062
                nullptr};
304✔
1063
            auto poSrcDSTmp = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1064
                poSrcDS->GetDescription(), GDAL_OF_RASTER, apszAllowedDrivers,
304✔
1065
                poSrcDS->papszOpenOptions));
608✔
1066
            if (poSrcDSTmp)
304✔
1067
            {
1068
                for (const char *pszFileInList :
203✔
1069
                     CPLStringList(poSrcDSTmp->GetFileList()))
394✔
1070
                {
1071
                    CPLString osFilename(pszFileInList);
406✔
1072
                    osFilename.replaceAll('\\', '/');
203✔
1073
                    if (cpl::contains(oSetExistingDestFiles, osFilename))
203✔
1074
                    {
1075
                        oSetExistingDestFilesFoundInSource.insert(osFilename);
16✔
1076
                    }
1077
                }
1078
            }
1079
        }
1080

1081
        // If the source file(s) and the dest one share some files in
1082
        // common, only remove the files that are *not* in common
1083
        if (!oSetExistingDestFilesFoundInSource.empty())
10,827✔
1084
        {
1085
            for (const std::string &osFilename : oSetExistingDestFiles)
36✔
1086
            {
1087
                if (!cpl::contains(oSetExistingDestFilesFoundInSource,
21✔
1088
                                   osFilename))
1089
                {
1090
                    VSIUnlink(osFilename.c_str());
5✔
1091
                }
1092
            }
1093
        }
1094

1095
        QuietDelete(pszFilename);
10,827✔
1096
    }
1097

1098
    return CE_None;
11,375✔
1099
}
1100

1101
//! @endcond
1102

1103
/************************************************************************/
1104
/*                             CreateCopy()                             */
1105
/************************************************************************/
1106

1107
/**
1108
 * \brief Create a copy of a dataset.
1109
 *
1110
 * This method will attempt to create a copy of a raster dataset with the
1111
 * indicated filename, and in this drivers format.  Band number, size,
1112
 * type, projection, geotransform and so forth are all to be copied from
1113
 * the provided template dataset.
1114
 *
1115
 * Note that many sequential write once formats (such as JPEG and PNG) don't
1116
 * implement the Create() method but do implement this CreateCopy() method.
1117
 * If the driver doesn't implement CreateCopy(), but does implement Create()
1118
 * then the default CreateCopy() mechanism built on calling Create() will
1119
 * be used.
1120
 * So to test if CreateCopy() is available, you can test if GDAL_DCAP_CREATECOPY
1121
 * or GDAL_DCAP_CREATE is set in the GDAL metadata.
1122
 *
1123
 * It is intended that CreateCopy() will often be used with a source dataset
1124
 * which is a virtual dataset allowing configuration of band types, and other
1125
 * information without actually duplicating raster data (see the VRT driver).
1126
 * This is what is done by the gdal_translate utility for example.
1127
 *
1128
 * That function will try to validate the creation option list passed to the
1129
 * driver with the GDALValidateCreationOptions() method. This check can be
1130
 * disabled by defining the configuration option
1131
 * GDAL_VALIDATE_CREATION_OPTIONS=NO.
1132
 *
1133
 * This function copy all metadata from the default domain ("")
1134
 *
1135
 * Even is bStrict is TRUE, only the <b>value</b> of the data is equivalent,
1136
 * but the data layout (INTERLEAVE as PIXEL/LINE/BAND) of the dst dataset is
1137
 * controlled by the papszOptions creation options, and may differ from the
1138
 * poSrcDS src dataset.
1139
 * Starting from GDAL 3.5, if no INTERLEAVE and COMPRESS creation option has
1140
 * been specified in papszOptions, and if the driver supports equivalent
1141
 * interleaving as the src dataset, the CreateCopy() will internally add the
1142
 * proper creation option to get the same data interleaving.
1143
 *
1144
 * After you have finished working with the returned dataset, it is
1145
 * <b>required</b> to close it with GDALClose(). This does not only close the
1146
 * file handle, but also ensures that all the data and metadata has been written
1147
 * to the dataset (GDALFlushCache() is not sufficient for that purpose).
1148
 *
1149
 * For multidimensional datasets, papszOptions can contain array creation
1150
 * options, if they are prefixed with "ARRAY:". \see GDALGroup::CopyFrom()
1151
 * documentation for further details regarding such options.
1152
 *
1153
 * @param pszFilename the name for the new dataset.  UTF-8 encoded.
1154
 * @param poSrcDS the dataset being duplicated.
1155
 * @param bStrict TRUE if the copy must be strictly equivalent, or more
1156
 * normally FALSE indicating that the copy may adapt as needed for the
1157
 * output format.
1158
 * @param papszOptions additional format dependent options controlling
1159
 * creation of the output file.
1160
 * The APPEND_SUBDATASET=YES option can be specified to avoid prior destruction
1161
 * of existing dataset.
1162
 * Starting with GDAL 3.8.0, the following options are recognized by the
1163
 * GTiff, COG, VRT, PNG au JPEG drivers:
1164
 * <ul>
1165
 * <li>COPY_SRC_MDD=AUTO/YES/NO: whether metadata domains of the source dataset
1166
 * should be copied to the destination dataset. In the default AUTO mode, only
1167
 * "safe" domains will be copied, which include the default metadata domain
1168
 * (some drivers may include other domains such as IMD, RPC, GEOLOCATION). When
1169
 * setting YES, all domains will be copied (but a few reserved ones like
1170
 * IMAGE_STRUCTURE or DERIVED_SUBDATASETS). When setting NO, no source metadata
1171
 * will be copied.
1172
 * </li>
1173
 *<li>SRC_MDD=domain_name: which source metadata domain should be copied.
1174
 * This option restricts the list of source metadata domains to be copied
1175
 * (it implies COPY_SRC_MDD=YES if it is not set). This option may be specified
1176
 * as many times as they are source domains. The default metadata domain is the
1177
 * empty string "" ("_DEFAULT_") may also be used when empty string is not practical)
1178
 * </li>
1179
 * </ul>
1180
 * @param pfnProgress a function to be used to report progress of the copy.
1181
 * @param pProgressData application data passed into progress function.
1182
 *
1183
 * @return a pointer to the newly created dataset (may be read-only access).
1184
 */
1185

1186
GDALDataset *GDALDriver::CreateCopy(const char *pszFilename,
11,022✔
1187
                                    GDALDataset *poSrcDS, int bStrict,
1188
                                    CSLConstList papszOptions,
1189
                                    GDALProgressFunc pfnProgress,
1190
                                    void *pProgressData)
1191

1192
{
1193
    if (pfnProgress == nullptr)
11,022✔
1194
        pfnProgress = GDALDummyProgress;
8,571✔
1195

1196
    const int nBandCount = poSrcDS->GetRasterCount();
11,022✔
1197

1198
    /* -------------------------------------------------------------------- */
1199
    /*      If no INTERLEAVE creation option is given, we will try to add   */
1200
    /*      one that matches the current srcDS interleaving                 */
1201
    /* -------------------------------------------------------------------- */
1202
    char **papszOptionsToDelete = nullptr;
11,022✔
1203
    const char *srcInterleave =
1204
        poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
11,022✔
1205
    if (nBandCount > 1 && srcInterleave != nullptr &&
6,777✔
1206
        CSLFetchNameValue(papszOptions, "INTERLEAVE") == nullptr &&
24,193✔
1207
        EQUAL(CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE"), "NONE"))
6,394✔
1208
    {
1209

1210
        // look for INTERLEAVE values of the driver
1211
        char **interleavesCSL = nullptr;
6,241✔
1212
        const char *pszOptionList =
1213
            this->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
6,241✔
1214
        CPLXMLNode *xmlNode =
1215
            !pszOptionList ? nullptr : CPLParseXMLString(pszOptionList);
6,241✔
1216
        for (CPLXMLNode *child = !xmlNode ? nullptr : xmlNode->psChild;
6,241✔
1217
             child != nullptr; child = child->psNext)
126,739✔
1218
        {
1219
            if ((child->eType == CXT_Element) &&
120,498✔
1220
                EQUAL(child->pszValue, "Option"))
120,498✔
1221
            {
1222
                const char *nameAttribute =
1223
                    CPLGetXMLValue(child, "name", nullptr);
120,498✔
1224
                const bool isInterleaveAttribute =
120,498✔
1225
                    nameAttribute && EQUAL(nameAttribute, "INTERLEAVE");
120,498✔
1226
                if (isInterleaveAttribute)
120,498✔
1227
                {
1228
                    for (CPLXMLNode *optionChild = child->psChild;
1,130✔
1229
                         optionChild != nullptr;
6,848✔
1230
                         optionChild = optionChild->psNext)
5,718✔
1231
                    {
1232
                        if ((optionChild->eType == CXT_Element) &&
5,718✔
1233
                            EQUAL(optionChild->pszValue, "Value"))
2,313✔
1234
                        {
1235
                            CPLXMLNode *optionChildValue = optionChild->psChild;
2,313✔
1236
                            if (optionChildValue &&
2,313✔
1237
                                (optionChildValue->eType == CXT_Text))
2,313✔
1238
                            {
1239
                                interleavesCSL = CSLAddString(
2,313✔
1240
                                    interleavesCSL, optionChildValue->pszValue);
2,313✔
1241
                            }
1242
                        }
1243
                    }
1244
                }
1245
            }
1246
        }
1247
        CPLDestroyXMLNode(xmlNode);
6,241✔
1248

1249
        const char *dstInterleaveBand =
1250
            (CSLFindString(interleavesCSL, "BAND") >= 0)  ? "BAND"
11,376✔
1251
            : (CSLFindString(interleavesCSL, "BSQ") >= 0) ? "BSQ"
5,135✔
1252
                                                          : nullptr;
6,241✔
1253
        const char *dstInterleaveLine =
1254
            (CSLFindString(interleavesCSL, "LINE") >= 0)  ? "LINE"
12,482✔
1255
            : (CSLFindString(interleavesCSL, "BIL") >= 0) ? "BIL"
6,241✔
1256
                                                          : nullptr;
6,241✔
1257
        const char *dstInterleavePixel =
1258
            (CSLFindString(interleavesCSL, "PIXEL") >= 0) ? "PIXEL"
11,376✔
1259
            : (CSLFindString(interleavesCSL, "BIP") >= 0) ? "BIP"
5,135✔
1260
                                                          : nullptr;
6,241✔
1261
        const char *dstInterleave =
6,241✔
1262
            EQUAL(srcInterleave, "BAND")    ? dstInterleaveBand
6,517✔
1263
            : EQUAL(srcInterleave, "LINE")  ? dstInterleaveLine
550✔
1264
            : EQUAL(srcInterleave, "PIXEL") ? dstInterleavePixel
274✔
1265
                                            : nullptr;
1266
        CSLDestroy(interleavesCSL);
6,241✔
1267

1268
        if (dstInterleave != nullptr)
6,241✔
1269
        {
1270
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1,130✔
1271
            papszOptionsToDelete = CSLSetNameValue(papszOptionsToDelete,
1,130✔
1272
                                                   "INTERLEAVE", dstInterleave);
1273
            papszOptionsToDelete = CSLSetNameValue(
1,130✔
1274
                papszOptionsToDelete, "@INTERLEAVE_ADDED_AUTOMATICALLY", "YES");
1275
            papszOptions = papszOptionsToDelete;
1,130✔
1276
        }
1277
    }
1278

1279
    /* -------------------------------------------------------------------- */
1280
    /*      Make sure we cleanup if there is an existing dataset of this    */
1281
    /*      name.  But even if that seems to fail we will continue since    */
1282
    /*      it might just be a corrupt file or something.                   */
1283
    /* -------------------------------------------------------------------- */
1284
    const bool bAppendSubdataset =
1285
        CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
11,022✔
1286
    // Note: @QUIET_DELETE_ON_CREATE_COPY is set to NO by the KMLSuperOverlay
1287
    // driver when writing a .kmz file. Also by GDALTranslate() if it has
1288
    // already done a similar job.
1289
    if (!bAppendSubdataset &&
22,024✔
1290
        CPLFetchBool(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY", true))
11,002✔
1291
    {
1292
        QuietDeleteForCreateCopy(pszFilename, poSrcDS);
8,699✔
1293
    }
1294

1295
    int iIdxQuietDeleteOnCreateCopy =
1296
        CSLPartialFindString(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY=");
11,022✔
1297
    if (iIdxQuietDeleteOnCreateCopy >= 0)
11,022✔
1298
    {
1299
        if (papszOptionsToDelete == nullptr)
2,303✔
1300
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1,310✔
1301
        papszOptionsToDelete = CSLRemoveStrings(
2,303✔
1302
            papszOptionsToDelete, iIdxQuietDeleteOnCreateCopy, 1, nullptr);
1303
        papszOptions = papszOptionsToDelete;
2,303✔
1304
    }
1305

1306
    /* -------------------------------------------------------------------- */
1307
    /*      If _INTERNAL_DATASET=YES, the returned dataset will not be      */
1308
    /*      registered in the global list of open datasets.                 */
1309
    /* -------------------------------------------------------------------- */
1310
    const int iIdxInternalDataset =
1311
        CSLPartialFindString(papszOptions, "_INTERNAL_DATASET=");
11,022✔
1312
    bool bInternalDataset = false;
11,022✔
1313
    if (iIdxInternalDataset >= 0)
11,022✔
1314
    {
1315
        bInternalDataset =
1316
            CPLFetchBool(papszOptions, "_INTERNAL_DATASET", false);
4,163✔
1317
        if (papszOptionsToDelete == nullptr)
4,163✔
1318
            papszOptionsToDelete = CSLDuplicate(papszOptions);
4,163✔
1319
        papszOptionsToDelete = CSLRemoveStrings(
4,163✔
1320
            papszOptionsToDelete, iIdxInternalDataset, 1, nullptr);
1321
        papszOptions = papszOptionsToDelete;
4,163✔
1322
    }
1323

1324
    /* -------------------------------------------------------------------- */
1325
    /*      Validate creation options.                                      */
1326
    /* -------------------------------------------------------------------- */
1327
    if (CPLTestBool(
11,022✔
1328
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
1329
    {
1330
        auto poSrcGroup = poSrcDS->GetRootGroup();
22,044✔
1331
        if (poSrcGroup != nullptr && GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
11,022✔
1332
        {
1333
            CPLStringList aosDatasetCO;
192✔
1334
            for (const char *pszOption : cpl::Iterate(papszOptions))
103✔
1335
            {
1336
                if (!STARTS_WITH_CI(pszOption, "ARRAY:"))
7✔
1337
                    aosDatasetCO.AddString(pszOption);
×
1338
            }
1339
            GDALValidateCreationOptions(this, aosDatasetCO.List());
96✔
1340
        }
1341
        else
1342
        {
1343
            GDALValidateCreationOptions(this, papszOptions);
10,926✔
1344
        }
1345
    }
1346

1347
    /* -------------------------------------------------------------------- */
1348
    /*      Advise the source raster that we are going to read it completely */
1349
    /* -------------------------------------------------------------------- */
1350

1351
    const int nXSize = poSrcDS->GetRasterXSize();
11,022✔
1352
    const int nYSize = poSrcDS->GetRasterYSize();
11,022✔
1353
    GDALDataType eDT = GDT_Unknown;
11,022✔
1354
    if (nBandCount > 0)
11,022✔
1355
    {
1356
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
10,814✔
1357
        if (poSrcBand)
10,814✔
1358
            eDT = poSrcBand->GetRasterDataType();
10,814✔
1359
    }
1360
    poSrcDS->AdviseRead(0, 0, nXSize, nYSize, nXSize, nYSize, eDT, nBandCount,
11,022✔
1361
                        nullptr, nullptr);
11,022✔
1362

1363
    /* -------------------------------------------------------------------- */
1364
    /*      If the format provides a CreateCopy() method use that,          */
1365
    /*      otherwise fallback to the internal implementation using the     */
1366
    /*      Create() method.                                                */
1367
    /* -------------------------------------------------------------------- */
1368
    GDALDataset *poDstDS = nullptr;
11,022✔
1369
    auto l_pfnCreateCopy = GetCreateCopyCallback();
11,022✔
1370
    if (l_pfnCreateCopy != nullptr &&
20,995✔
1371
        !CPLTestBool(CPLGetConfigOption("GDAL_DEFAULT_CREATE_COPY", "NO")))
9,973✔
1372
    {
1373
        poDstDS = l_pfnCreateCopy(pszFilename, poSrcDS, bStrict,
9,973✔
1374
                                  const_cast<char **>(papszOptions),
1375
                                  pfnProgress, pProgressData);
1376
        if (poDstDS != nullptr)
9,973✔
1377
        {
1378
            if (poDstDS->GetDescription() == nullptr ||
17,818✔
1379
                strlen(poDstDS->GetDescription()) == 0)
8,909✔
1380
                poDstDS->SetDescription(pszFilename);
212✔
1381

1382
            if (poDstDS->poDriver == nullptr)
8,909✔
1383
                poDstDS->poDriver = this;
7,929✔
1384

1385
            if (!bInternalDataset)
8,909✔
1386
                poDstDS->AddToDatasetOpenList();
4,746✔
1387
        }
1388
    }
1389
    else
1390
    {
1391
        poDstDS = DefaultCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1,049✔
1392
                                    pfnProgress, pProgressData);
1393
    }
1394

1395
    CSLDestroy(papszOptionsToDelete);
11,022✔
1396
    return poDstDS;
11,022✔
1397
}
1398

1399
/************************************************************************/
1400
/*                           GDALCreateCopy()                           */
1401
/************************************************************************/
1402

1403
/**
1404
 * \brief Create a copy of a dataset.
1405
 *
1406
 * @see GDALDriver::CreateCopy()
1407
 */
1408

1409
GDALDatasetH CPL_STDCALL GDALCreateCopy(GDALDriverH hDriver,
6,154✔
1410
                                        const char *pszFilename,
1411
                                        GDALDatasetH hSrcDS, int bStrict,
1412
                                        CSLConstList papszOptions,
1413
                                        GDALProgressFunc pfnProgress,
1414
                                        void *pProgressData)
1415

1416
{
1417
    VALIDATE_POINTER1(hDriver, "GDALCreateCopy", nullptr);
6,154✔
1418
    VALIDATE_POINTER1(hSrcDS, "GDALCreateCopy", nullptr);
6,154✔
1419

1420
    return GDALDriver::FromHandle(hDriver)->CreateCopy(
6,154✔
1421
        pszFilename, GDALDataset::FromHandle(hSrcDS), bStrict, papszOptions,
1422
        pfnProgress, pProgressData);
6,154✔
1423
}
1424

1425
/************************************************************************/
1426
/*                      CanVectorTranslateFrom()                        */
1427
/************************************************************************/
1428

1429
/** Returns whether the driver can translate from a vector dataset,
1430
 * using the arguments passed to GDALVectorTranslate() stored in
1431
 * papszVectorTranslateArguments.
1432
 *
1433
 * This is used to determine if the driver supports the VectorTranslateFrom()
1434
 * operation.
1435
 *
1436
 * @param pszDestName Target dataset name
1437
 * @param poSourceDS  Source dataset
1438
 * @param papszVectorTranslateArguments Non-positional arguments passed to
1439
 *                                      GDALVectorTranslate() (may be nullptr)
1440
 * @param[out] ppapszFailureReasons nullptr, or a pointer to an null-terminated
1441
 * array of strings to record the reason(s) for the impossibility.
1442
 * @return true if VectorTranslateFrom() can be called with the same arguments.
1443
 * @since GDAL 3.8
1444
 */
1445
bool GDALDriver::CanVectorTranslateFrom(
770✔
1446
    const char *pszDestName, GDALDataset *poSourceDS,
1447
    CSLConstList papszVectorTranslateArguments, char ***ppapszFailureReasons)
1448

1449
{
1450
    if (ppapszFailureReasons)
770✔
1451
    {
1452
        *ppapszFailureReasons = nullptr;
×
1453
    }
1454

1455
    if (!pfnCanVectorTranslateFrom)
770✔
1456
    {
1457
        if (ppapszFailureReasons)
767✔
1458
        {
1459
            *ppapszFailureReasons = CSLAddString(
×
1460
                nullptr,
1461
                "CanVectorTranslateFrom() not implemented for this driver");
1462
        }
1463
        return false;
767✔
1464
    }
1465

1466
    char **papszFailureReasons = nullptr;
3✔
1467
    bool bRet = pfnCanVectorTranslateFrom(
3✔
1468
        pszDestName, poSourceDS, papszVectorTranslateArguments,
1469
        ppapszFailureReasons ? ppapszFailureReasons : &papszFailureReasons);
1470
    if (!ppapszFailureReasons)
3✔
1471
    {
1472
        for (const char *pszReason :
1✔
1473
             cpl::Iterate(static_cast<CSLConstList>(papszFailureReasons)))
5✔
1474
        {
1475
            CPLDebug("GDAL", "%s", pszReason);
1✔
1476
        }
1477
        CSLDestroy(papszFailureReasons);
3✔
1478
    }
1479
    return bRet;
3✔
1480
}
1481

1482
bool GDALDriver::HasOpenOption(const char *pszOpenOptionName) const
224✔
1483
{
1484
    if (pszOpenOptionName == nullptr)
224✔
1485
        return false;
×
1486

1487
    // Const cast is safe here since we are only reading the metadata
1488
    auto pszOOMd{const_cast<GDALDriver *>(this)->GetMetadataItem(
448✔
1489
        GDAL_DMD_OPENOPTIONLIST)};
224✔
1490
    if (pszOOMd == nullptr)
224✔
1491
        return false;
72✔
1492

1493
    const CPLXMLTreeCloser oXml{CPLParseXMLString(pszOOMd)};
304✔
1494
    for (CPLXMLNode *option = oXml->psChild; option != nullptr;
1,159✔
1495
         option = option->psNext)
1,007✔
1496
    {
1497
        if (EQUAL(CPLGetXMLValue(CPLGetXMLNode(option, "name"), nullptr, ""),
1,008✔
1498
                  pszOpenOptionName))
1499
            return true;
1✔
1500
    }
1501
    return false;
151✔
1502
}
1503

1504
/************************************************************************/
1505
/*                         VectorTranslateFrom()                        */
1506
/************************************************************************/
1507

1508
/** Create a copy of a vector dataset, using the arguments passed to
1509
 * GDALVectorTranslate() stored in papszVectorTranslateArguments.
1510
 *
1511
 * This may be implemented by some drivers that can convert from an existing
1512
 * dataset in an optimized way.
1513
 *
1514
 * This is for example used by the PMTiles to convert from MBTiles.
1515
 *
1516
 * @param pszDestName Target dataset name
1517
 * @param poSourceDS  Source dataset
1518
 * @param papszVectorTranslateArguments Non-positional arguments passed to
1519
 *                                      GDALVectorTranslate() (may be nullptr)
1520
 * @param pfnProgress a function to be used to report progress of the copy.
1521
 * @param pProgressData application data passed into progress function.
1522
 * @return a new dataset in case of success, or nullptr in case of error.
1523
 * @since GDAL 3.8
1524
 */
1525
GDALDataset *GDALDriver::VectorTranslateFrom(
2✔
1526
    const char *pszDestName, GDALDataset *poSourceDS,
1527
    CSLConstList papszVectorTranslateArguments, GDALProgressFunc pfnProgress,
1528
    void *pProgressData)
1529

1530
{
1531
    if (!pfnVectorTranslateFrom)
2✔
1532
    {
1533
        CPLError(CE_Failure, CPLE_AppDefined,
×
1534
                 "VectorTranslateFrom() not implemented for this driver");
1535
        return nullptr;
×
1536
    }
1537

1538
    return pfnVectorTranslateFrom(pszDestName, poSourceDS,
2✔
1539
                                  papszVectorTranslateArguments, pfnProgress,
1540
                                  pProgressData);
2✔
1541
}
1542

1543
/************************************************************************/
1544
/*                            QuietDelete()                             */
1545
/************************************************************************/
1546

1547
/**
1548
 * \brief Delete dataset if found.
1549
 *
1550
 * This is a helper method primarily used by Create() and
1551
 * CreateCopy() to predelete any dataset of the name soon to be
1552
 * created.  It will attempt to delete the named dataset if
1553
 * one is found, otherwise it does nothing.  An error is only
1554
 * returned if the dataset is found but the delete fails.
1555
 *
1556
 * This is a static method and it doesn't matter what driver instance
1557
 * it is invoked on.  It will attempt to discover the correct driver
1558
 * using Identify().
1559
 *
1560
 * @param pszName the dataset name to try and delete.
1561
 * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL
1562
 * terminated list of strings with the driver short names that must be
1563
 * considered. (Note: implemented only starting with GDAL 3.4.1)
1564
 * @return CE_None if the dataset does not exist, or is deleted without issues.
1565
 */
1566

1567
CPLErr GDALDriver::QuietDelete(const char *pszName,
23,388✔
1568
                               CSLConstList papszAllowedDrivers)
1569

1570
{
1571
    VSIStatBufL sStat;
1572
    const bool bExists =
1573
        VSIStatExL(pszName, &sStat,
23,388✔
1574
                   VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0;
23,388✔
1575

1576
#ifdef S_ISFIFO
1577
    if (bExists && S_ISFIFO(sStat.st_mode))
23,388✔
1578
        return CE_None;
×
1579
#endif
1580

1581
    GDALDriver *poDriver = nullptr;
23,388✔
1582
    if (papszAllowedDrivers)
23,388✔
1583
    {
1584
        GDALOpenInfo oOpenInfo(pszName, GDAL_OF_ALL);
62✔
1585
        for (const char *pszDriverName : cpl::Iterate(papszAllowedDrivers))
31✔
1586
        {
1587
            GDALDriver *poTmpDriver =
1588
                GDALDriver::FromHandle(GDALGetDriverByName(pszDriverName));
31✔
1589
            if (poTmpDriver)
31✔
1590
            {
1591
                const bool bIdentifyRes =
1592
                    poTmpDriver->pfnIdentifyEx
31✔
1593
                        ? poTmpDriver->pfnIdentifyEx(poTmpDriver, &oOpenInfo) >
62✔
1594
                              0
1595
                        : poTmpDriver->pfnIdentify &&
62✔
1596
                              poTmpDriver->pfnIdentify(&oOpenInfo) > 0;
31✔
1597
                if (bIdentifyRes)
31✔
1598
                {
1599
                    poDriver = poTmpDriver;
31✔
1600
                    break;
31✔
1601
                }
1602
            }
1603
        }
1604
    }
1605
    else
1606
    {
1607
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
46,714✔
1608
        poDriver = GDALDriver::FromHandle(GDALIdentifyDriver(pszName, nullptr));
23,357✔
1609
    }
1610

1611
    if (poDriver == nullptr)
23,388✔
1612
        return CE_None;
22,431✔
1613

1614
    if (bExists && VSI_ISDIR(sStat.st_mode) &&
1,001✔
1615
        (EQUAL(poDriver->GetDescription(), "MapInfo File") ||
44✔
1616
         EQUAL(poDriver->GetDescription(), "ESRI Shapefile")))
44✔
1617
    {
1618
        // Those drivers are a bit special and handle directories as container
1619
        // of layers, but it is quite common to found other files too, and
1620
        // removing the directory might be non-desirable.
1621
        return CE_None;
42✔
1622
    }
1623

1624
    CPLDebug("GDAL", "QuietDelete(%s) invoking Delete()", pszName);
915✔
1625

1626
    poDriver->pfnDelete = poDriver->GetDeleteCallback();
915✔
1627
    const bool bQuiet = !bExists && poDriver->pfnDelete == nullptr &&
953✔
1628
                        poDriver->pfnDeleteDataSource == nullptr;
38✔
1629
    if (bQuiet)
915✔
1630
    {
1631
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
76✔
1632
        return poDriver->Delete(pszName);
38✔
1633
    }
1634
    else
1635
    {
1636
        return poDriver->Delete(pszName);
877✔
1637
    }
1638
}
1639

1640
/************************************************************************/
1641
/*                               Delete()                               */
1642
/************************************************************************/
1643

1644
/**
1645
 * \brief Delete named dataset.
1646
 *
1647
 * The driver will attempt to delete the named dataset in a driver specific
1648
 * fashion.  Full featured drivers will delete all associated files,
1649
 * database objects, or whatever is appropriate.  The default behavior when
1650
 * no driver specific behavior is provided is to attempt to delete all the
1651
 * files that are returned by GDALGetFileList() on the dataset handle.
1652
 *
1653
 * It is unwise to have open dataset handles on this dataset when it is
1654
 * deleted.
1655
 *
1656
 * Equivalent of the C function GDALDeleteDataset().
1657
 *
1658
 * @param pszFilename name of dataset to delete.
1659
 *
1660
 * @return CE_None on success, or CE_Failure if the operation fails.
1661
 */
1662

1663
CPLErr GDALDriver::Delete(const char *pszFilename)
4,527✔
1664

1665
{
1666
    pfnDelete = GetDeleteCallback();
4,527✔
1667
    if (pfnDelete != nullptr)
4,527✔
1668
        return pfnDelete(pszFilename);
1,078✔
1669
    else if (pfnDeleteDataSource != nullptr)
3,449✔
1670
        return pfnDeleteDataSource(this, pszFilename);
×
1671

1672
    /* -------------------------------------------------------------------- */
1673
    /*      Collect file list.                                              */
1674
    /* -------------------------------------------------------------------- */
1675
    GDALDatasetH hDS = GDALOpenEx(pszFilename, 0, nullptr, nullptr, nullptr);
3,449✔
1676

1677
    if (hDS == nullptr)
3,449✔
1678
    {
1679
        if (CPLGetLastErrorNo() == 0)
237✔
1680
            CPLError(CE_Failure, CPLE_OpenFailed,
210✔
1681
                     "Unable to open %s to obtain file list.", pszFilename);
1682

1683
        return CE_Failure;
237✔
1684
    }
1685

1686
    char **papszFileList = GDALGetFileList(hDS);
3,212✔
1687

1688
    GDALClose(hDS);
3,212✔
1689
    hDS = nullptr;
3,212✔
1690

1691
    if (CSLCount(papszFileList) == 0)
3,212✔
1692
    {
1693
        CPLError(CE_Failure, CPLE_NotSupported,
×
1694
                 "Unable to determine files associated with %s, "
1695
                 "delete fails.",
1696
                 pszFilename);
1697
        CSLDestroy(papszFileList);
×
1698
        return CE_Failure;
×
1699
    }
1700

1701
    /* -------------------------------------------------------------------- */
1702
    /*      Delete all files.                                               */
1703
    /* -------------------------------------------------------------------- */
1704
    CPLErr eErr = CE_None;
3,212✔
1705
    for (int i = 0; papszFileList[i] != nullptr; ++i)
7,104✔
1706
    {
1707
        if (VSIUnlink(papszFileList[i]) != 0)
3,892✔
1708
        {
1709
            CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s",
4✔
1710
                     papszFileList[i], VSIStrerror(errno));
2✔
1711
            eErr = CE_Failure;
2✔
1712
        }
1713
    }
1714

1715
    CSLDestroy(papszFileList);
3,212✔
1716

1717
    return eErr;
3,212✔
1718
}
1719

1720
/************************************************************************/
1721
/*                         GDALDeleteDataset()                          */
1722
/************************************************************************/
1723

1724
/**
1725
 * \brief Delete named dataset.
1726
 *
1727
 * @see GDALDriver::Delete()
1728
 */
1729

1730
CPLErr CPL_STDCALL GDALDeleteDataset(GDALDriverH hDriver,
2,489✔
1731
                                     const char *pszFilename)
1732

1733
{
1734
    if (hDriver == nullptr)
2,489✔
1735
        hDriver = GDALIdentifyDriver(pszFilename, nullptr);
10✔
1736

1737
    if (hDriver == nullptr)
2,489✔
1738
    {
1739
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1✔
1740
                 pszFilename);
1741
        return CE_Failure;
1✔
1742
    }
1743

1744
#ifdef OGRAPISPY_ENABLED
1745
    if (GDALGetMetadataItem(hDriver, GDAL_DCAP_VECTOR, nullptr))
2,488✔
1746
    {
1747
        OGRAPISpyDeleteDataSource(hDriver, pszFilename);
484✔
1748
    }
1749
#endif
1750

1751
    return GDALDriver::FromHandle(hDriver)->Delete(pszFilename);
2,488✔
1752
}
1753

1754
/************************************************************************/
1755
/*                           DefaultRename()                            */
1756
/*                                                                      */
1757
/*      The generic implementation based on the file list used when     */
1758
/*      there is no format specific implementation.                     */
1759
/************************************************************************/
1760

1761
//! @cond Doxygen_Suppress
1762
CPLErr GDALDriver::DefaultRename(const char *pszNewName, const char *pszOldName)
173✔
1763

1764
{
1765
    /* -------------------------------------------------------------------- */
1766
    /*      Collect file list.                                              */
1767
    /* -------------------------------------------------------------------- */
1768
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
173✔
1769

1770
    if (hDS == nullptr)
173✔
1771
    {
1772
        if (CPLGetLastErrorNo() == 0)
×
1773
            CPLError(CE_Failure, CPLE_OpenFailed,
×
1774
                     "Unable to open %s to obtain file list.", pszOldName);
1775

1776
        return CE_Failure;
×
1777
    }
1778

1779
    char **papszFileList = GDALGetFileList(hDS);
173✔
1780

1781
    GDALClose(hDS);
173✔
1782

1783
    if (CSLCount(papszFileList) == 0)
173✔
1784
    {
1785
        CPLError(CE_Failure, CPLE_NotSupported,
×
1786
                 "Unable to determine files associated with %s,\n"
1787
                 "rename fails.",
1788
                 pszOldName);
1789

1790
        return CE_Failure;
×
1791
    }
1792

1793
    /* -------------------------------------------------------------------- */
1794
    /*      Produce a list of new filenames that correspond to the old      */
1795
    /*      names.                                                          */
1796
    /* -------------------------------------------------------------------- */
1797
    CPLErr eErr = CE_None;
173✔
1798
    char **papszNewFileList =
1799
        CPLCorrespondingPaths(pszOldName, pszNewName, papszFileList);
173✔
1800

1801
    if (papszNewFileList == nullptr)
173✔
1802
        return CE_Failure;
×
1803

1804
    for (int i = 0; papszFileList[i] != nullptr; ++i)
353✔
1805
    {
1806
        if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
180✔
1807
        {
1808
            eErr = CE_Failure;
×
1809
            // Try to put the ones we moved back.
1810
            for (--i; i >= 0; i--)
×
1811
            {
1812
                // Nothing we can do if the moving back doesn't work...
1813
                CPL_IGNORE_RET_VAL(
×
1814
                    CPLMoveFile(papszFileList[i], papszNewFileList[i]));
×
1815
            }
1816
            break;
×
1817
        }
1818
    }
1819

1820
    CSLDestroy(papszNewFileList);
173✔
1821
    CSLDestroy(papszFileList);
173✔
1822

1823
    return eErr;
173✔
1824
}
1825

1826
//! @endcond
1827

1828
/************************************************************************/
1829
/*                               Rename()                               */
1830
/************************************************************************/
1831

1832
/**
1833
 * \brief Rename a dataset.
1834
 *
1835
 * Rename a dataset. This may including moving the dataset to a new directory
1836
 * or even a new filesystem.
1837
 *
1838
 * It is unwise to have open dataset handles on this dataset when it is
1839
 * being renamed.
1840
 *
1841
 * Equivalent of the C function GDALRenameDataset().
1842
 *
1843
 * @param pszNewName new name for the dataset.
1844
 * @param pszOldName old name for the dataset.
1845
 *
1846
 * @return CE_None on success, or CE_Failure if the operation fails.
1847
 */
1848

1849
CPLErr GDALDriver::Rename(const char *pszNewName, const char *pszOldName)
175✔
1850

1851
{
1852
    pfnRename = GetRenameCallback();
175✔
1853
    if (pfnRename != nullptr)
175✔
1854
        return pfnRename(pszNewName, pszOldName);
3✔
1855

1856
    return DefaultRename(pszNewName, pszOldName);
172✔
1857
}
1858

1859
/************************************************************************/
1860
/*                         GDALRenameDataset()                          */
1861
/************************************************************************/
1862

1863
/**
1864
 * \brief Rename a dataset.
1865
 *
1866
 * @see GDALDriver::Rename()
1867
 */
1868

1869
CPLErr CPL_STDCALL GDALRenameDataset(GDALDriverH hDriver,
176✔
1870
                                     const char *pszNewName,
1871
                                     const char *pszOldName)
1872

1873
{
1874
    if (hDriver == nullptr)
176✔
1875
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
3✔
1876

1877
    if (hDriver == nullptr)
176✔
1878
    {
1879
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1✔
1880
                 pszOldName);
1881
        return CE_Failure;
1✔
1882
    }
1883

1884
    return GDALDriver::FromHandle(hDriver)->Rename(pszNewName, pszOldName);
175✔
1885
}
1886

1887
/************************************************************************/
1888
/*                          DefaultCopyFiles()                          */
1889
/*                                                                      */
1890
/*      The default implementation based on file lists used when        */
1891
/*      there is no format specific implementation.                     */
1892
/************************************************************************/
1893

1894
//! @cond Doxygen_Suppress
1895
CPLErr GDALDriver::DefaultCopyFiles(const char *pszNewName,
10✔
1896
                                    const char *pszOldName)
1897

1898
{
1899
    /* -------------------------------------------------------------------- */
1900
    /*      Collect file list.                                              */
1901
    /* -------------------------------------------------------------------- */
1902
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
10✔
1903

1904
    if (hDS == nullptr)
10✔
1905
    {
1906
        if (CPLGetLastErrorNo() == 0)
×
1907
            CPLError(CE_Failure, CPLE_OpenFailed,
×
1908
                     "Unable to open %s to obtain file list.", pszOldName);
1909

1910
        return CE_Failure;
×
1911
    }
1912

1913
    char **papszFileList = GDALGetFileList(hDS);
10✔
1914

1915
    GDALClose(hDS);
10✔
1916
    hDS = nullptr;
10✔
1917

1918
    if (CSLCount(papszFileList) == 0)
10✔
1919
    {
1920
        CPLError(CE_Failure, CPLE_NotSupported,
×
1921
                 "Unable to determine files associated with %s,\n"
1922
                 "rename fails.",
1923
                 pszOldName);
1924

1925
        return CE_Failure;
×
1926
    }
1927

1928
    /* -------------------------------------------------------------------- */
1929
    /*      Produce a list of new filenames that correspond to the old      */
1930
    /*      names.                                                          */
1931
    /* -------------------------------------------------------------------- */
1932
    CPLErr eErr = CE_None;
10✔
1933
    char **papszNewFileList =
1934
        CPLCorrespondingPaths(pszOldName, pszNewName, papszFileList);
10✔
1935

1936
    if (papszNewFileList == nullptr)
10✔
1937
        return CE_Failure;
×
1938

1939
    for (int i = 0; papszFileList[i] != nullptr; ++i)
28✔
1940
    {
1941
        if (CPLCopyFile(papszNewFileList[i], papszFileList[i]) != 0)
18✔
1942
        {
1943
            eErr = CE_Failure;
×
1944
            // Try to put the ones we moved back.
1945
            for (--i; i >= 0; --i)
×
1946
                VSIUnlink(papszNewFileList[i]);
×
1947
            break;
×
1948
        }
1949
    }
1950

1951
    CSLDestroy(papszNewFileList);
10✔
1952
    CSLDestroy(papszFileList);
10✔
1953

1954
    return eErr;
10✔
1955
}
1956

1957
//! @endcond
1958

1959
/************************************************************************/
1960
/*                             CopyFiles()                              */
1961
/************************************************************************/
1962

1963
/**
1964
 * \brief Copy the files of a dataset.
1965
 *
1966
 * Copy all the files associated with a dataset.
1967
 *
1968
 * Equivalent of the C function GDALCopyDatasetFiles().
1969
 *
1970
 * @param pszNewName new name for the dataset.
1971
 * @param pszOldName old name for the dataset.
1972
 *
1973
 * @return CE_None on success, or CE_Failure if the operation fails.
1974
 */
1975

1976
CPLErr GDALDriver::CopyFiles(const char *pszNewName, const char *pszOldName)
12✔
1977

1978
{
1979
    pfnCopyFiles = GetCopyFilesCallback();
12✔
1980
    if (pfnCopyFiles != nullptr)
12✔
1981
        return pfnCopyFiles(pszNewName, pszOldName);
3✔
1982

1983
    return DefaultCopyFiles(pszNewName, pszOldName);
9✔
1984
}
1985

1986
/************************************************************************/
1987
/*                        GDALCopyDatasetFiles()                        */
1988
/************************************************************************/
1989

1990
/**
1991
 * \brief Copy the files of a dataset.
1992
 *
1993
 * @see GDALDriver::CopyFiles()
1994
 */
1995

1996
CPLErr CPL_STDCALL GDALCopyDatasetFiles(GDALDriverH hDriver,
13✔
1997
                                        const char *pszNewName,
1998
                                        const char *pszOldName)
1999

2000
{
2001
    if (hDriver == nullptr)
13✔
2002
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
8✔
2003

2004
    if (hDriver == nullptr)
13✔
2005
    {
2006
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1✔
2007
                 pszOldName);
2008
        return CE_Failure;
1✔
2009
    }
2010

2011
    return GDALDriver::FromHandle(hDriver)->CopyFiles(pszNewName, pszOldName);
12✔
2012
}
2013

2014
/************************************************************************/
2015
/*                       GDALDriverHasOpenOption()                      */
2016
/************************************************************************/
2017

2018
/**
2019
 * \brief Returns TRUE if the given open option is supported by the driver.
2020
 * @param hDriver the handle of the driver
2021
 * @param pszOpenOptionName name of the open option to be checked
2022
 * @return TRUE if the driver supports the open option
2023
 * @since GDAL 3.11
2024
 */
2025
bool GDALDriverHasOpenOption(GDALDriverH hDriver, const char *pszOpenOptionName)
2✔
2026
{
2027
    VALIDATE_POINTER1(hDriver, "GDALDriverHasOpenOption", false);
2✔
2028
    return GDALDriver::FromHandle(hDriver)->HasOpenOption(pszOpenOptionName);
2✔
2029
}
2030

2031
/************************************************************************/
2032
/*                       GDALGetDriverShortName()                       */
2033
/************************************************************************/
2034

2035
/**
2036
 * \brief Return the short name of a driver
2037
 *
2038
 * This is the string that can be
2039
 * passed to the GDALGetDriverByName() function.
2040
 *
2041
 * For the GeoTIFF driver, this is "GTiff"
2042
 *
2043
 * @param hDriver the handle of the driver
2044
 * @return the short name of the driver. The
2045
 *         returned string should not be freed and is owned by the driver.
2046
 */
2047

2048
const char *CPL_STDCALL GDALGetDriverShortName(GDALDriverH hDriver)
3,509,910✔
2049

2050
{
2051
    VALIDATE_POINTER1(hDriver, "GDALGetDriverShortName", nullptr);
3,509,910✔
2052

2053
    return GDALDriver::FromHandle(hDriver)->GetDescription();
3,509,910✔
2054
}
2055

2056
/************************************************************************/
2057
/*                       GDALGetDriverLongName()                        */
2058
/************************************************************************/
2059

2060
/**
2061
 * \brief Return the long name of a driver
2062
 *
2063
 * For the GeoTIFF driver, this is "GeoTIFF"
2064
 *
2065
 * @param hDriver the handle of the driver
2066
 * @return the long name of the driver or empty string. The
2067
 *         returned string should not be freed and is owned by the driver.
2068
 */
2069

2070
const char *CPL_STDCALL GDALGetDriverLongName(GDALDriverH hDriver)
479✔
2071

2072
{
2073
    VALIDATE_POINTER1(hDriver, "GDALGetDriverLongName", nullptr);
479✔
2074

2075
    const char *pszLongName =
2076
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_LONGNAME);
479✔
2077

2078
    if (pszLongName == nullptr)
479✔
2079
        return "";
×
2080

2081
    return pszLongName;
479✔
2082
}
2083

2084
/************************************************************************/
2085
/*                       GDALGetDriverHelpTopic()                       */
2086
/************************************************************************/
2087

2088
/**
2089
 * \brief Return the URL to the help that describes the driver
2090
 *
2091
 * That URL is relative to the GDAL documentation directory.
2092
 *
2093
 * For the GeoTIFF driver, this is "frmt_gtiff.html"
2094
 *
2095
 * @param hDriver the handle of the driver
2096
 * @return the URL to the help that describes the driver or NULL. The
2097
 *         returned string should not be freed and is owned by the driver.
2098
 */
2099

2100
const char *CPL_STDCALL GDALGetDriverHelpTopic(GDALDriverH hDriver)
×
2101

2102
{
2103
    VALIDATE_POINTER1(hDriver, "GDALGetDriverHelpTopic", nullptr);
×
2104

2105
    return GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_HELPTOPIC);
×
2106
}
2107

2108
/************************************************************************/
2109
/*                   GDALGetDriverCreationOptionList()                  */
2110
/************************************************************************/
2111

2112
/**
2113
 * \brief Return the list of creation options of the driver
2114
 *
2115
 * Return the list of creation options of the driver used by Create() and
2116
 * CreateCopy() as an XML string
2117
 *
2118
 * @param hDriver the handle of the driver
2119
 * @return an XML string that describes the list of creation options or
2120
 *         empty string. The returned string should not be freed and is
2121
 *         owned by the driver.
2122
 */
2123

2124
const char *CPL_STDCALL GDALGetDriverCreationOptionList(GDALDriverH hDriver)
×
2125

2126
{
2127
    VALIDATE_POINTER1(hDriver, "GDALGetDriverCreationOptionList", nullptr);
×
2128

2129
    const char *pszOptionList =
2130
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
×
2131
            GDAL_DMD_CREATIONOPTIONLIST);
×
2132

2133
    if (pszOptionList == nullptr)
×
2134
        return "";
×
2135

2136
    return pszOptionList;
×
2137
}
2138

2139
/************************************************************************/
2140
/*                   GDALValidateCreationOptions()                      */
2141
/************************************************************************/
2142

2143
/**
2144
 * \brief Validate the list of creation options that are handled by a driver
2145
 *
2146
 * This is a helper method primarily used by Create() and
2147
 * CreateCopy() to validate that the passed in list of creation options
2148
 * is compatible with the GDAL_DMD_CREATIONOPTIONLIST metadata item defined
2149
 * by some drivers. @see GDALGetDriverCreationOptionList()
2150
 *
2151
 * If the GDAL_DMD_CREATIONOPTIONLIST metadata item is not defined, this
2152
 * function will return TRUE. Otherwise it will check that the keys and values
2153
 * in the list of creation options are compatible with the capabilities declared
2154
 * by the GDAL_DMD_CREATIONOPTIONLIST metadata item. In case of incompatibility
2155
 * a (non fatal) warning will be emitted and FALSE will be returned.
2156
 *
2157
 * @param hDriver the handle of the driver with whom the lists of creation
2158
 * option must be validated
2159
 * @param papszCreationOptions the list of creation options. An array of
2160
 * strings, whose last element is a NULL pointer
2161
 * @return TRUE if the list of creation options is compatible with the Create()
2162
 *         and CreateCopy() method of the driver, FALSE otherwise.
2163
 */
2164

2165
int CPL_STDCALL GDALValidateCreationOptions(GDALDriverH hDriver,
32,915✔
2166
                                            CSLConstList papszCreationOptions)
2167
{
2168
    VALIDATE_POINTER1(hDriver, "GDALValidateCreationOptions", FALSE);
32,915✔
2169
    const char *pszOptionList =
2170
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
32,915✔
2171
            GDAL_DMD_CREATIONOPTIONLIST);
32,915✔
2172
    CPLString osDriver;
32,915✔
2173
    osDriver.Printf("driver %s",
2174
                    GDALDriver::FromHandle(hDriver)->GetDescription());
32,915✔
2175
    bool bFoundOptionToRemove = false;
32,915✔
2176
    constexpr const char *const apszExcludedOptions[] = {
32,915✔
2177
        "APPEND_SUBDATASET", "COPY_SRC_MDD", "SRC_MDD", "SKIP_HOLES"};
2178
    for (const char *pszCO : cpl::Iterate(papszCreationOptions))
48,142✔
2179
    {
2180
        for (const char *pszExcludedOptions : apszExcludedOptions)
76,752✔
2181
        {
2182
            if (STARTS_WITH_CI(pszCO, pszExcludedOptions) &&
61,525✔
2183
                pszCO[strlen(pszExcludedOptions)] == '=')
194✔
2184
            {
2185
                bFoundOptionToRemove = true;
194✔
2186
                break;
194✔
2187
            }
2188
        }
2189
        if (bFoundOptionToRemove)
15,421✔
2190
            break;
194✔
2191
    }
2192
    CSLConstList papszOptionsToValidate = papszCreationOptions;
32,915✔
2193
    char **papszOptionsToFree = nullptr;
32,915✔
2194
    if (bFoundOptionToRemove)
32,915✔
2195
    {
2196
        for (const char *pszCO : cpl::Iterate(papszCreationOptions))
587✔
2197
        {
2198
            bool bMatch = false;
393✔
2199
            for (const char *pszExcludedOptions : apszExcludedOptions)
1,592✔
2200
            {
2201
                if (STARTS_WITH_CI(pszCO, pszExcludedOptions) &&
1,406✔
2202
                    pszCO[strlen(pszExcludedOptions)] == '=')
207✔
2203
                {
2204
                    bMatch = true;
207✔
2205
                    break;
207✔
2206
                }
2207
            }
2208
            if (!bMatch)
393✔
2209
                papszOptionsToFree = CSLAddString(papszOptionsToFree, pszCO);
186✔
2210
        }
2211
        papszOptionsToValidate = papszOptionsToFree;
194✔
2212
    }
2213

2214
    const bool bRet = CPL_TO_BOOL(GDALValidateOptions(
32,915✔
2215
        pszOptionList, papszOptionsToValidate, "creation option", osDriver));
2216
    CSLDestroy(papszOptionsToFree);
32,915✔
2217
    return bRet;
32,915✔
2218
}
2219

2220
/************************************************************************/
2221
/*                     GDALValidateOpenOptions()                        */
2222
/************************************************************************/
2223

2224
int GDALValidateOpenOptions(GDALDriverH hDriver,
55,312✔
2225
                            const char *const *papszOpenOptions)
2226
{
2227
    VALIDATE_POINTER1(hDriver, "GDALValidateOpenOptions", FALSE);
55,312✔
2228
    const char *pszOptionList =
2229
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
55,312✔
2230
            GDAL_DMD_OPENOPTIONLIST);
55,292✔
2231
    CPLString osDriver;
110,581✔
2232
    osDriver.Printf("driver %s",
2233
                    GDALDriver::FromHandle(hDriver)->GetDescription());
55,284✔
2234
    return GDALValidateOptions(pszOptionList, papszOpenOptions, "open option",
55,300✔
2235
                               osDriver);
55,283✔
2236
}
2237

2238
/************************************************************************/
2239
/*                           GDALValidateOptions()                      */
2240
/************************************************************************/
2241

2242
int GDALValidateOptions(const char *pszOptionList,
100,632✔
2243
                        const char *const *papszOptionsToValidate,
2244
                        const char *pszErrorMessageOptionType,
2245
                        const char *pszErrorMessageContainerName)
2246
{
2247
    if (papszOptionsToValidate == nullptr || *papszOptionsToValidate == nullptr)
100,632✔
2248
        return TRUE;
84,809✔
2249
    if (pszOptionList == nullptr)
15,823✔
2250
        return TRUE;
125✔
2251

2252
    CPLXMLNode *psNode = CPLParseXMLString(pszOptionList);
15,698✔
2253
    if (psNode == nullptr)
15,698✔
2254
    {
2255
        CPLError(CE_Warning, CPLE_AppDefined,
×
2256
                 "Could not parse %s list of %s. Assuming options are valid.",
2257
                 pszErrorMessageOptionType, pszErrorMessageContainerName);
2258
        return TRUE;
×
2259
    }
2260

2261
    bool bRet = true;
15,698✔
2262
    while (*papszOptionsToValidate)
42,472✔
2263
    {
2264
        char *pszKey = nullptr;
26,774✔
2265
        const char *pszValue =
2266
            CPLParseNameValue(*papszOptionsToValidate, &pszKey);
26,774✔
2267
        if (pszKey == nullptr)
26,774✔
2268
        {
2269
            CPLError(CE_Warning, CPLE_NotSupported,
1✔
2270
                     "%s '%s' is not formatted with the key=value format",
2271
                     pszErrorMessageOptionType, *papszOptionsToValidate);
2272
            bRet = false;
1✔
2273

2274
            ++papszOptionsToValidate;
1✔
2275
            continue;
1,683✔
2276
        }
2277

2278
        if (EQUAL(pszKey, "VALIDATE_OPEN_OPTIONS"))
26,773✔
2279
        {
2280
            ++papszOptionsToValidate;
×
2281
            CPLFree(pszKey);
×
2282
            continue;
×
2283
        }
2284

2285
        // Must we be forgiving in case of missing option ?
2286
        bool bWarnIfMissingKey = true;
26,773✔
2287
        if (pszKey[0] == '@')
26,773✔
2288
        {
2289
            bWarnIfMissingKey = false;
1,669✔
2290
            memmove(pszKey, pszKey + 1, strlen(pszKey + 1) + 1);
1,669✔
2291
        }
2292

2293
        CPLXMLNode *psChildNode = psNode->psChild;
26,773✔
2294
        while (psChildNode)
250,842✔
2295
        {
2296
            if (EQUAL(psChildNode->pszValue, "OPTION"))
249,160✔
2297
            {
2298
                const char *pszOptionName =
2299
                    CPLGetXMLValue(psChildNode, "name", "");
249,160✔
2300
                /* For option names terminated by wildcard (NITF BLOCKA option
2301
                 * names for example) */
2302
                if (strlen(pszOptionName) > 0 &&
249,160✔
2303
                    pszOptionName[strlen(pszOptionName) - 1] == '*' &&
249,160✔
2304
                    EQUALN(pszOptionName, pszKey, strlen(pszOptionName) - 1))
377✔
2305
                {
2306
                    break;
216✔
2307
                }
2308

2309
                /* For option names beginning by a wildcard */
2310
                if (pszOptionName[0] == '*' &&
248,944✔
2311
                    strlen(pszKey) > strlen(pszOptionName) &&
57✔
2312
                    EQUAL(pszKey + strlen(pszKey) - strlen(pszOptionName + 1),
9✔
2313
                          pszOptionName + 1))
2314
                {
2315
                    break;
2✔
2316
                }
2317

2318
                // For options names with * in the middle
2319
                const char *pszStarInOptionName = strchr(pszOptionName, '*');
248,942✔
2320
                if (pszStarInOptionName &&
248,942✔
2321
                    pszStarInOptionName != pszOptionName &&
182✔
2322
                    pszStarInOptionName !=
2323
                        pszOptionName + strlen(pszOptionName) - 1 &&
182✔
2324
                    strlen(pszKey) > static_cast<size_t>(pszStarInOptionName -
21✔
2325
                                                         pszOptionName) &&
12✔
2326
                    EQUALN(pszKey, pszOptionName,
12✔
2327
                           static_cast<size_t>(pszStarInOptionName -
2328
                                               pszOptionName)) &&
12✔
2329
                    EQUAL(pszKey +
12✔
2330
                              static_cast<size_t>(pszStarInOptionName -
2331
                                                  pszOptionName) +
2332
                              1,
2333
                          pszStarInOptionName + 1))
2334
                {
2335
                    break;
6✔
2336
                }
2337

2338
                if (EQUAL(pszOptionName, pszKey))
248,936✔
2339
                {
2340
                    break;
24,819✔
2341
                }
2342
                const char *pszAlias = CPLGetXMLValue(
224,117✔
2343
                    psChildNode, "alias",
2344
                    CPLGetXMLValue(psChildNode, "deprecated_alias", ""));
2345
                if (EQUAL(pszAlias, pszKey))
224,117✔
2346
                {
2347
                    CPLDebug("GDAL",
48✔
2348
                             "Using deprecated alias '%s'. New name is '%s'",
2349
                             pszAlias, pszOptionName);
2350
                    break;
48✔
2351
                }
2352
            }
2353
            psChildNode = psChildNode->psNext;
224,069✔
2354
        }
2355
        if (psChildNode == nullptr)
26,773✔
2356
        {
2357
            if (bWarnIfMissingKey &&
1,701✔
2358
                (!EQUAL(pszErrorMessageOptionType, "open option") ||
19✔
2359
                 CPLFetchBool(papszOptionsToValidate, "VALIDATE_OPEN_OPTIONS",
×
2360
                              true)))
2361
            {
2362
                CPLError(CE_Warning, CPLE_NotSupported,
19✔
2363
                         "%s does not support %s %s",
2364
                         pszErrorMessageContainerName,
2365
                         pszErrorMessageOptionType, pszKey);
2366
                bRet = false;
19✔
2367
            }
2368

2369
            CPLFree(pszKey);
1,682✔
2370
            ++papszOptionsToValidate;
1,682✔
2371
            continue;
1,682✔
2372
        }
2373

2374
#ifdef DEBUG
2375
        CPLXMLNode *psChildSubNode = psChildNode->psChild;
25,091✔
2376
        while (psChildSubNode)
149,059✔
2377
        {
2378
            if (psChildSubNode->eType == CXT_Attribute)
123,968✔
2379
            {
2380
                if (!(EQUAL(psChildSubNode->pszValue, "name") ||
87,383✔
2381
                      EQUAL(psChildSubNode->pszValue, "alias") ||
62,292✔
2382
                      EQUAL(psChildSubNode->pszValue, "deprecated_alias") ||
62,176✔
2383
                      EQUAL(psChildSubNode->pszValue, "alt_config_option") ||
62,095✔
2384
                      EQUAL(psChildSubNode->pszValue, "description") ||
62,062✔
2385
                      EQUAL(psChildSubNode->pszValue, "type") ||
40,902✔
2386
                      EQUAL(psChildSubNode->pszValue, "min") ||
15,811✔
2387
                      EQUAL(psChildSubNode->pszValue, "max") ||
15,513✔
2388
                      EQUAL(psChildSubNode->pszValue, "default") ||
15,145✔
2389
                      EQUAL(psChildSubNode->pszValue, "maxsize") ||
1,010✔
2390
                      EQUAL(psChildSubNode->pszValue, "required") ||
985✔
2391
                      EQUAL(psChildSubNode->pszValue, "scope")))
930✔
2392
                {
2393
                    /* Driver error */
2394
                    CPLError(CE_Warning, CPLE_NotSupported,
×
2395
                             "%s : unhandled attribute '%s' for %s %s.",
2396
                             pszErrorMessageContainerName,
2397
                             psChildSubNode->pszValue, pszKey,
2398
                             pszErrorMessageOptionType);
2399
                }
2400
            }
2401
            psChildSubNode = psChildSubNode->psNext;
123,968✔
2402
        }
2403
#endif
2404

2405
        const char *pszType = CPLGetXMLValue(psChildNode, "type", nullptr);
25,091✔
2406
        const char *pszMin = CPLGetXMLValue(psChildNode, "min", nullptr);
25,091✔
2407
        const char *pszMax = CPLGetXMLValue(psChildNode, "max", nullptr);
25,091✔
2408
        if (pszType != nullptr)
25,091✔
2409
        {
2410
            if (EQUAL(pszType, "INT") || EQUAL(pszType, "INTEGER"))
25,091✔
2411
            {
2412
                const char *pszValueIter = pszValue;
8,521✔
2413
                while (*pszValueIter)
21,764✔
2414
                {
2415
                    if (!((*pszValueIter >= '0' && *pszValueIter <= '9') ||
13,245✔
2416
                          *pszValueIter == '+' || *pszValueIter == '-'))
19✔
2417
                    {
2418
                        CPLError(CE_Warning, CPLE_NotSupported,
2✔
2419
                                 "'%s' is an unexpected value for %s %s of "
2420
                                 "type int.",
2421
                                 pszValue, pszKey, pszErrorMessageOptionType);
2422
                        bRet = false;
2✔
2423
                        break;
2✔
2424
                    }
2425
                    ++pszValueIter;
13,243✔
2426
                }
2427
                if (*pszValueIter == '\0')
8,521✔
2428
                {
2429
                    if (pszMin && atoi(pszValue) < atoi(pszMin))
8,519✔
2430
                    {
2431
                        CPLError(CE_Warning, CPLE_NotSupported,
10✔
2432
                                 "'%s' is an unexpected value for %s %s that "
2433
                                 "should be >= %s.",
2434
                                 pszValue, pszKey, pszErrorMessageOptionType,
2435
                                 pszMin);
2436
                        bRet = false;
10✔
2437
                    }
2438
                    if (pszMax && atoi(pszValue) > atoi(pszMax))
8,519✔
2439
                    {
2440
                        CPLError(CE_Warning, CPLE_NotSupported,
12✔
2441
                                 "'%s' is an unexpected value for %s %s that "
2442
                                 "should be <= %s.",
2443
                                 pszValue, pszKey, pszErrorMessageOptionType,
2444
                                 pszMax);
2445
                        bRet = false;
12✔
2446
                    }
2447
                }
8,521✔
2448
            }
2449
            else if (EQUAL(pszType, "UNSIGNED INT"))
16,570✔
2450
            {
2451
                const char *pszValueIter = pszValue;
3✔
2452
                while (*pszValueIter)
10✔
2453
                {
2454
                    if (!((*pszValueIter >= '0' && *pszValueIter <= '9') ||
7✔
2455
                          *pszValueIter == '+'))
×
2456
                    {
2457
                        CPLError(CE_Warning, CPLE_NotSupported,
×
2458
                                 "'%s' is an unexpected value for %s %s of "
2459
                                 "type unsigned int.",
2460
                                 pszValue, pszKey, pszErrorMessageOptionType);
2461
                        bRet = false;
×
2462
                        break;
×
2463
                    }
2464
                    ++pszValueIter;
7✔
2465
                }
2466
                if (*pszValueIter == '\0')
3✔
2467
                {
2468
                    if (pszMin && atoi(pszValue) < atoi(pszMin))
3✔
2469
                    {
2470
                        CPLError(CE_Warning, CPLE_NotSupported,
×
2471
                                 "'%s' is an unexpected value for %s %s that "
2472
                                 "should be >= %s.",
2473
                                 pszValue, pszKey, pszErrorMessageOptionType,
2474
                                 pszMin);
2475
                        bRet = false;
×
2476
                    }
2477
                    if (pszMax && atoi(pszValue) > atoi(pszMax))
3✔
2478
                    {
2479
                        CPLError(CE_Warning, CPLE_NotSupported,
×
2480
                                 "'%s' is an unexpected value for %s %s that "
2481
                                 "should be <= %s.",
2482
                                 pszValue, pszKey, pszErrorMessageOptionType,
2483
                                 pszMax);
2484
                        bRet = false;
×
2485
                    }
2486
                }
2487
            }
2488
            else if (EQUAL(pszType, "FLOAT"))
16,567✔
2489
            {
2490
                char *endPtr = nullptr;
710✔
2491
                double dfVal = CPLStrtod(pszValue, &endPtr);
710✔
2492
                if (!(endPtr == nullptr || *endPtr == '\0'))
710✔
2493
                {
2494
                    CPLError(
×
2495
                        CE_Warning, CPLE_NotSupported,
2496
                        "'%s' is an unexpected value for %s %s of type float.",
2497
                        pszValue, pszKey, pszErrorMessageOptionType);
2498
                    bRet = false;
×
2499
                }
2500
                else
2501
                {
2502
                    if (pszMin && dfVal < CPLAtof(pszMin))
710✔
2503
                    {
2504
                        CPLError(CE_Warning, CPLE_NotSupported,
2✔
2505
                                 "'%s' is an unexpected value for %s %s that "
2506
                                 "should be >= %s.",
2507
                                 pszValue, pszKey, pszErrorMessageOptionType,
2508
                                 pszMin);
2509
                        bRet = false;
2✔
2510
                    }
2511
                    if (pszMax && dfVal > CPLAtof(pszMax))
710✔
2512
                    {
2513
                        CPLError(CE_Warning, CPLE_NotSupported,
×
2514
                                 "'%s' is an unexpected value for %s %s that "
2515
                                 "should be <= %s.",
2516
                                 pszValue, pszKey, pszErrorMessageOptionType,
2517
                                 pszMax);
2518
                        bRet = false;
×
2519
                    }
2520
                }
2521
            }
2522
            else if (EQUAL(pszType, "BOOLEAN"))
15,857✔
2523
            {
2524
                if (!(EQUAL(pszValue, "ON") || EQUAL(pszValue, "TRUE") ||
3,867✔
2525
                      EQUAL(pszValue, "YES") || EQUAL(pszValue, "OFF") ||
3,813✔
2526
                      EQUAL(pszValue, "FALSE") || EQUAL(pszValue, "NO")))
487✔
2527
                {
2528
                    CPLError(CE_Warning, CPLE_NotSupported,
×
2529
                             "'%s' is an unexpected value for %s %s of type "
2530
                             "boolean.",
2531
                             pszValue, pszKey, pszErrorMessageOptionType);
2532
                    bRet = false;
×
2533
                }
2534
            }
2535
            else if (EQUAL(pszType, "STRING-SELECT"))
11,990✔
2536
            {
2537
                bool bMatchFound = false;
6,239✔
2538
                CPLXMLNode *psStringSelect = psChildNode->psChild;
6,239✔
2539
                while (psStringSelect)
38,781✔
2540
                {
2541
                    if (psStringSelect->eType == CXT_Element &&
38,744✔
2542
                        EQUAL(psStringSelect->pszValue, "Value"))
18,980✔
2543
                    {
2544
                        CPLXMLNode *psOptionNode = psStringSelect->psChild;
18,980✔
2545
                        while (psOptionNode)
32,058✔
2546
                        {
2547
                            if (psOptionNode->eType == CXT_Text &&
19,280✔
2548
                                EQUAL(psOptionNode->pszValue, pszValue))
18,970✔
2549
                            {
2550
                                bMatchFound = true;
6,192✔
2551
                                break;
6,192✔
2552
                            }
2553
                            if (psOptionNode->eType == CXT_Attribute &&
13,088✔
2554
                                (EQUAL(psOptionNode->pszValue, "alias") ||
310✔
2555
                                 EQUAL(psOptionNode->pszValue,
15✔
2556
                                       "deprecated_alias")) &&
295✔
2557
                                EQUAL(psOptionNode->psChild->pszValue,
295✔
2558
                                      pszValue))
2559
                            {
2560
                                bMatchFound = true;
10✔
2561
                                break;
10✔
2562
                            }
2563
                            psOptionNode = psOptionNode->psNext;
13,078✔
2564
                        }
2565
                        if (bMatchFound)
18,980✔
2566
                            break;
6,202✔
2567
                    }
2568
                    psStringSelect = psStringSelect->psNext;
32,542✔
2569
                }
2570
                if (!bMatchFound)
6,239✔
2571
                {
2572
                    CPLError(CE_Warning, CPLE_NotSupported,
37✔
2573
                             "'%s' is an unexpected value for %s %s of type "
2574
                             "string-select.",
2575
                             pszValue, pszKey, pszErrorMessageOptionType);
2576
                    bRet = false;
37✔
2577
                }
2578
            }
2579
            else if (EQUAL(pszType, "STRING"))
5,751✔
2580
            {
2581
                const char *pszMaxSize =
2582
                    CPLGetXMLValue(psChildNode, "maxsize", nullptr);
5,751✔
2583
                if (pszMaxSize != nullptr)
5,751✔
2584
                {
2585
                    if (static_cast<int>(strlen(pszValue)) > atoi(pszMaxSize))
25✔
2586
                    {
2587
                        CPLError(CE_Warning, CPLE_NotSupported,
1✔
2588
                                 "'%s' is of size %d, whereas maximum size for "
2589
                                 "%s %s is %d.",
2590
                                 pszValue, static_cast<int>(strlen(pszValue)),
1✔
2591
                                 pszKey, pszErrorMessageOptionType,
2592
                                 atoi(pszMaxSize));
2593
                        bRet = false;
1✔
2594
                    }
2595
                }
2596
            }
2597
            else
2598
            {
2599
                /* Driver error */
2600
                CPLError(CE_Warning, CPLE_NotSupported,
×
2601
                         "%s : type '%s' for %s %s is not recognized.",
2602
                         pszErrorMessageContainerName, pszType, pszKey,
2603
                         pszErrorMessageOptionType);
2604
            }
2605
        }
2606
        else
2607
        {
2608
            /* Driver error */
2609
            CPLError(CE_Warning, CPLE_NotSupported, "%s : no type for %s %s.",
×
2610
                     pszErrorMessageContainerName, pszKey,
2611
                     pszErrorMessageOptionType);
2612
        }
2613
        CPLFree(pszKey);
25,091✔
2614
        ++papszOptionsToValidate;
25,091✔
2615
    }
2616

2617
    CPLDestroyXMLNode(psNode);
15,698✔
2618
    return bRet ? TRUE : FALSE;
15,698✔
2619
}
2620

2621
/************************************************************************/
2622
/*                         GDALIdentifyDriver()                         */
2623
/************************************************************************/
2624

2625
/**
2626
 * \brief Identify the driver that can open a dataset.
2627
 *
2628
 * This function will try to identify the driver that can open the passed file
2629
 * name by invoking the Identify method of each registered GDALDriver in turn.
2630
 * The first driver that successfully identifies the file name will be returned.
2631
 * If all drivers fail then NULL is returned.
2632
 *
2633
 * In order to reduce the need for such searches to touch the operating system
2634
 * file system machinery, it is possible to give an optional list of files.
2635
 * This is the list of all files at the same level in the file system as the
2636
 * target file, including the target file. The filenames will not include any
2637
 * path components, and are essentially just the output of VSIReadDir() on the
2638
 * parent directory. If the target object does not have filesystem semantics
2639
 * then the file list should be NULL.
2640
 *
2641
 * @param pszFilename the name of the file to access.  In the case of
2642
 * exotic drivers this may not refer to a physical file, but instead contain
2643
 * information for the driver on how to access a dataset.
2644
 *
2645
 * @param papszFileList an array of strings, whose last element is the NULL
2646
 * pointer.  These strings are filenames that are auxiliary to the main
2647
 * filename. The passed value may be NULL.
2648
 *
2649
 * @return A GDALDriverH handle or NULL on failure.  For C++ applications
2650
 * this handle can be cast to a GDALDriver *.
2651
 */
2652

2653
GDALDriverH CPL_STDCALL GDALIdentifyDriver(const char *pszFilename,
24,526✔
2654
                                           CSLConstList papszFileList)
2655

2656
{
2657
    return GDALIdentifyDriverEx(pszFilename, 0, nullptr, papszFileList);
24,526✔
2658
}
2659

2660
/************************************************************************/
2661
/*                         GDALIdentifyDriverEx()                       */
2662
/************************************************************************/
2663

2664
/**
2665
 * \brief Identify the driver that can open a dataset.
2666
 *
2667
 * This function will try to identify the driver that can open the passed file
2668
 * name by invoking the Identify method of each registered GDALDriver in turn.
2669
 * The first driver that successfully identifies the file name will be returned.
2670
 * If all drivers fail then NULL is returned.
2671
 *
2672
 * In order to reduce the need for such searches to touch the operating system
2673
 * file system machinery, it is possible to give an optional list of files.
2674
 * This is the list of all files at the same level in the file system as the
2675
 * target file, including the target file. The filenames will not include any
2676
 * path components, and are essentially just the output of VSIReadDir() on the
2677
 * parent directory. If the target object does not have filesystem semantics
2678
 * then the file list should be NULL.
2679
 *
2680
 * @param pszFilename the name of the file to access.  In the case of
2681
 * exotic drivers this may not refer to a physical file, but instead contain
2682
 * information for the driver on how to access a dataset.
2683
 *
2684
 * @param nIdentifyFlags a combination of GDAL_OF_RASTER for raster drivers
2685
 * or GDAL_OF_VECTOR for vector drivers. If none of the value is specified,
2686
 * both kinds are implied.
2687
 *
2688
 * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL
2689
 * terminated list of strings with the driver short names that must be
2690
 * considered.
2691
 *
2692
 * @param papszFileList an array of strings, whose last element is the NULL
2693
 * pointer.  These strings are filenames that are auxiliary to the main
2694
 * filename. The passed value may be NULL.
2695
 *
2696
 * @return A GDALDriverH handle or NULL on failure.  For C++ applications
2697
 * this handle can be cast to a GDALDriver *.
2698
 *
2699
 * @since GDAL 2.2
2700
 */
2701

2702
GDALDriverH CPL_STDCALL GDALIdentifyDriverEx(
24,616✔
2703
    const char *pszFilename, unsigned int nIdentifyFlags,
2704
    const char *const *papszAllowedDrivers, const char *const *papszFileList)
2705
{
2706
    GDALDriverManager *poDM = GetGDALDriverManager();
24,616✔
2707
    CPLAssert(nullptr != poDM);
24,616✔
2708

2709
    // If no driver kind is specified, assume all are to be probed.
2710
    if ((nIdentifyFlags & GDAL_OF_KIND_MASK) == 0)
24,616✔
2711
        nIdentifyFlags |= GDAL_OF_KIND_MASK & ~GDAL_OF_MULTIDIM_RASTER;
24,573✔
2712

2713
    GDALOpenInfo oOpenInfo(pszFilename, nIdentifyFlags, papszFileList);
49,232✔
2714
    oOpenInfo.papszAllowedDrivers = papszAllowedDrivers;
24,616✔
2715

2716
    CPLErrorStateBackuper oBackuper;
49,232✔
2717
    CPLErrorSetState(CE_None, CPLE_AppDefined, "");
24,616✔
2718

2719
    const int nDriverCount = poDM->GetDriverCount();
24,616✔
2720

2721
    // First pass: only use drivers that have a pfnIdentify implementation.
2722
    std::vector<GDALDriver *> apoSecondPassDrivers;
49,232✔
2723
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,239,470✔
2724
    {
2725
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,216,140✔
2726
        if (papszAllowedDrivers != nullptr &&
5,223,350✔
2727
            CSLFindString(papszAllowedDrivers,
7,183✔
2728
                          GDALGetDriverShortName(poDriver)) == -1)
2729
        {
2730
            continue;
979,737✔
2731
        }
2732

2733
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,210,320✔
2734

2735
        if (poDriver->pfnIdentify == nullptr &&
5,209,030✔
2736
            poDriver->pfnIdentifyEx == nullptr)
968,879✔
2737
        {
2738
            continue;
968,879✔
2739
        }
2740

2741
        if (papszAllowedDrivers != nullptr &&
4,240,190✔
2742
            CSLFindString(papszAllowedDrivers,
44✔
2743
                          GDALGetDriverShortName(poDriver)) == -1)
2744
            continue;
×
2745
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
12,715,800✔
2746
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
4,240,650✔
2747
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
499✔
2748
            continue;
141✔
2749
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
12,723,200✔
2750
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
4,244,800✔
2751
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
4,797✔
2752
            continue;
3,578✔
2753

2754
        if (poDriver->pfnIdentifyEx)
4,236,430✔
2755
        {
2756
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) > 0)
×
2757
                return poDriver;
×
2758
        }
2759
        else
2760
        {
2761
            const int nIdentifyRes = poDriver->pfnIdentify(&oOpenInfo);
4,236,430✔
2762
            if (nIdentifyRes > 0)
4,236,410✔
2763
                return poDriver;
1,292✔
2764
            if (nIdentifyRes < 0 &&
4,259,350✔
2765
                poDriver->GetMetadataItem("IS_NON_LOADED_PLUGIN"))
24,227✔
2766
            {
2767
                // Not loaded plugin
2768
                apoSecondPassDrivers.push_back(poDriver);
36✔
2769
            }
2770
        }
2771
    }
2772

2773
    // second pass: try loading plugin drivers
2774
    for (auto poDriver : apoSecondPassDrivers)
23,357✔
2775
    {
2776
        // Force plugin driver loading
2777
        poDriver->GetMetadata();
34✔
2778
        if (poDriver->pfnIdentify(&oOpenInfo) > 0)
34✔
2779
            return poDriver;
1✔
2780
    }
2781

2782
    // third pass: slow method.
2783
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,140,630✔
2784
    {
2785
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,117,390✔
2786
        if (papszAllowedDrivers != nullptr &&
5,121,140✔
2787
            CSLFindString(papszAllowedDrivers,
3,739✔
2788
                          GDALGetDriverShortName(poDriver)) == -1)
2789
        {
2790
            continue;
3,722✔
2791
        }
2792

2793
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,113,680✔
2794

2795
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
15,339,600✔
2796
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
5,114,120✔
2797
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
438✔
2798
            continue;
137✔
2799
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
15,341,200✔
2800
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
5,115,080✔
2801
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
1,535✔
2802
            continue;
918✔
2803

2804
        if (poDriver->pfnIdentifyEx != nullptr)
5,112,620✔
2805
        {
2806
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) == 0)
×
2807
                continue;
×
2808
        }
2809
        else if (poDriver->pfnIdentify != nullptr)
5,112,620✔
2810
        {
2811
            if (poDriver->pfnIdentify(&oOpenInfo) == 0)
4,161,180✔
2812
                continue;
4,137,430✔
2813
        }
2814

2815
        GDALDataset *poDS;
2816
        if (poDriver->pfnOpen != nullptr)
975,196✔
2817
        {
2818
            poDS = poDriver->pfnOpen(&oOpenInfo);
928,669✔
2819
            if (poDS != nullptr)
928,667✔
2820
            {
2821
                delete poDS;
91✔
2822
                return GDALDriver::ToHandle(poDriver);
91✔
2823
            }
2824

2825
            if (CPLGetLastErrorType() != CE_None)
928,576✔
2826
                return nullptr;
×
2827
        }
2828
        else if (poDriver->pfnOpenWithDriverArg != nullptr)
46,527✔
2829
        {
2830
            poDS = poDriver->pfnOpenWithDriverArg(poDriver, &oOpenInfo);
×
2831
            if (poDS != nullptr)
×
2832
            {
2833
                delete poDS;
×
2834
                return GDALDriver::ToHandle(poDriver);
×
2835
            }
2836

2837
            if (CPLGetLastErrorType() != CE_None)
×
2838
                return nullptr;
×
2839
        }
2840
    }
2841

2842
    return nullptr;
23,232✔
2843
}
2844

2845
/************************************************************************/
2846
/*                          SetMetadataItem()                           */
2847
/************************************************************************/
2848

2849
CPLErr GDALDriver::SetMetadataItem(const char *pszName, const char *pszValue,
3,913,210✔
2850
                                   const char *pszDomain)
2851

2852
{
2853
    if (pszDomain == nullptr || pszDomain[0] == '\0')
3,913,210✔
2854
    {
2855
        /* Automatically sets GDAL_DMD_EXTENSIONS from GDAL_DMD_EXTENSION */
2856
        if (EQUAL(pszName, GDAL_DMD_EXTENSION) &&
4,081,660✔
2857
            GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSIONS) == nullptr)
173,319✔
2858
        {
2859
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSIONS, pszValue);
173,319✔
2860
        }
2861
        /* and vice-versa if there is a single extension in GDAL_DMD_EXTENSIONS */
2862
        else if (EQUAL(pszName, GDAL_DMD_EXTENSIONS) &&
7,539,630✔
2863
                 strchr(pszValue, ' ') == nullptr &&
3,741,500✔
2864
                 GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSION) ==
6,476✔
2865
                     nullptr)
2866
        {
2867
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSION, pszValue);
6,476✔
2868
        }
2869
    }
2870
    return GDALMajorObject::SetMetadataItem(pszName, pszValue, pszDomain);
3,913,210✔
2871
}
2872

2873
/************************************************************************/
2874
/*                         InstantiateAlgorithm()                       */
2875
/************************************************************************/
2876

2877
//! @cond Doxygen_Suppress
2878

2879
GDALAlgorithm *
2880
GDALDriver::InstantiateAlgorithm(const std::vector<std::string> &aosPath)
56✔
2881
{
2882
    pfnInstantiateAlgorithm = GetInstantiateAlgorithmCallback();
56✔
2883
    if (pfnInstantiateAlgorithm)
56✔
2884
        return pfnInstantiateAlgorithm(aosPath);
56✔
2885
    return nullptr;
×
2886
}
2887

2888
/************************************************************************/
2889
/*                        DeclareAlgorithm()                            */
2890
/************************************************************************/
2891

2892
void GDALDriver::DeclareAlgorithm(const std::vector<std::string> &aosPath)
6,482✔
2893
{
2894
    const std::string osDriverName = GetDescription();
12,964✔
2895
    auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
6,482✔
2896

2897
    if (!singleton.HasDeclaredSubAlgorithm({"driver"}))
12,964✔
2898
    {
2899
        singleton.DeclareAlgorithm(
2,832✔
2900
            {"driver"},
2901
            []() -> std::unique_ptr<GDALAlgorithm>
39✔
2902
            {
2903
                return std::make_unique<GDALContainerAlgorithm>(
78✔
2904
                    "driver", "Command for driver specific operations.");
39✔
2905
            });
1,416✔
2906
    }
2907

2908
    std::vector<std::string> path = {"driver",
2909
                                     CPLString(osDriverName).tolower()};
38,892✔
2910
    if (!singleton.HasDeclaredSubAlgorithm(path))
6,482✔
2911
    {
2912
        // coverity[copy_constructor_call]
2913
        auto lambda = [osDriverName]() -> std::unique_ptr<GDALAlgorithm>
137✔
2914
        {
2915
            auto poDriver =
2916
                GetGDALDriverManager()->GetDriverByName(osDriverName.c_str());
137✔
2917
            if (poDriver)
137✔
2918
            {
2919
                const char *pszHelpTopic =
2920
                    poDriver->GetMetadataItem(GDAL_DMD_HELPTOPIC);
137✔
2921
                return std::make_unique<GDALContainerAlgorithm>(
274✔
2922
                    CPLString(osDriverName).tolower(),
274✔
2923
                    std::string("Command for ")
274✔
2924
                        .append(osDriverName)
137✔
2925
                        .append(" driver specific operations."),
2926
                    pszHelpTopic ? std::string("/").append(pszHelpTopic)
274✔
2927
                                 : std::string());
137✔
2928
            }
2929
            return nullptr;
×
2930
        };
5,664✔
2931
        singleton.DeclareAlgorithm(path, std::move(lambda));
5,664✔
2932
    }
2933

2934
    path.insert(path.end(), aosPath.begin(), aosPath.end());
6,482✔
2935

2936
    // coverity[copy_constructor_call]
2937
    auto lambda = [osDriverName, aosPath]() -> std::unique_ptr<GDALAlgorithm>
56✔
2938
    {
2939
        auto poDriver =
2940
            GetGDALDriverManager()->GetDriverByName(osDriverName.c_str());
56✔
2941
        if (poDriver)
56✔
2942
            return std::unique_ptr<GDALAlgorithm>(
2943
                poDriver->InstantiateAlgorithm(aosPath));
56✔
2944
        return nullptr;
×
2945
    };
6,482✔
2946

2947
    singleton.DeclareAlgorithm(path, std::move(lambda));
6,482✔
2948
}
6,482✔
2949

2950
//! @endcond
2951

2952
/************************************************************************/
2953
/*                   DoesDriverHandleExtension()                        */
2954
/************************************************************************/
2955

2956
static bool DoesDriverHandleExtension(GDALDriverH hDriver, const char *pszExt)
164,304✔
2957
{
2958
    bool bRet = false;
164,304✔
2959
    const char *pszDriverExtensions =
2960
        GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSIONS, nullptr);
164,304✔
2961
    if (pszDriverExtensions)
164,304✔
2962
    {
2963
        const CPLStringList aosTokens(CSLTokenizeString(pszDriverExtensions));
271,338✔
2964
        const int nTokens = aosTokens.size();
135,669✔
2965
        for (int j = 0; j < nTokens; ++j)
304,634✔
2966
        {
2967
            if (EQUAL(pszExt, aosTokens[j]))
173,290✔
2968
            {
2969
                bRet = true;
4,325✔
2970
                break;
4,325✔
2971
            }
2972
        }
2973
    }
2974
    return bRet;
164,304✔
2975
}
2976

2977
/************************************************************************/
2978
/*                     IsOnlyExpectedGDBDrivers()                       */
2979
/************************************************************************/
2980

2981
static bool IsOnlyExpectedGDBDrivers(const CPLStringList &aosDriverNames)
1✔
2982
{
2983
    for (const char *pszDrvName : aosDriverNames)
3✔
2984
    {
2985
        if (!EQUAL(pszDrvName, "OpenFileGDB") &&
2✔
2986
            !EQUAL(pszDrvName, "FileGDB") && !EQUAL(pszDrvName, "GPSBabel"))
1✔
2987
        {
2988
            return false;
×
2989
        }
2990
    }
2991
    return true;
1✔
2992
}
2993

2994
/************************************************************************/
2995
/*                  GDALGetOutputDriversForDatasetName()                */
2996
/************************************************************************/
2997

2998
/** Return a list of driver short names that are likely candidates for the
2999
 * provided output file name.
3000
 *
3001
 * @param pszDestDataset Output dataset name (might not exist).
3002
 * @param nFlagRasterVector GDAL_OF_RASTER, GDAL_OF_VECTOR or
3003
 *                          binary-or'ed combination of both
3004
 * @param bSingleMatch Whether a single match is desired, that is to say the
3005
 *                     returned list will contain at most one item, which will
3006
 *                     be the first driver in the order they are registered to
3007
 *                     match the output dataset name. Note that in this mode, if
3008
 *                     nFlagRasterVector==GDAL_OF_RASTER and pszDestDataset has
3009
 *                     no extension, GTiff will be selected.
3010
 * @param bEmitWarning Whether a warning should be emitted when bSingleMatch is
3011
 *                     true and there are more than 2 candidates.
3012
 * @return NULL terminated list of driver short names.
3013
 * To be freed with CSLDestroy()
3014
 * @since 3.9
3015
 */
3016
char **GDALGetOutputDriversForDatasetName(const char *pszDestDataset,
2,500✔
3017
                                          int nFlagRasterVector,
3018
                                          bool bSingleMatch, bool bEmitWarning)
3019
{
3020
    CPLStringList aosDriverNames;
5,000✔
3021
    CPLStringList aosMissingDriverNames;
5,000✔
3022

3023
    std::string osExt = CPLGetExtensionSafe(pszDestDataset);
5,000✔
3024
    if (EQUAL(osExt.c_str(), "zip"))
2,500✔
3025
    {
3026
        const CPLString osLower(CPLString(pszDestDataset).tolower());
2✔
3027
        if (osLower.endsWith(".shp.zip"))
1✔
3028
        {
3029
            osExt = "shp.zip";
1✔
3030
        }
3031
        else if (osLower.endsWith(".gpkg.zip"))
×
3032
        {
3033
            osExt = "gpkg.zip";
×
3034
        }
3035
    }
3036
    else if (EQUAL(osExt.c_str(), "json"))
2,499✔
3037
    {
3038
        const CPLString osLower(CPLString(pszDestDataset).tolower());
1✔
3039
        if (osLower.endsWith(".gdalg.json"))
1✔
3040
            return nullptr;
×
3041
    }
3042

3043
    auto poDM = GetGDALDriverManager();
2,500✔
3044
    const int nDriverCount = poDM->GetDriverCount(true);
2,500✔
3045
    GDALDriver *poMissingPluginDriver = nullptr;
2,500✔
3046
    for (int i = 0; i < nDriverCount; i++)
552,100✔
3047
    {
3048
        GDALDriver *poDriver = poDM->GetDriver(i, true);
549,600✔
3049
        bool bOk = false;
549,600✔
3050
        if ((poDriver->GetMetadataItem(GDAL_DCAP_CREATE) != nullptr ||
549,600✔
3051
             poDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY) != nullptr) &&
827,097✔
3052
            (((nFlagRasterVector & GDAL_OF_RASTER) &&
277,497✔
3053
              poDriver->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr) ||
229,325✔
3054
             ((nFlagRasterVector & GDAL_OF_VECTOR) &&
134,944✔
3055
              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)))
48,172✔
3056
        {
3057
            bOk = true;
166,855✔
3058
        }
3059
        else if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR_TRANSLATE_FROM) &&
384,811✔
3060
                 (nFlagRasterVector & GDAL_OF_VECTOR) != 0)
2,066✔
3061
        {
3062
            bOk = true;
×
3063
        }
3064
        if (bOk)
549,600✔
3065
        {
3066
            if (!osExt.empty() &&
331,159✔
3067
                DoesDriverHandleExtension(GDALDriver::ToHandle(poDriver),
164,304✔
3068
                                          osExt.c_str()))
3069
            {
3070
                if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
4,325✔
3071
                {
3072
                    poMissingPluginDriver = poDriver;
×
3073
                    aosMissingDriverNames.AddString(poDriver->GetDescription());
×
3074
                }
3075
                else
3076
                    aosDriverNames.AddString(poDriver->GetDescription());
4,325✔
3077
            }
3078
            else
3079
            {
3080
                const char *pszPrefix =
3081
                    poDriver->GetMetadataItem(GDAL_DMD_CONNECTION_PREFIX);
162,530✔
3082
                if (pszPrefix && STARTS_WITH_CI(pszDestDataset, pszPrefix))
162,530✔
3083
                {
3084
                    if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
9✔
3085
                    {
3086
                        poMissingPluginDriver = poDriver;
×
3087
                        aosMissingDriverNames.AddString(
3088
                            poDriver->GetDescription());
×
3089
                    }
3090
                    else
3091
                        aosDriverNames.AddString(poDriver->GetDescription());
9✔
3092
                }
3093
            }
3094
        }
3095
    }
3096

3097
    // GMT is registered before netCDF for opening reasons, but we want
3098
    // netCDF to be used by default for output.
3099
    if (EQUAL(osExt.c_str(), "nc") && aosDriverNames.size() == 2 &&
2,502✔
3100
        EQUAL(aosDriverNames[0], "GMT") && EQUAL(aosDriverNames[1], "netCDF"))
2,502✔
3101
    {
3102
        aosDriverNames.Clear();
×
3103
        aosDriverNames.AddString("netCDF");
×
3104
        aosDriverNames.AddString("GMT");
×
3105
    }
3106

3107
    if (bSingleMatch)
2,500✔
3108
    {
3109
        if (nFlagRasterVector == GDAL_OF_RASTER)
2,426✔
3110
        {
3111
            if (aosDriverNames.empty())
2,066✔
3112
            {
3113
                if (osExt.empty())
14✔
3114
                {
3115
                    aosDriverNames.AddString("GTiff");
11✔
3116
                }
3117
            }
3118
            else if (aosDriverNames.size() >= 2)
2,052✔
3119
            {
3120
                if (bEmitWarning && !(EQUAL(aosDriverNames[0], "GTiff") &&
3,744✔
3121
                                      EQUAL(aosDriverNames[1], "COG")))
1,872✔
3122
                {
3123
                    CPLError(CE_Warning, CPLE_AppDefined,
×
3124
                             "Several drivers matching %s extension. Using %s",
3125
                             osExt.c_str(), aosDriverNames[0]);
3126
                }
3127
                const std::string osDrvName = aosDriverNames[0];
3,744✔
3128
                aosDriverNames.Clear();
1,872✔
3129
                aosDriverNames.AddString(osDrvName.c_str());
1,872✔
3130
            }
3131
        }
3132
        else if (EQUAL(osExt.c_str(), "gdb") &&
361✔
3133
                 IsOnlyExpectedGDBDrivers(aosDriverNames))
1✔
3134
        {
3135
            // Do not warn about that case given that FileGDB write support
3136
            // forwards to OpenFileGDB one. And also consider GPSBabel as too
3137
            // marginal to deserve the warning.
3138
            aosDriverNames.Clear();
1✔
3139
            aosDriverNames.AddString("OpenFileGDB");
1✔
3140
        }
3141
        else if (aosDriverNames.size() >= 2)
359✔
3142
        {
3143
            if (bEmitWarning)
×
3144
            {
3145
                CPLError(CE_Warning, CPLE_AppDefined,
×
3146
                         "Several drivers matching %s extension. Using %s",
3147
                         osExt.c_str(), aosDriverNames[0]);
3148
            }
3149
            const std::string osDrvName = aosDriverNames[0];
×
3150
            aosDriverNames.Clear();
×
3151
            aosDriverNames.AddString(osDrvName.c_str());
×
3152
        }
3153
    }
3154

3155
    if (aosDriverNames.empty() && bEmitWarning &&
2,529✔
3156
        aosMissingDriverNames.size() == 1 && poMissingPluginDriver)
2,529✔
3157
    {
3158
        CPLError(CE_Warning, CPLE_AppDefined,
×
3159
                 "No installed driver matching %s extension, but %s driver is "
3160
                 "known. However plugin %s",
3161
                 osExt.c_str(), poMissingPluginDriver->GetDescription(),
×
3162
                 GDALGetMessageAboutMissingPluginDriver(poMissingPluginDriver)
×
3163
                     .c_str());
3164
    }
3165

3166
    return aosDriverNames.StealList();
2,500✔
3167
}
3168

3169
/************************************************************************/
3170
/*                GDALGetMessageAboutMissingPluginDriver()              */
3171
/************************************************************************/
3172

3173
std::string
3174
GDALGetMessageAboutMissingPluginDriver(GDALDriver *poMissingPluginDriver)
×
3175
{
3176
    std::string osMsg =
3177
        poMissingPluginDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME");
×
3178
    osMsg += " is not available in your "
3179
             "installation.";
×
3180
    if (const char *pszInstallationMsg = poMissingPluginDriver->GetMetadataItem(
×
3181
            GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE))
×
3182
    {
3183
        osMsg += " ";
×
3184
        osMsg += pszInstallationMsg;
×
3185
    }
3186

3187
    VSIStatBuf sStat;
3188
    if (const char *pszGDALDriverPath =
×
3189
            CPLGetConfigOption("GDAL_DRIVER_PATH", nullptr))
×
3190
    {
3191
        if (VSIStat(pszGDALDriverPath, &sStat) != 0)
×
3192
        {
3193
            if (osMsg.back() != '.')
×
3194
                osMsg += ".";
×
3195
            osMsg += " Directory '";
×
3196
            osMsg += pszGDALDriverPath;
×
3197
            osMsg += "' pointed by GDAL_DRIVER_PATH does not exist.";
×
3198
        }
3199
    }
3200
    else
3201
    {
3202
        if (osMsg.back() != '.')
×
3203
            osMsg += ".";
×
3204
#ifdef INSTALL_PLUGIN_FULL_DIR
3205
        if (VSIStat(INSTALL_PLUGIN_FULL_DIR, &sStat) != 0)
3206
        {
3207
            osMsg += " Directory '";
3208
            osMsg += INSTALL_PLUGIN_FULL_DIR;
3209
            osMsg += "' hardcoded in the GDAL library does not "
3210
                     "exist and the GDAL_DRIVER_PATH "
3211
                     "configuration option is not set.";
3212
        }
3213
        else
3214
#endif
3215
        {
3216
            osMsg += " The GDAL_DRIVER_PATH configuration "
3217
                     "option is not set.";
×
3218
        }
3219
    }
3220
    return osMsg;
×
3221
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc