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

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

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

86.1
/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 <algorithm>
21
#include <cerrno>
22
#include <cstdlib>
23
#include <cstring>
24
#include <set>
25
#include <sys/stat.h>
26

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

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

41
GDALDriver::GDALDriver() = default;
42

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

47
GDALDriver::~GDALDriver()
435,117✔
48

49
{
50
    if (pfnUnloadDriver != nullptr)
246,522✔
51
        pfnUnloadDriver(this);
6,803✔
52
}
435,117✔
53

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

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

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

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

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

83
void CPL_STDCALL GDALDestroyDriver(GDALDriverH hDriver)
×
84

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

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

94
//! @cond Doxygen_Suppress
95

96
GDALDataset *GDALDriver::Open(GDALOpenInfo *poOpenInfo, bool bSetOpenOptions)
435,371✔
97
{
98

99
    GDALDataset *poDS = nullptr;
435,371✔
100
    pfnOpen = GetOpenCallback();
435,371✔
101
    if (pfnOpen != nullptr)
435,286✔
102
    {
103
        poDS = pfnOpen(poOpenInfo);
435,364✔
104
    }
UNCOV
105
    else if (pfnOpenWithDriverArg != nullptr)
×
106
    {
107
        poDS = pfnOpenWithDriverArg(this, poOpenInfo);
3✔
108
    }
109

110
    if (poDS)
435,622✔
111
    {
112
        // Only set GDAL_OF_THREAD_SAFE if the driver itself has set it in
113
        // poDS->nOpenFlags
114
        int nOpenFlags = poOpenInfo->nOpenFlags &
55,173✔
115
                         ~(GDAL_OF_FROM_GDALOPEN | GDAL_OF_THREAD_SAFE);
116
        if (poDS->nOpenFlags & GDAL_OF_THREAD_SAFE)
55,173✔
117
            nOpenFlags |= GDAL_OF_THREAD_SAFE;
905✔
118
        poDS->nOpenFlags = nOpenFlags;
55,173✔
119

120
        if (strlen(poDS->GetDescription()) == 0)
55,173✔
121
            poDS->SetDescription(poOpenInfo->pszFilename);
11,619✔
122

123
        if (poDS->poDriver == nullptr)
55,161✔
124
            poDS->poDriver = this;
51,185✔
125

126
        if (poDS->papszOpenOptions == nullptr && bSetOpenOptions)
55,161✔
127
        {
128
            poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
29✔
129
        }
130

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

145
            poDS->AddToDatasetOpenList();
45,734✔
146
        }
147
    }
148

149
    return poDS;
435,703✔
150
}
151

152
//! @endcond
153

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

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

191
GDALDataset *GDALDriver::Create(const char *pszFilename, int nXSize, int nYSize,
22,078✔
192
                                int nBands, GDALDataType eType,
193
                                CSLConstList papszOptions)
194

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

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

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

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

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

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

269
    /* -------------------------------------------------------------------- */
270
    /*      Proceed with creation.                                          */
271
    /* -------------------------------------------------------------------- */
272
    CPLDebug("GDAL", "GDALDriver::Create(%s,%s,%d,%d,%d,%s,%p)",
44,148✔
273
             GetDescription(), pszFilename, nXSize, nYSize, nBands,
22,074✔
274
             GDALGetDataTypeName(eType), papszOptions);
275

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

293
    if (poDS != nullptr)
22,074✔
294
    {
295
        if (poDS->GetDescription() == nullptr ||
41,634✔
296
            strlen(poDS->GetDescription()) == 0)
20,817✔
297
            poDS->SetDescription(pszFilename);
18,226✔
298

299
        if (poDS->poDriver == nullptr)
20,817✔
300
            poDS->poDriver = this;
20,173✔
301

302
        poDS->AddToDatasetOpenList();
20,817✔
303
    }
304

305
    return poDS;
22,074✔
306
}
307

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

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

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

324
{
325
    VALIDATE_POINTER1(hDriver, "GDALCreate", nullptr);
18,342✔
326

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

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

338
    return hDS;
18,342✔
339
}
340

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

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

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

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

381
        return nullptr;
×
382
    }
383

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

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

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

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

411
    return poDstDS;
465✔
412
}
413

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

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

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

438
//! @cond Doxygen_Suppress
439

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

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

462
//! @endcond
463

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

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

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

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

482
{
483
    if (pfnProgress == nullptr)
8,281✔
484
        pfnProgress = GDALDummyProgress;
6,344✔
485

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

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

496
    int nTotalBandsWithMask = 0;
8,281✔
497
    for (int iBand = 0; iBand < nBands; ++iBand)
29,073✔
498
    {
499
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
20,792✔
500

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

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

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

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

565
    return eErr;
8,281✔
566
}
567

568
/************************************************************************/
569
/*                         DefaultCreateCopy()                          */
570
/************************************************************************/
571

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

578
{
579
    if (pfnProgress == nullptr)
1,344✔
580
        pfnProgress = GDALDummyProgress;
×
581

582
    CPLErrorReset();
1,344✔
583

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

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

617
    CPLDebug("GDAL", "Using default GDALDriver::CreateCopy implementation.");
1,327✔
618

619
    const int nLayerCount = poSrcDS->GetLayerCount();
1,327✔
620
    if (nBands == 0 && nLayerCount == 0 &&
1,350✔
621
        GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
23✔
622
    {
623
        CPLError(CE_Failure, CPLE_NotSupported,
16✔
624
                 "GDALDriver::DefaultCreateCopy does not support zero band");
625
        return nullptr;
16✔
626
    }
627
    if (poSrcDS->GetDriver() != nullptr &&
1,311✔
628
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
1,267✔
629
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
1,260✔
630
        GetMetadataItem(GDAL_DCAP_RASTER) == nullptr &&
2,581✔
631
        GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)
3✔
632
    {
633
        CPLError(CE_Failure, CPLE_NotSupported,
3✔
634
                 "Source driver is raster-only whereas output driver is "
635
                 "vector-only");
636
        return nullptr;
3✔
637
    }
638
    else if (poSrcDS->GetDriver() != nullptr &&
1,308✔
639
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) ==
1,264✔
640
                 nullptr &&
7✔
641
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) !=
7✔
642
                 nullptr &&
7✔
643
             GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
2,572✔
644
             GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
×
645
    {
646
        CPLError(CE_Failure, CPLE_NotSupported,
×
647
                 "Source driver is vector-only whereas output driver is "
648
                 "raster-only");
649
        return nullptr;
×
650
    }
651

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

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

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

677
        if (pszValue == nullptr)
2,580✔
678
            continue;
2,579✔
679

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

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

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

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

696
    /* -------------------------------------------------------------------- */
697
    /*      Create destination dataset.                                     */
698
    /* -------------------------------------------------------------------- */
699
    GDALDataType eType = GDT_Unknown;
1,306✔
700

701
    if (nBands > 0)
1,306✔
702
        eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1,290✔
703
    GDALDataset *poDstDS =
704
        Create(pszFilename, nXSize, nYSize, nBands, eType, papszCreateOptions);
1,306✔
705

706
    CSLDestroy(papszCreateOptions);
1,306✔
707

708
    if (poDstDS == nullptr)
1,306✔
709
        return nullptr;
316✔
710

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

727
    /* -------------------------------------------------------------------- */
728
    /*      Try setting the projection and geotransform if it seems         */
729
    /*      suitable.                                                       */
730
    /* -------------------------------------------------------------------- */
731
    if (nDstBands == 0 && !bStrict)
990✔
732
        CPLTurnFailureIntoWarning(true);
4✔
733

734
    GDALGeoTransform gt;
990✔
735
    if (eErr == CE_None && poSrcDS->GetGeoTransform(gt) == CE_None &&
1,868✔
736
        gt != GDALGeoTransform())
1,868✔
737
    {
738
        eErr = poDstDS->SetGeoTransform(gt);
875✔
739
        if (!bStrict)
875✔
740
            eErr = CE_None;
530✔
741
    }
742

743
    if (eErr == CE_None)
990✔
744
    {
745
        const auto poSrcSRS = poSrcDS->GetSpatialRef();
981✔
746
        if (poSrcSRS && !poSrcSRS->IsEmpty())
981✔
747
        {
748
            eErr = poDstDS->SetSpatialRef(poSrcSRS);
813✔
749
            if (!bStrict)
813✔
750
                eErr = CE_None;
500✔
751
        }
752
    }
753

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

765
    if (nDstBands == 0 && !bStrict)
990✔
766
        CPLTurnFailureIntoWarning(false);
4✔
767

768
    /* -------------------------------------------------------------------- */
769
    /*      Copy metadata.                                                  */
770
    /* -------------------------------------------------------------------- */
771
    DefaultCopyMetadata(poSrcDS, poDstDS, papszOptions, nullptr);
990✔
772

773
    /* -------------------------------------------------------------------- */
774
    /*      Loop copying bands.                                             */
775
    /* -------------------------------------------------------------------- */
776
    for (int iBand = 0; eErr == CE_None && iBand < nDstBands; ++iBand)
2,528✔
777
    {
778
        GDALRasterBand *const poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
1,538✔
779
        GDALRasterBand *const poDstBand = poDstDS->GetRasterBand(iBand + 1);
1,538✔
780

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

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

800
        if (strlen(poSrcBand->GetDescription()) > 0)
1,538✔
801
            poDstBand->SetDescription(poSrcBand->GetDescription());
52✔
802

803
        if (CSLCount(poSrcBand->GetMetadata()) > 0)
1,538✔
804
            poDstBand->SetMetadata(poSrcBand->GetMetadata());
112✔
805

806
        int bSuccess = FALSE;
1,538✔
807
        double dfValue = poSrcBand->GetOffset(&bSuccess);
1,538✔
808
        if (bSuccess && dfValue != 0.0)
1,538✔
809
            poDstBand->SetOffset(dfValue);
5✔
810

811
        dfValue = poSrcBand->GetScale(&bSuccess);
1,538✔
812
        if (bSuccess && dfValue != 1.0)
1,538✔
813
            poDstBand->SetScale(dfValue);
4✔
814

815
        GDALCopyNoDataValue(poDstBand, poSrcBand);
1,538✔
816

817
        if (poSrcBand->GetColorInterpretation() != GCI_Undefined &&
2,527✔
818
            poSrcBand->GetColorInterpretation() !=
989✔
819
                poDstBand->GetColorInterpretation())
989✔
820
            poDstBand->SetColorInterpretation(
774✔
821
                poSrcBand->GetColorInterpretation());
774✔
822

823
        char **papszCatNames = poSrcBand->GetCategoryNames();
1,538✔
824
        if (nullptr != papszCatNames)
1,538✔
825
            poDstBand->SetCategoryNames(papszCatNames);
1✔
826

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

836
        if (!bStrict)
1,538✔
837
        {
838
            CPLTurnFailureIntoWarning(false);
926✔
839
        }
840
        else
841
        {
842
            eErr = CPLGetLastErrorType();
612✔
843
        }
844
    }
845

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

861
    /* -------------------------------------------------------------------- */
862
    /*      Should we copy some masks over?                                 */
863
    /* -------------------------------------------------------------------- */
864
    if (eErr == CE_None && nDstBands > 0)
990✔
865
        eErr = DefaultCopyMasks(poSrcDS, poDstDS, eErr);
928✔
866

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

878
                if (poLayer == nullptr)
10✔
879
                    continue;
×
880

881
                poDstDS->CopyLayer(poLayer, poLayer->GetName(), nullptr);
10✔
882
            }
883
        }
884
    }
885

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

904
    return poDstDS;
941✔
905
}
906

907
/************************************************************************/
908
/*                       DefaultCopyMetadata()                          */
909
/************************************************************************/
910

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

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

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

999
/************************************************************************/
1000
/*                      QuietDeleteForCreateCopy()                      */
1001
/************************************************************************/
1002

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

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

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

1093
        QuietDelete(pszFilename);
10,888✔
1094
    }
1095

1096
    return CE_None;
11,438✔
1097
}
1098

1099
//! @endcond
1100

1101
/************************************************************************/
1102
/*                             CreateCopy()                             */
1103
/************************************************************************/
1104

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

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

1190
{
1191
    if (pfnProgress == nullptr)
11,073✔
1192
        pfnProgress = GDALDummyProgress;
8,601✔
1193

1194
    const int nBandCount = poSrcDS->GetRasterCount();
11,073✔
1195

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

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

1247
        const char *dstInterleaveBand =
1248
            (CSLFindString(interleavesCSL, "BAND") >= 0)  ? "BAND"
11,399✔
1249
            : (CSLFindString(interleavesCSL, "BSQ") >= 0) ? "BSQ"
5,145✔
1250
                                                          : nullptr;
6,254✔
1251
        const char *dstInterleaveLine =
1252
            (CSLFindString(interleavesCSL, "LINE") >= 0)  ? "LINE"
12,508✔
1253
            : (CSLFindString(interleavesCSL, "BIL") >= 0) ? "BIL"
6,254✔
1254
                                                          : nullptr;
6,254✔
1255
        const char *dstInterleavePixel =
1256
            (CSLFindString(interleavesCSL, "PIXEL") >= 0) ? "PIXEL"
11,399✔
1257
            : (CSLFindString(interleavesCSL, "BIP") >= 0) ? "BIP"
5,145✔
1258
                                                          : nullptr;
6,254✔
1259
        const char *dstInterleave =
6,254✔
1260
            EQUAL(srcInterleave, "BAND")    ? dstInterleaveBand
6,536✔
1261
            : EQUAL(srcInterleave, "LINE")  ? dstInterleaveLine
562✔
1262
            : EQUAL(srcInterleave, "PIXEL") ? dstInterleavePixel
280✔
1263
                                            : nullptr;
1264
        CSLDestroy(interleavesCSL);
6,254✔
1265

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

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

1293
    int iIdxQuietDeleteOnCreateCopy =
1294
        CSLPartialFindString(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY=");
11,073✔
1295
    if (iIdxQuietDeleteOnCreateCopy >= 0)
11,073✔
1296
    {
1297
        if (papszOptionsToDelete == nullptr)
2,309✔
1298
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1,313✔
1299
        papszOptionsToDelete = CSLRemoveStrings(
2,309✔
1300
            papszOptionsToDelete, iIdxQuietDeleteOnCreateCopy, 1, nullptr);
1301
        papszOptions = papszOptionsToDelete;
2,309✔
1302
    }
1303

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

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

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

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

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

1380
            if (poDstDS->poDriver == nullptr)
8,938✔
1381
                poDstDS->poDriver = this;
7,932✔
1382

1383
            if (!bInternalDataset)
8,938✔
1384
                poDstDS->AddToDatasetOpenList();
4,775✔
1385
        }
1386
    }
1387
    else
1388
    {
1389
        poDstDS = DefaultCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1,051✔
1390
                                    pfnProgress, pProgressData);
1391
    }
1392

1393
    CSLDestroy(papszOptionsToDelete);
11,073✔
1394
    return poDstDS;
11,073✔
1395
}
1396

1397
/************************************************************************/
1398
/*                           GDALCreateCopy()                           */
1399
/************************************************************************/
1400

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

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

1414
{
1415
    VALIDATE_POINTER1(hDriver, "GDALCreateCopy", nullptr);
6,205✔
1416
    VALIDATE_POINTER1(hSrcDS, "GDALCreateCopy", nullptr);
6,205✔
1417

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

1423
/************************************************************************/
1424
/*                      CanVectorTranslateFrom()                        */
1425
/************************************************************************/
1426

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

1447
{
1448
    if (ppapszFailureReasons)
784✔
1449
    {
1450
        *ppapszFailureReasons = nullptr;
×
1451
    }
1452

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

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

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

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

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

1502
/************************************************************************/
1503
/*                         VectorTranslateFrom()                        */
1504
/************************************************************************/
1505

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

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

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

1541
/************************************************************************/
1542
/*                            QuietDelete()                             */
1543
/************************************************************************/
1544

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

1565
CPLErr GDALDriver::QuietDelete(const char *pszName,
23,649✔
1566
                               CSLConstList papszAllowedDrivers)
1567

1568
{
1569
    VSIStatBufL sStat;
1570
    const bool bExists =
1571
        VSIStatExL(pszName, &sStat,
23,649✔
1572
                   VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0;
23,650✔
1573

1574
#ifdef S_ISFIFO
1575
    if (bExists && S_ISFIFO(sStat.st_mode))
23,650✔
1576
        return CE_None;
×
1577
#endif
1578

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

1609
    if (poDriver == nullptr)
23,650✔
1610
        return CE_None;
22,691✔
1611

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

1622
    CPLDebug("GDAL", "QuietDelete(%s) invoking Delete()", pszName);
917✔
1623

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

1638
/************************************************************************/
1639
/*                               Delete()                               */
1640
/************************************************************************/
1641

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

1661
CPLErr GDALDriver::Delete(const char *pszFilename)
4,532✔
1662

1663
{
1664
    pfnDelete = GetDeleteCallback();
4,532✔
1665
    if (pfnDelete != nullptr)
4,532✔
1666
        return pfnDelete(pszFilename);
1,081✔
1667
    else if (pfnDeleteDataSource != nullptr)
3,451✔
1668
        return pfnDeleteDataSource(this, pszFilename);
×
1669

1670
    /* -------------------------------------------------------------------- */
1671
    /*      Collect file list.                                              */
1672
    /* -------------------------------------------------------------------- */
1673
    GDALDatasetH hDS = GDALOpenEx(pszFilename, 0, nullptr, nullptr, nullptr);
3,451✔
1674

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

1681
        return CE_Failure;
237✔
1682
    }
1683

1684
    char **papszFileList = GDALGetFileList(hDS);
3,214✔
1685

1686
    GDALClose(hDS);
3,214✔
1687
    hDS = nullptr;
3,214✔
1688

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

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

1713
    CSLDestroy(papszFileList);
3,214✔
1714

1715
    return eErr;
3,214✔
1716
}
1717

1718
/************************************************************************/
1719
/*                         GDALDeleteDataset()                          */
1720
/************************************************************************/
1721

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

1728
CPLErr CPL_STDCALL GDALDeleteDataset(GDALDriverH hDriver,
2,492✔
1729
                                     const char *pszFilename)
1730

1731
{
1732
    if (hDriver == nullptr)
2,492✔
1733
        hDriver = GDALIdentifyDriver(pszFilename, nullptr);
10✔
1734

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

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

1749
    return GDALDriver::FromHandle(hDriver)->Delete(pszFilename);
2,491✔
1750
}
1751

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

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

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

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

1774
        return CE_Failure;
×
1775
    }
1776

1777
    char **papszFileList = GDALGetFileList(hDS);
173✔
1778

1779
    GDALClose(hDS);
173✔
1780

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

1788
        return CE_Failure;
×
1789
    }
1790

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

1799
    if (papszNewFileList == nullptr)
173✔
1800
        return CE_Failure;
×
1801

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

1818
    CSLDestroy(papszNewFileList);
173✔
1819
    CSLDestroy(papszFileList);
173✔
1820

1821
    return eErr;
173✔
1822
}
1823

1824
//! @endcond
1825

1826
/************************************************************************/
1827
/*                               Rename()                               */
1828
/************************************************************************/
1829

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

1847
CPLErr GDALDriver::Rename(const char *pszNewName, const char *pszOldName)
175✔
1848

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

1854
    return DefaultRename(pszNewName, pszOldName);
172✔
1855
}
1856

1857
/************************************************************************/
1858
/*                         GDALRenameDataset()                          */
1859
/************************************************************************/
1860

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

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

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

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

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

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

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

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

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

1908
        return CE_Failure;
×
1909
    }
1910

1911
    char **papszFileList = GDALGetFileList(hDS);
10✔
1912

1913
    GDALClose(hDS);
10✔
1914
    hDS = nullptr;
10✔
1915

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

1923
        return CE_Failure;
×
1924
    }
1925

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

1934
    if (papszNewFileList == nullptr)
10✔
1935
        return CE_Failure;
×
1936

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

1949
    CSLDestroy(papszNewFileList);
10✔
1950
    CSLDestroy(papszFileList);
10✔
1951

1952
    return eErr;
10✔
1953
}
1954

1955
//! @endcond
1956

1957
/************************************************************************/
1958
/*                             CopyFiles()                              */
1959
/************************************************************************/
1960

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

1974
CPLErr GDALDriver::CopyFiles(const char *pszNewName, const char *pszOldName)
12✔
1975

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

1981
    return DefaultCopyFiles(pszNewName, pszOldName);
9✔
1982
}
1983

1984
/************************************************************************/
1985
/*                        GDALCopyDatasetFiles()                        */
1986
/************************************************************************/
1987

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

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

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

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

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

2012
/************************************************************************/
2013
/*                       GDALDriverHasOpenOption()                      */
2014
/************************************************************************/
2015

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

2029
/************************************************************************/
2030
/*                       GDALGetDriverShortName()                       */
2031
/************************************************************************/
2032

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

2046
const char *CPL_STDCALL GDALGetDriverShortName(GDALDriverH hDriver)
3,541,720✔
2047

2048
{
2049
    VALIDATE_POINTER1(hDriver, "GDALGetDriverShortName", nullptr);
3,541,720✔
2050

2051
    return GDALDriver::FromHandle(hDriver)->GetDescription();
3,541,720✔
2052
}
2053

2054
/************************************************************************/
2055
/*                       GDALGetDriverLongName()                        */
2056
/************************************************************************/
2057

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

2068
const char *CPL_STDCALL GDALGetDriverLongName(GDALDriverH hDriver)
483✔
2069

2070
{
2071
    VALIDATE_POINTER1(hDriver, "GDALGetDriverLongName", nullptr);
483✔
2072

2073
    const char *pszLongName =
2074
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_LONGNAME);
483✔
2075

2076
    if (pszLongName == nullptr)
483✔
2077
        return "";
×
2078

2079
    return pszLongName;
483✔
2080
}
2081

2082
/************************************************************************/
2083
/*                       GDALGetDriverHelpTopic()                       */
2084
/************************************************************************/
2085

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

2098
const char *CPL_STDCALL GDALGetDriverHelpTopic(GDALDriverH hDriver)
×
2099

2100
{
2101
    VALIDATE_POINTER1(hDriver, "GDALGetDriverHelpTopic", nullptr);
×
2102

2103
    return GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_HELPTOPIC);
×
2104
}
2105

2106
/************************************************************************/
2107
/*                   GDALGetDriverCreationOptionList()                  */
2108
/************************************************************************/
2109

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

2122
const char *CPL_STDCALL GDALGetDriverCreationOptionList(GDALDriverH hDriver)
×
2123

2124
{
2125
    VALIDATE_POINTER1(hDriver, "GDALGetDriverCreationOptionList", nullptr);
×
2126

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

2131
    if (pszOptionList == nullptr)
×
2132
        return "";
×
2133

2134
    return pszOptionList;
×
2135
}
2136

2137
/************************************************************************/
2138
/*                   GDALValidateCreationOptions()                      */
2139
/************************************************************************/
2140

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

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

2212
    const bool bRet = CPL_TO_BOOL(GDALValidateOptions(
33,310✔
2213
        pszOptionList, papszOptionsToValidate, "creation option", osDriver));
2214
    CSLDestroy(papszOptionsToFree);
33,310✔
2215
    return bRet;
33,310✔
2216
}
2217

2218
/************************************************************************/
2219
/*                     GDALValidateOpenOptions()                        */
2220
/************************************************************************/
2221

2222
int GDALValidateOpenOptions(GDALDriverH hDriver,
56,040✔
2223
                            const char *const *papszOpenOptions)
2224
{
2225
    VALIDATE_POINTER1(hDriver, "GDALValidateOpenOptions", FALSE);
56,040✔
2226
    const char *pszOptionList =
2227
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
56,040✔
2228
            GDAL_DMD_OPENOPTIONLIST);
56,039✔
2229
    CPLString osDriver;
112,077✔
2230
    osDriver.Printf("driver %s",
2231
                    GDALDriver::FromHandle(hDriver)->GetDescription());
56,040✔
2232
    return GDALValidateOptions(pszOptionList, papszOpenOptions, "open option",
56,039✔
2233
                               osDriver);
56,037✔
2234
}
2235

2236
/************************************************************************/
2237
/*                           GDALValidateOptions()                      */
2238
/************************************************************************/
2239

2240
int GDALValidateOptions(const char *pszOptionList,
101,833✔
2241
                        const char *const *papszOptionsToValidate,
2242
                        const char *pszErrorMessageOptionType,
2243
                        const char *pszErrorMessageContainerName)
2244
{
2245
    if (papszOptionsToValidate == nullptr || *papszOptionsToValidate == nullptr)
101,833✔
2246
        return TRUE;
85,981✔
2247
    if (pszOptionList == nullptr)
15,852✔
2248
        return TRUE;
155✔
2249

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

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

2272
            ++papszOptionsToValidate;
1✔
2273
            continue;
1,692✔
2274
        }
2275

2276
        if (EQUAL(pszKey, "VALIDATE_OPEN_OPTIONS"))
26,784✔
2277
        {
2278
            ++papszOptionsToValidate;
×
2279
            CPLFree(pszKey);
×
2280
            continue;
×
2281
        }
2282

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

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

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

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

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

2367
            CPLFree(pszKey);
1,691✔
2368
            ++papszOptionsToValidate;
1,691✔
2369
            continue;
1,691✔
2370
        }
2371

2372
#ifdef DEBUG
2373
        CPLXMLNode *psChildSubNode = psChildNode->psChild;
25,093✔
2374
        while (psChildSubNode)
149,370✔
2375
        {
2376
            if (psChildSubNode->eType == CXT_Attribute)
124,277✔
2377
            {
2378
                if (!(EQUAL(psChildSubNode->pszValue, "name") ||
87,357✔
2379
                      EQUAL(psChildSubNode->pszValue, "alias") ||
62,264✔
2380
                      EQUAL(psChildSubNode->pszValue, "deprecated_alias") ||
62,142✔
2381
                      EQUAL(psChildSubNode->pszValue, "alt_config_option") ||
62,061✔
2382
                      EQUAL(psChildSubNode->pszValue, "description") ||
62,028✔
2383
                      EQUAL(psChildSubNode->pszValue, "type") ||
40,892✔
2384
                      EQUAL(psChildSubNode->pszValue, "min") ||
15,799✔
2385
                      EQUAL(psChildSubNode->pszValue, "max") ||
15,501✔
2386
                      EQUAL(psChildSubNode->pszValue, "default") ||
15,133✔
2387
                      EQUAL(psChildSubNode->pszValue, "maxsize") ||
1,010✔
2388
                      EQUAL(psChildSubNode->pszValue, "required") ||
985✔
2389
                      EQUAL(psChildSubNode->pszValue, "scope")))
930✔
2390
                {
2391
                    /* Driver error */
2392
                    CPLError(CE_Warning, CPLE_NotSupported,
×
2393
                             "%s : unhandled attribute '%s' for %s %s.",
2394
                             pszErrorMessageContainerName,
2395
                             psChildSubNode->pszValue, pszKey,
2396
                             pszErrorMessageOptionType);
2397
                }
2398
            }
2399
            psChildSubNode = psChildSubNode->psNext;
124,277✔
2400
        }
2401
#endif
2402

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

2615
    CPLDestroyXMLNode(psNode);
15,697✔
2616
    return bRet ? TRUE : FALSE;
15,697✔
2617
}
2618

2619
/************************************************************************/
2620
/*                         GDALIdentifyDriver()                         */
2621
/************************************************************************/
2622

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

2651
GDALDriverH CPL_STDCALL GDALIdentifyDriver(const char *pszFilename,
24,762✔
2652
                                           CSLConstList papszFileList)
2653

2654
{
2655
    return GDALIdentifyDriverEx(pszFilename, 0, nullptr, papszFileList);
24,762✔
2656
}
2657

2658
/************************************************************************/
2659
/*                         GDALIdentifyDriverEx()                       */
2660
/************************************************************************/
2661

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

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

2707
    // If no driver kind is specified, assume all are to be probed.
2708
    if ((nIdentifyFlags & GDAL_OF_KIND_MASK) == 0)
24,852✔
2709
        nIdentifyFlags |= GDAL_OF_KIND_MASK & ~GDAL_OF_MULTIDIM_RASTER;
24,809✔
2710

2711
    GDALOpenInfo oOpenInfo(pszFilename, nIdentifyFlags, papszFileList);
49,704✔
2712
    oOpenInfo.papszAllowedDrivers = papszAllowedDrivers;
24,852✔
2713

2714
    CPLErrorStateBackuper oBackuper;
49,704✔
2715
    CPLErrorSetState(CE_None, CPLE_AppDefined, "");
24,852✔
2716

2717
    const int nDriverCount = poDM->GetDriverCount();
24,852✔
2718

2719
    // First pass: only use drivers that have a pfnIdentify implementation.
2720
    std::vector<GDALDriver *> apoSecondPassDrivers;
49,703✔
2721
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,315,500✔
2722
    {
2723
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,291,940✔
2724
        if (papszAllowedDrivers != nullptr &&
5,299,190✔
2725
            CSLFindString(papszAllowedDrivers,
7,226✔
2726
                          GDALGetDriverShortName(poDriver)) == -1)
2727
        {
2728
            continue;
989,465✔
2729
        }
2730

2731
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,286,070✔
2732

2733
        if (poDriver->pfnIdentify == nullptr &&
5,284,780✔
2734
            poDriver->pfnIdentifyEx == nullptr)
978,529✔
2735
        {
2736
            continue;
978,528✔
2737
        }
2738

2739
        if (papszAllowedDrivers != nullptr &&
4,306,300✔
2740
            CSLFindString(papszAllowedDrivers,
44✔
2741
                          GDALGetDriverShortName(poDriver)) == -1)
2742
            continue;
×
2743
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
12,914,100✔
2744
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
4,306,750✔
2745
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
502✔
2746
            continue;
141✔
2747
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
12,921,600✔
2748
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
4,310,940✔
2749
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
4,833✔
2750
            continue;
3,614✔
2751

2752
        if (poDriver->pfnIdentifyEx)
4,302,500✔
2753
        {
2754
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) > 0)
×
2755
                return poDriver;
×
2756
        }
2757
        else
2758
        {
2759
            const int nIdentifyRes = poDriver->pfnIdentify(&oOpenInfo);
4,302,500✔
2760
            if (nIdentifyRes > 0)
4,302,470✔
2761
                return poDriver;
1,294✔
2762
            if (nIdentifyRes < 0 &&
4,325,640✔
2763
                poDriver->GetMetadataItem("IS_NON_LOADED_PLUGIN"))
24,462✔
2764
            {
2765
                // Not loaded plugin
2766
                apoSecondPassDrivers.push_back(poDriver);
36✔
2767
            }
2768
        }
2769
    }
2770

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

2780
    // third pass: slow method.
2781
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,215,890✔
2782
    {
2783
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,192,420✔
2784
        if (papszAllowedDrivers != nullptr &&
5,196,180✔
2785
            CSLFindString(papszAllowedDrivers,
3,756✔
2786
                          GDALGetDriverShortName(poDriver)) == -1)
2787
        {
2788
            continue;
3,739✔
2789
        }
2790

2791
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,188,680✔
2792

2793
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
15,564,600✔
2794
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
5,189,120✔
2795
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
440✔
2796
            continue;
137✔
2797
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
15,566,300✔
2798
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
5,190,090✔
2799
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
1,542✔
2800
            continue;
925✔
2801

2802
        if (poDriver->pfnIdentifyEx != nullptr)
5,187,620✔
2803
        {
2804
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) == 0)
×
2805
                continue;
×
2806
        }
2807
        else if (poDriver->pfnIdentify != nullptr)
5,187,620✔
2808
        {
2809
            if (poDriver->pfnIdentify(&oOpenInfo) == 0)
4,226,570✔
2810
                continue;
4,202,580✔
2811
        }
2812

2813
        GDALDataset *poDS;
2814
        if (poDriver->pfnOpen != nullptr)
985,041✔
2815
        {
2816
            poDS = poDriver->pfnOpen(&oOpenInfo);
938,044✔
2817
            if (poDS != nullptr)
938,045✔
2818
            {
2819
                delete poDS;
91✔
2820
                return GDALDriver::ToHandle(poDriver);
91✔
2821
            }
2822

2823
            if (CPLGetLastErrorType() != CE_None)
937,954✔
2824
                return nullptr;
×
2825
        }
2826
        else if (poDriver->pfnOpenWithDriverArg != nullptr)
46,997✔
2827
        {
2828
            poDS = poDriver->pfnOpenWithDriverArg(poDriver, &oOpenInfo);
×
2829
            if (poDS != nullptr)
×
2830
            {
2831
                delete poDS;
×
2832
                return GDALDriver::ToHandle(poDriver);
×
2833
            }
2834

2835
            if (CPLGetLastErrorType() != CE_None)
×
UNCOV
2836
                return nullptr;
×
2837
        }
2838
    }
2839

2840
    return nullptr;
23,466✔
2841
}
2842

2843
/************************************************************************/
2844
/*                          SetMetadataItem()                           */
2845
/************************************************************************/
2846

2847
CPLErr GDALDriver::SetMetadataItem(const char *pszName, const char *pszValue,
3,959,250✔
2848
                                   const char *pszDomain)
2849

2850
{
2851
    if (pszDomain == nullptr || pszDomain[0] == '\0')
3,959,250✔
2852
    {
2853
        /* Automatically sets GDAL_DMD_EXTENSIONS from GDAL_DMD_EXTENSION */
2854
        if (EQUAL(pszName, GDAL_DMD_EXTENSION) &&
4,130,680✔
2855
            GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSIONS) == nullptr)
176,339✔
2856
        {
2857
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSIONS, pszValue);
176,339✔
2858
        }
2859
        /* and vice-versa if there is a single extension in GDAL_DMD_EXTENSIONS */
2860
        else if (EQUAL(pszName, GDAL_DMD_EXTENSIONS) &&
7,626,140✔
2861
                 strchr(pszValue, ' ') == nullptr &&
3,784,530✔
2862
                 GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSION) ==
6,528✔
2863
                     nullptr)
2864
        {
2865
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSION, pszValue);
6,528✔
2866
        }
2867
    }
2868
    return GDALMajorObject::SetMetadataItem(pszName, pszValue, pszDomain);
3,959,250✔
2869
}
2870

2871
/************************************************************************/
2872
/*                         InstantiateAlgorithm()                       */
2873
/************************************************************************/
2874

2875
//! @cond Doxygen_Suppress
2876

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

2886
/************************************************************************/
2887
/*                        DeclareAlgorithm()                            */
2888
/************************************************************************/
2889

2890
void GDALDriver::DeclareAlgorithm(const std::vector<std::string> &aosPath)
6,534✔
2891
{
2892
    const std::string osDriverName = GetDescription();
13,068✔
2893
    auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
6,534✔
2894

2895
    if (!singleton.HasDeclaredSubAlgorithm({"driver"}))
13,068✔
2896
    {
2897
        singleton.DeclareAlgorithm(
2,858✔
2898
            {"driver"},
2899
            []() -> std::unique_ptr<GDALAlgorithm>
40✔
2900
            {
2901
                return std::make_unique<GDALContainerAlgorithm>(
80✔
2902
                    "driver", "Command for driver specific operations.");
40✔
2903
            });
1,429✔
2904
    }
2905

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

2931
    path.insert(path.end(), aosPath.begin(), aosPath.end());
6,534✔
2932

2933
    auto lambda = [osDriverName, aosPath]() -> std::unique_ptr<GDALAlgorithm>
56✔
2934
    {
2935
        auto poDriver =
2936
            GetGDALDriverManager()->GetDriverByName(osDriverName.c_str());
56✔
2937
        if (poDriver)
56✔
2938
            return std::unique_ptr<GDALAlgorithm>(
2939
                poDriver->InstantiateAlgorithm(aosPath));
56✔
2940
        return nullptr;
×
2941
    };
13,068✔
2942

2943
    singleton.DeclareAlgorithm(path, std::move(lambda));
6,534✔
2944

2945
    CPL_IGNORE_RET_VAL(osDriverName);
6,534✔
2946
}
6,534✔
2947

2948
//! @endcond
2949

2950
/************************************************************************/
2951
/*                   DoesDriverHandleExtension()                        */
2952
/************************************************************************/
2953

2954
static bool DoesDriverHandleExtension(GDALDriverH hDriver, const char *pszExt)
166,793✔
2955
{
2956
    bool bRet = false;
166,793✔
2957
    const char *pszDriverExtensions =
2958
        GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSIONS, nullptr);
166,793✔
2959
    if (pszDriverExtensions)
166,793✔
2960
    {
2961
        const CPLStringList aosTokens(CSLTokenizeString(pszDriverExtensions));
276,006✔
2962
        const int nTokens = aosTokens.size();
138,003✔
2963
        for (int j = 0; j < nTokens; ++j)
309,616✔
2964
        {
2965
            if (EQUAL(pszExt, aosTokens[j]))
175,935✔
2966
            {
2967
                bRet = true;
4,322✔
2968
                break;
4,322✔
2969
            }
2970
        }
2971
    }
2972
    return bRet;
166,793✔
2973
}
2974

2975
/************************************************************************/
2976
/*                     IsOnlyExpectedGDBDrivers()                       */
2977
/************************************************************************/
2978

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

2992
/************************************************************************/
2993
/*                  GDALGetOutputDriversForDatasetName()                */
2994
/************************************************************************/
2995

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

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

3041
    auto poDM = GetGDALDriverManager();
2,506✔
3042
    const int nDriverCount = poDM->GetDriverCount(true);
2,506✔
3043
    GDALDriver *poMissingPluginDriver = nullptr;
2,506✔
3044
    std::string osMatchingPrefix;
5,012✔
3045
    for (int i = 0; i < nDriverCount; i++)
555,942✔
3046
    {
3047
        GDALDriver *poDriver = poDM->GetDriver(i, true);
553,436✔
3048
        bool bOk = false;
553,436✔
3049
        if ((poDriver->GetMetadataItem(GDAL_DCAP_CREATE) != nullptr ||
553,436✔
3050
             poDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY) != nullptr) &&
834,105✔
3051
            (((nFlagRasterVector & GDAL_OF_RASTER) &&
280,669✔
3052
              poDriver->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr) ||
230,495✔
3053
             ((nFlagRasterVector & GDAL_OF_VECTOR) &&
136,610✔
3054
              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)))
50,174✔
3055
        {
3056
            bOk = true;
169,145✔
3057
        }
3058
        else if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR_TRANSLATE_FROM) &&
386,349✔
3059
                 (nFlagRasterVector & GDAL_OF_VECTOR) != 0)
2,058✔
3060
        {
3061
            bOk = true;
×
3062
        }
3063
        if (bOk)
553,436✔
3064
        {
3065
            if (!osExt.empty() &&
335,938✔
3066
                DoesDriverHandleExtension(GDALDriver::ToHandle(poDriver),
166,793✔
3067
                                          osExt.c_str()))
3068
            {
3069
                if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
4,322✔
3070
                {
3071
                    poMissingPluginDriver = poDriver;
×
3072
                    aosMissingDriverNames.AddString(poDriver->GetDescription());
×
3073
                }
3074
                else
3075
                    aosDriverNames.AddString(poDriver->GetDescription());
4,322✔
3076
            }
3077
            else
3078
            {
3079
                const char *pszPrefix =
3080
                    poDriver->GetMetadataItem(GDAL_DMD_CONNECTION_PREFIX);
164,823✔
3081
                if (pszPrefix && STARTS_WITH_CI(pszDestDataset, pszPrefix))
164,823✔
3082
                {
3083
                    if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
9✔
3084
                    {
3085
                        osMatchingPrefix = pszPrefix;
×
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,508✔
3100
        EQUAL(aosDriverNames[0], "GMT") && EQUAL(aosDriverNames[1], "netCDF"))
2,508✔
3101
    {
3102
        aosDriverNames.Clear();
×
3103
        aosDriverNames.AddString("netCDF");
×
3104
        aosDriverNames.AddString("GMT");
×
3105
    }
3106

3107
    if (bSingleMatch)
2,506✔
3108
    {
3109
        if (nFlagRasterVector == GDAL_OF_RASTER)
2,438✔
3110
        {
3111
            if (aosDriverNames.empty())
2,058✔
3112
            {
3113
                if (osExt.empty())
11✔
3114
                {
3115
                    aosDriverNames.AddString("GTiff");
8✔
3116
                }
3117
            }
3118
            else if (aosDriverNames.size() >= 2)
2,047✔
3119
            {
3120
                if (bEmitWarning && !(EQUAL(aosDriverNames[0], "GTiff") &&
3,720✔
3121
                                      EQUAL(aosDriverNames[1], "COG")))
1,860✔
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,720✔
3128
                aosDriverNames.Clear();
1,860✔
3129
                aosDriverNames.AddString(osDrvName.c_str());
1,860✔
3130
            }
3131
        }
3132
        else if (EQUAL(osExt.c_str(), "gdb") &&
381✔
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)
379✔
3142
        {
3143
            if (bEmitWarning)
×
3144
            {
3145
                CPLError(CE_Warning, CPLE_AppDefined,
×
3146
                         "Several drivers matching %s %s. Using %s",
3147
                         osMatchingPrefix.empty() ? osExt.c_str()
×
3148
                                                  : osMatchingPrefix.c_str(),
×
3149
                         osMatchingPrefix.empty() ? "extension" : "prefix",
×
3150
                         aosDriverNames[0]);
3151
            }
3152
            const std::string osDrvName = aosDriverNames[0];
×
3153
            aosDriverNames.Clear();
×
3154
            aosDriverNames.AddString(osDrvName.c_str());
×
3155
        }
3156
    }
3157

3158
    if (aosDriverNames.empty() && bEmitWarning &&
2,535✔
3159
        aosMissingDriverNames.size() == 1 && poMissingPluginDriver)
2,535✔
3160
    {
3161
        CPLError(CE_Failure, CPLE_AppDefined,
×
3162
                 "No installed driver matching %s %s, but %s driver is "
3163
                 "known. However plugin %s",
3164
                 osMatchingPrefix.empty() ? osExt.c_str()
×
3165
                                          : osMatchingPrefix.c_str(),
×
3166
                 osMatchingPrefix.empty() ? "extension" : "prefix",
×
3167
                 poMissingPluginDriver->GetDescription(),
×
3168
                 GDALGetMessageAboutMissingPluginDriver(poMissingPluginDriver)
×
3169
                     .c_str());
3170
    }
3171

3172
    return aosDriverNames.StealList();
2,506✔
3173
}
3174

3175
/************************************************************************/
3176
/*                GDALGetMessageAboutMissingPluginDriver()              */
3177
/************************************************************************/
3178

3179
std::string
3180
GDALGetMessageAboutMissingPluginDriver(GDALDriver *poMissingPluginDriver)
×
3181
{
3182
    std::string osMsg =
3183
        poMissingPluginDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME");
×
3184
    osMsg += " is not available in your "
3185
             "installation.";
×
3186
    if (const char *pszInstallationMsg = poMissingPluginDriver->GetMetadataItem(
×
3187
            GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE))
×
3188
    {
3189
        osMsg += " ";
×
3190
        osMsg += pszInstallationMsg;
×
3191
    }
3192

3193
    VSIStatBuf sStat;
3194
    if (const char *pszGDALDriverPath =
×
3195
            CPLGetConfigOption("GDAL_DRIVER_PATH", nullptr))
×
3196
    {
3197
        if (VSIStat(pszGDALDriverPath, &sStat) != 0)
×
3198
        {
3199
            if (osMsg.back() != '.')
×
3200
                osMsg += ".";
×
3201
            osMsg += " Directory '";
×
3202
            osMsg += pszGDALDriverPath;
×
3203
            osMsg += "' pointed by GDAL_DRIVER_PATH does not exist.";
×
3204
        }
3205
    }
3206
    else
3207
    {
3208
        if (osMsg.back() != '.')
×
3209
            osMsg += ".";
×
3210
#ifdef INSTALL_PLUGIN_FULL_DIR
3211
        if (VSIStat(INSTALL_PLUGIN_FULL_DIR, &sStat) != 0)
3212
        {
3213
            osMsg += " Directory '";
3214
            osMsg += INSTALL_PLUGIN_FULL_DIR;
3215
            osMsg += "' hardcoded in the GDAL library does not "
3216
                     "exist and the GDAL_DRIVER_PATH "
3217
                     "configuration option is not set.";
3218
        }
3219
        else
3220
#endif
3221
        {
3222
            osMsg += " The GDAL_DRIVER_PATH configuration "
3223
                     "option is not set.";
×
3224
        }
3225
    }
3226
    return osMsg;
×
3227
}
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