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

OSGeo / gdal / 15797738239

21 Jun 2025 04:51PM UTC coverage: 71.076% (+0.001%) from 71.075%
15797738239

Pull #12622

github

web-flow
Merge 19559fd15 into 645a00347
Pull Request #12622: VSI Win32: implement OpenDir()

574181 of 807840 relevant lines covered (71.08%)

249831.39 hits per line

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

86.14
/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,343✔
97
{
98

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

110
    if (poDS)
435,148✔
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,040✔
115
                         ~(GDAL_OF_FROM_GDALOPEN | GDAL_OF_THREAD_SAFE);
116
        if (poDS->nOpenFlags & GDAL_OF_THREAD_SAFE)
55,040✔
117
            nOpenFlags |= GDAL_OF_THREAD_SAFE;
905✔
118
        poDS->nOpenFlags = nOpenFlags;
55,040✔
119

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

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

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

131
        if (!(poOpenInfo->nOpenFlags & GDAL_OF_INTERNAL))
55,004✔
132
        {
133
            if (CPLGetPID() != GDALGetResponsiblePIDForCurrentThread())
45,618✔
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,610✔
143
                         poOpenInfo->pszFilename, poDS, GetDescription());
45,614✔
144

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

149
    return poDS;
435,441✔
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,030✔
192
                                int nBands, GDALDataType eType,
193
                                CSLConstList papszOptions)
194

195
{
196
    /* -------------------------------------------------------------------- */
197
    /*      Does this format support creation.                              */
198
    /* -------------------------------------------------------------------- */
199
    pfnCreate = GetCreateCallback();
22,030✔
200
    if (CPL_UNLIKELY(pfnCreate == nullptr && pfnCreateEx == nullptr &&
22,030✔
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,029✔
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,028✔
222
                     GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
223
                     (nXSize < 1 || nYSize < 1)))
22,028✔
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,027✔
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,026✔
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,007✔
253
            !EQUAL(GetDescription(), "Memory") &&
34,699✔
254
            // ogr2ogr -f PostgreSQL might reach the Delete method of the
255
            // PostgisRaster driver which is undesirable
256
            !EQUAL(GetDescription(), "PostgreSQL"))
12,692✔
257
        {
258
            QuietDelete(pszFilename);
12,690✔
259
        }
260
    }
261

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

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

276
    GDALDataset *poDS = nullptr;
22,026✔
277
    if (pfnCreateEx != nullptr)
22,026✔
278
    {
279
        poDS = pfnCreateEx(this, pszFilename, nXSize, nYSize, nBands, eType,
×
280
                           const_cast<char **>(papszOptions));
281
    }
282
    else if (pfnCreate != nullptr)
22,026✔
283
    {
284
        poDS = pfnCreate(pszFilename, nXSize, nYSize, nBands, eType,
22,026✔
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,026✔
294
    {
295
        if (poDS->GetDescription() == nullptr ||
41,538✔
296
            strlen(poDS->GetDescription()) == 0)
20,769✔
297
            poDS->SetDescription(pszFilename);
18,178✔
298

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

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

305
    return poDS;
22,026✔
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,303✔
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,303✔
326

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

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

338
    return hDS;
18,303✔
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
    double adfGeoTransform[6] = {};
990✔
732

733
    if (nDstBands == 0 && !bStrict)
990✔
734
        CPLTurnFailureIntoWarning(true);
4✔
735

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

748
    if (eErr == CE_None)
990✔
749
    {
750
        const auto poSrcSRS = poSrcDS->GetSpatialRef();
981✔
751
        if (poSrcSRS && !poSrcSRS->IsEmpty())
981✔
752
        {
753
            eErr = poDstDS->SetSpatialRef(poSrcSRS);
813✔
754
            if (!bStrict)
813✔
755
                eErr = CE_None;
500✔
756
        }
757
    }
758

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

770
    if (nDstBands == 0 && !bStrict)
990✔
771
        CPLTurnFailureIntoWarning(false);
4✔
772

773
    /* -------------------------------------------------------------------- */
774
    /*      Copy metadata.                                                  */
775
    /* -------------------------------------------------------------------- */
776
    DefaultCopyMetadata(poSrcDS, poDstDS, papszOptions, nullptr);
990✔
777

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

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

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

805
        if (strlen(poSrcBand->GetDescription()) > 0)
1,538✔
806
            poDstBand->SetDescription(poSrcBand->GetDescription());
52✔
807

808
        if (CSLCount(poSrcBand->GetMetadata()) > 0)
1,538✔
809
            poDstBand->SetMetadata(poSrcBand->GetMetadata());
112✔
810

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

816
        dfValue = poSrcBand->GetScale(&bSuccess);
1,538✔
817
        if (bSuccess && dfValue != 1.0)
1,538✔
818
            poDstBand->SetScale(dfValue);
4✔
819

820
        GDALCopyNoDataValue(poDstBand, poSrcBand);
1,538✔
821

822
        if (poSrcBand->GetColorInterpretation() != GCI_Undefined &&
2,527✔
823
            poSrcBand->GetColorInterpretation() !=
989✔
824
                poDstBand->GetColorInterpretation())
989✔
825
            poDstBand->SetColorInterpretation(
774✔
826
                poSrcBand->GetColorInterpretation());
774✔
827

828
        char **papszCatNames = poSrcBand->GetCategoryNames();
1,538✔
829
        if (nullptr != papszCatNames)
1,538✔
830
            poDstBand->SetCategoryNames(papszCatNames);
1✔
831

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

841
        if (!bStrict)
1,538✔
842
        {
843
            CPLTurnFailureIntoWarning(false);
926✔
844
        }
845
        else
846
        {
847
            eErr = CPLGetLastErrorType();
612✔
848
        }
849
    }
850

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

866
    /* -------------------------------------------------------------------- */
867
    /*      Should we copy some masks over?                                 */
868
    /* -------------------------------------------------------------------- */
869
    if (eErr == CE_None && nDstBands > 0)
990✔
870
        eErr = DefaultCopyMasks(poSrcDS, poDstDS, eErr);
928✔
871

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

883
                if (poLayer == nullptr)
10✔
884
                    continue;
×
885

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

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

909
    return poDstDS;
941✔
910
}
911

912
/************************************************************************/
913
/*                       DefaultCopyMetadata()                          */
914
/************************************************************************/
915

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

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

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

1004
/************************************************************************/
1005
/*                      QuietDeleteForCreateCopy()                      */
1006
/************************************************************************/
1007

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

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

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

1098
        QuietDelete(pszFilename);
10,888✔
1099
    }
1100

1101
    return CE_None;
11,438✔
1102
}
1103

1104
//! @endcond
1105

1106
/************************************************************************/
1107
/*                             CreateCopy()                             */
1108
/************************************************************************/
1109

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

1189
GDALDataset *GDALDriver::CreateCopy(const char *pszFilename,
11,072✔
1190
                                    GDALDataset *poSrcDS, int bStrict,
1191
                                    CSLConstList papszOptions,
1192
                                    GDALProgressFunc pfnProgress,
1193
                                    void *pProgressData)
1194

1195
{
1196
    if (pfnProgress == nullptr)
11,072✔
1197
        pfnProgress = GDALDummyProgress;
8,601✔
1198

1199
    const int nBandCount = poSrcDS->GetRasterCount();
11,072✔
1200

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

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

1252
        const char *dstInterleaveBand =
1253
            (CSLFindString(interleavesCSL, "BAND") >= 0)  ? "BAND"
11,398✔
1254
            : (CSLFindString(interleavesCSL, "BSQ") >= 0) ? "BSQ"
5,144✔
1255
                                                          : nullptr;
6,253✔
1256
        const char *dstInterleaveLine =
1257
            (CSLFindString(interleavesCSL, "LINE") >= 0)  ? "LINE"
12,506✔
1258
            : (CSLFindString(interleavesCSL, "BIL") >= 0) ? "BIL"
6,253✔
1259
                                                          : nullptr;
6,253✔
1260
        const char *dstInterleavePixel =
1261
            (CSLFindString(interleavesCSL, "PIXEL") >= 0) ? "PIXEL"
11,397✔
1262
            : (CSLFindString(interleavesCSL, "BIP") >= 0) ? "BIP"
5,144✔
1263
                                                          : nullptr;
6,253✔
1264
        const char *dstInterleave =
6,253✔
1265
            EQUAL(srcInterleave, "BAND")    ? dstInterleaveBand
6,535✔
1266
            : EQUAL(srcInterleave, "LINE")  ? dstInterleaveLine
562✔
1267
            : EQUAL(srcInterleave, "PIXEL") ? dstInterleavePixel
280✔
1268
                                            : nullptr;
1269
        CSLDestroy(interleavesCSL);
6,253✔
1270

1271
        if (dstInterleave != nullptr)
6,253✔
1272
        {
1273
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1,133✔
1274
            papszOptionsToDelete = CSLSetNameValue(papszOptionsToDelete,
1,133✔
1275
                                                   "INTERLEAVE", dstInterleave);
1276
            papszOptionsToDelete = CSLSetNameValue(
1,133✔
1277
                papszOptionsToDelete, "@INTERLEAVE_ADDED_AUTOMATICALLY", "YES");
1278
            papszOptions = papszOptionsToDelete;
1,133✔
1279
        }
1280
    }
1281

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

1298
    int iIdxQuietDeleteOnCreateCopy =
1299
        CSLPartialFindString(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY=");
11,073✔
1300
    if (iIdxQuietDeleteOnCreateCopy >= 0)
11,073✔
1301
    {
1302
        if (papszOptionsToDelete == nullptr)
2,309✔
1303
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1,313✔
1304
        papszOptionsToDelete = CSLRemoveStrings(
2,309✔
1305
            papszOptionsToDelete, iIdxQuietDeleteOnCreateCopy, 1, nullptr);
1306
        papszOptions = papszOptionsToDelete;
2,309✔
1307
    }
1308

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

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

1350
    /* -------------------------------------------------------------------- */
1351
    /*      Advise the source raster that we are going to read it completely */
1352
    /* -------------------------------------------------------------------- */
1353

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

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

1385
            if (poDstDS->poDriver == nullptr)
8,938✔
1386
                poDstDS->poDriver = this;
7,932✔
1387

1388
            if (!bInternalDataset)
8,938✔
1389
                poDstDS->AddToDatasetOpenList();
4,775✔
1390
        }
1391
    }
1392
    else
1393
    {
1394
        poDstDS = DefaultCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1,051✔
1395
                                    pfnProgress, pProgressData);
1396
    }
1397

1398
    CSLDestroy(papszOptionsToDelete);
11,073✔
1399
    return poDstDS;
11,073✔
1400
}
1401

1402
/************************************************************************/
1403
/*                           GDALCreateCopy()                           */
1404
/************************************************************************/
1405

1406
/**
1407
 * \brief Create a copy of a dataset.
1408
 *
1409
 * @see GDALDriver::CreateCopy()
1410
 */
1411

1412
GDALDatasetH CPL_STDCALL GDALCreateCopy(GDALDriverH hDriver,
6,205✔
1413
                                        const char *pszFilename,
1414
                                        GDALDatasetH hSrcDS, int bStrict,
1415
                                        CSLConstList papszOptions,
1416
                                        GDALProgressFunc pfnProgress,
1417
                                        void *pProgressData)
1418

1419
{
1420
    VALIDATE_POINTER1(hDriver, "GDALCreateCopy", nullptr);
6,205✔
1421
    VALIDATE_POINTER1(hSrcDS, "GDALCreateCopy", nullptr);
6,205✔
1422

1423
    return GDALDriver::FromHandle(hDriver)->CreateCopy(
6,205✔
1424
        pszFilename, GDALDataset::FromHandle(hSrcDS), bStrict, papszOptions,
1425
        pfnProgress, pProgressData);
6,205✔
1426
}
1427

1428
/************************************************************************/
1429
/*                      CanVectorTranslateFrom()                        */
1430
/************************************************************************/
1431

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

1452
{
1453
    if (ppapszFailureReasons)
784✔
1454
    {
1455
        *ppapszFailureReasons = nullptr;
×
1456
    }
1457

1458
    if (!pfnCanVectorTranslateFrom)
784✔
1459
    {
1460
        if (ppapszFailureReasons)
781✔
1461
        {
1462
            *ppapszFailureReasons = CSLAddString(
×
1463
                nullptr,
1464
                "CanVectorTranslateFrom() not implemented for this driver");
1465
        }
1466
        return false;
781✔
1467
    }
1468

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

1485
bool GDALDriver::HasOpenOption(const char *pszOpenOptionName) const
224✔
1486
{
1487
    if (pszOpenOptionName == nullptr)
224✔
1488
        return false;
×
1489

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

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

1507
/************************************************************************/
1508
/*                         VectorTranslateFrom()                        */
1509
/************************************************************************/
1510

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

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

1541
    return pfnVectorTranslateFrom(pszDestName, poSourceDS,
2✔
1542
                                  papszVectorTranslateArguments, pfnProgress,
1543
                                  pProgressData);
2✔
1544
}
1545

1546
/************************************************************************/
1547
/*                            QuietDelete()                             */
1548
/************************************************************************/
1549

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

1570
CPLErr GDALDriver::QuietDelete(const char *pszName,
23,610✔
1571
                               CSLConstList papszAllowedDrivers)
1572

1573
{
1574
    VSIStatBufL sStat;
1575
    const bool bExists =
1576
        VSIStatExL(pszName, &sStat,
23,610✔
1577
                   VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0;
23,610✔
1578

1579
#ifdef S_ISFIFO
1580
    if (bExists && S_ISFIFO(sStat.st_mode))
23,610✔
1581
        return CE_None;
×
1582
#endif
1583

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

1614
    if (poDriver == nullptr)
23,610✔
1615
        return CE_None;
22,651✔
1616

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

1627
    CPLDebug("GDAL", "QuietDelete(%s) invoking Delete()", pszName);
917✔
1628

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

1643
/************************************************************************/
1644
/*                               Delete()                               */
1645
/************************************************************************/
1646

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

1666
CPLErr GDALDriver::Delete(const char *pszFilename)
4,532✔
1667

1668
{
1669
    pfnDelete = GetDeleteCallback();
4,532✔
1670
    if (pfnDelete != nullptr)
4,532✔
1671
        return pfnDelete(pszFilename);
1,081✔
1672
    else if (pfnDeleteDataSource != nullptr)
3,451✔
1673
        return pfnDeleteDataSource(this, pszFilename);
×
1674

1675
    /* -------------------------------------------------------------------- */
1676
    /*      Collect file list.                                              */
1677
    /* -------------------------------------------------------------------- */
1678
    GDALDatasetH hDS = GDALOpenEx(pszFilename, 0, nullptr, nullptr, nullptr);
3,451✔
1679

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

1686
        return CE_Failure;
237✔
1687
    }
1688

1689
    char **papszFileList = GDALGetFileList(hDS);
3,214✔
1690

1691
    GDALClose(hDS);
3,214✔
1692
    hDS = nullptr;
3,214✔
1693

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

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

1718
    CSLDestroy(papszFileList);
3,214✔
1719

1720
    return eErr;
3,214✔
1721
}
1722

1723
/************************************************************************/
1724
/*                         GDALDeleteDataset()                          */
1725
/************************************************************************/
1726

1727
/**
1728
 * \brief Delete named dataset.
1729
 *
1730
 * @see GDALDriver::Delete()
1731
 */
1732

1733
CPLErr CPL_STDCALL GDALDeleteDataset(GDALDriverH hDriver,
2,492✔
1734
                                     const char *pszFilename)
1735

1736
{
1737
    if (hDriver == nullptr)
2,492✔
1738
        hDriver = GDALIdentifyDriver(pszFilename, nullptr);
10✔
1739

1740
    if (hDriver == nullptr)
2,492✔
1741
    {
1742
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1✔
1743
                 pszFilename);
1744
        return CE_Failure;
1✔
1745
    }
1746

1747
#ifdef OGRAPISPY_ENABLED
1748
    if (GDALGetMetadataItem(hDriver, GDAL_DCAP_VECTOR, nullptr))
2,491✔
1749
    {
1750
        OGRAPISpyDeleteDataSource(hDriver, pszFilename);
484✔
1751
    }
1752
#endif
1753

1754
    return GDALDriver::FromHandle(hDriver)->Delete(pszFilename);
2,491✔
1755
}
1756

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

1764
//! @cond Doxygen_Suppress
1765
CPLErr GDALDriver::DefaultRename(const char *pszNewName, const char *pszOldName)
173✔
1766

1767
{
1768
    /* -------------------------------------------------------------------- */
1769
    /*      Collect file list.                                              */
1770
    /* -------------------------------------------------------------------- */
1771
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
173✔
1772

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

1779
        return CE_Failure;
×
1780
    }
1781

1782
    char **papszFileList = GDALGetFileList(hDS);
173✔
1783

1784
    GDALClose(hDS);
173✔
1785

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

1793
        return CE_Failure;
×
1794
    }
1795

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

1804
    if (papszNewFileList == nullptr)
173✔
1805
        return CE_Failure;
×
1806

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

1823
    CSLDestroy(papszNewFileList);
173✔
1824
    CSLDestroy(papszFileList);
173✔
1825

1826
    return eErr;
173✔
1827
}
1828

1829
//! @endcond
1830

1831
/************************************************************************/
1832
/*                               Rename()                               */
1833
/************************************************************************/
1834

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

1852
CPLErr GDALDriver::Rename(const char *pszNewName, const char *pszOldName)
175✔
1853

1854
{
1855
    pfnRename = GetRenameCallback();
175✔
1856
    if (pfnRename != nullptr)
175✔
1857
        return pfnRename(pszNewName, pszOldName);
3✔
1858

1859
    return DefaultRename(pszNewName, pszOldName);
172✔
1860
}
1861

1862
/************************************************************************/
1863
/*                         GDALRenameDataset()                          */
1864
/************************************************************************/
1865

1866
/**
1867
 * \brief Rename a dataset.
1868
 *
1869
 * @see GDALDriver::Rename()
1870
 */
1871

1872
CPLErr CPL_STDCALL GDALRenameDataset(GDALDriverH hDriver,
176✔
1873
                                     const char *pszNewName,
1874
                                     const char *pszOldName)
1875

1876
{
1877
    if (hDriver == nullptr)
176✔
1878
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
3✔
1879

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

1887
    return GDALDriver::FromHandle(hDriver)->Rename(pszNewName, pszOldName);
175✔
1888
}
1889

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

1897
//! @cond Doxygen_Suppress
1898
CPLErr GDALDriver::DefaultCopyFiles(const char *pszNewName,
10✔
1899
                                    const char *pszOldName)
1900

1901
{
1902
    /* -------------------------------------------------------------------- */
1903
    /*      Collect file list.                                              */
1904
    /* -------------------------------------------------------------------- */
1905
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
10✔
1906

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

1913
        return CE_Failure;
×
1914
    }
1915

1916
    char **papszFileList = GDALGetFileList(hDS);
10✔
1917

1918
    GDALClose(hDS);
10✔
1919
    hDS = nullptr;
10✔
1920

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

1928
        return CE_Failure;
×
1929
    }
1930

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

1939
    if (papszNewFileList == nullptr)
10✔
1940
        return CE_Failure;
×
1941

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

1954
    CSLDestroy(papszNewFileList);
10✔
1955
    CSLDestroy(papszFileList);
10✔
1956

1957
    return eErr;
10✔
1958
}
1959

1960
//! @endcond
1961

1962
/************************************************************************/
1963
/*                             CopyFiles()                              */
1964
/************************************************************************/
1965

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

1979
CPLErr GDALDriver::CopyFiles(const char *pszNewName, const char *pszOldName)
12✔
1980

1981
{
1982
    pfnCopyFiles = GetCopyFilesCallback();
12✔
1983
    if (pfnCopyFiles != nullptr)
12✔
1984
        return pfnCopyFiles(pszNewName, pszOldName);
3✔
1985

1986
    return DefaultCopyFiles(pszNewName, pszOldName);
9✔
1987
}
1988

1989
/************************************************************************/
1990
/*                        GDALCopyDatasetFiles()                        */
1991
/************************************************************************/
1992

1993
/**
1994
 * \brief Copy the files of a dataset.
1995
 *
1996
 * @see GDALDriver::CopyFiles()
1997
 */
1998

1999
CPLErr CPL_STDCALL GDALCopyDatasetFiles(GDALDriverH hDriver,
13✔
2000
                                        const char *pszNewName,
2001
                                        const char *pszOldName)
2002

2003
{
2004
    if (hDriver == nullptr)
13✔
2005
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
8✔
2006

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

2014
    return GDALDriver::FromHandle(hDriver)->CopyFiles(pszNewName, pszOldName);
12✔
2015
}
2016

2017
/************************************************************************/
2018
/*                       GDALDriverHasOpenOption()                      */
2019
/************************************************************************/
2020

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

2034
/************************************************************************/
2035
/*                       GDALGetDriverShortName()                       */
2036
/************************************************************************/
2037

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

2051
const char *CPL_STDCALL GDALGetDriverShortName(GDALDriverH hDriver)
3,539,260✔
2052

2053
{
2054
    VALIDATE_POINTER1(hDriver, "GDALGetDriverShortName", nullptr);
3,539,260✔
2055

2056
    return GDALDriver::FromHandle(hDriver)->GetDescription();
3,539,260✔
2057
}
2058

2059
/************************************************************************/
2060
/*                       GDALGetDriverLongName()                        */
2061
/************************************************************************/
2062

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

2073
const char *CPL_STDCALL GDALGetDriverLongName(GDALDriverH hDriver)
480✔
2074

2075
{
2076
    VALIDATE_POINTER1(hDriver, "GDALGetDriverLongName", nullptr);
480✔
2077

2078
    const char *pszLongName =
2079
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_LONGNAME);
480✔
2080

2081
    if (pszLongName == nullptr)
480✔
2082
        return "";
×
2083

2084
    return pszLongName;
480✔
2085
}
2086

2087
/************************************************************************/
2088
/*                       GDALGetDriverHelpTopic()                       */
2089
/************************************************************************/
2090

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

2103
const char *CPL_STDCALL GDALGetDriverHelpTopic(GDALDriverH hDriver)
×
2104

2105
{
2106
    VALIDATE_POINTER1(hDriver, "GDALGetDriverHelpTopic", nullptr);
×
2107

2108
    return GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_HELPTOPIC);
×
2109
}
2110

2111
/************************************************************************/
2112
/*                   GDALGetDriverCreationOptionList()                  */
2113
/************************************************************************/
2114

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

2127
const char *CPL_STDCALL GDALGetDriverCreationOptionList(GDALDriverH hDriver)
×
2128

2129
{
2130
    VALIDATE_POINTER1(hDriver, "GDALGetDriverCreationOptionList", nullptr);
×
2131

2132
    const char *pszOptionList =
2133
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
×
2134
            GDAL_DMD_CREATIONOPTIONLIST);
×
2135

2136
    if (pszOptionList == nullptr)
×
2137
        return "";
×
2138

2139
    return pszOptionList;
×
2140
}
2141

2142
/************************************************************************/
2143
/*                   GDALValidateCreationOptions()                      */
2144
/************************************************************************/
2145

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

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

2217
    const bool bRet = CPL_TO_BOOL(GDALValidateOptions(
33,262✔
2218
        pszOptionList, papszOptionsToValidate, "creation option", osDriver));
2219
    CSLDestroy(papszOptionsToFree);
33,262✔
2220
    return bRet;
33,262✔
2221
}
2222

2223
/************************************************************************/
2224
/*                     GDALValidateOpenOptions()                        */
2225
/************************************************************************/
2226

2227
int GDALValidateOpenOptions(GDALDriverH hDriver,
55,919✔
2228
                            const char *const *papszOpenOptions)
2229
{
2230
    VALIDATE_POINTER1(hDriver, "GDALValidateOpenOptions", FALSE);
55,919✔
2231
    const char *pszOptionList =
2232
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
55,919✔
2233
            GDAL_DMD_OPENOPTIONLIST);
55,913✔
2234
    CPLString osDriver;
111,824✔
2235
    osDriver.Printf("driver %s",
2236
                    GDALDriver::FromHandle(hDriver)->GetDescription());
55,911✔
2237
    return GDALValidateOptions(pszOptionList, papszOpenOptions, "open option",
55,912✔
2238
                               osDriver);
55,908✔
2239
}
2240

2241
/************************************************************************/
2242
/*                           GDALValidateOptions()                      */
2243
/************************************************************************/
2244

2245
int GDALValidateOptions(const char *pszOptionList,
101,648✔
2246
                        const char *const *papszOptionsToValidate,
2247
                        const char *pszErrorMessageOptionType,
2248
                        const char *pszErrorMessageContainerName)
2249
{
2250
    if (papszOptionsToValidate == nullptr || *papszOptionsToValidate == nullptr)
101,648✔
2251
        return TRUE;
85,797✔
2252
    if (pszOptionList == nullptr)
15,851✔
2253
        return TRUE;
155✔
2254

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

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

2277
            ++papszOptionsToValidate;
1✔
2278
            continue;
1,692✔
2279
        }
2280

2281
        if (EQUAL(pszKey, "VALIDATE_OPEN_OPTIONS"))
26,782✔
2282
        {
2283
            ++papszOptionsToValidate;
×
2284
            CPLFree(pszKey);
×
2285
            continue;
×
2286
        }
2287

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

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

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

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

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

2372
            CPLFree(pszKey);
1,691✔
2373
            ++papszOptionsToValidate;
1,691✔
2374
            continue;
1,691✔
2375
        }
2376

2377
#ifdef DEBUG
2378
        CPLXMLNode *psChildSubNode = psChildNode->psChild;
25,091✔
2379
        while (psChildSubNode)
149,361✔
2380
        {
2381
            if (psChildSubNode->eType == CXT_Attribute)
124,270✔
2382
            {
2383
                if (!(EQUAL(psChildSubNode->pszValue, "name") ||
87,350✔
2384
                      EQUAL(psChildSubNode->pszValue, "alias") ||
62,259✔
2385
                      EQUAL(psChildSubNode->pszValue, "deprecated_alias") ||
62,137✔
2386
                      EQUAL(psChildSubNode->pszValue, "alt_config_option") ||
62,056✔
2387
                      EQUAL(psChildSubNode->pszValue, "description") ||
62,023✔
2388
                      EQUAL(psChildSubNode->pszValue, "type") ||
40,889✔
2389
                      EQUAL(psChildSubNode->pszValue, "min") ||
15,798✔
2390
                      EQUAL(psChildSubNode->pszValue, "max") ||
15,500✔
2391
                      EQUAL(psChildSubNode->pszValue, "default") ||
15,132✔
2392
                      EQUAL(psChildSubNode->pszValue, "maxsize") ||
1,010✔
2393
                      EQUAL(psChildSubNode->pszValue, "required") ||
985✔
2394
                      EQUAL(psChildSubNode->pszValue, "scope")))
930✔
2395
                {
2396
                    /* Driver error */
2397
                    CPLError(CE_Warning, CPLE_NotSupported,
×
2398
                             "%s : unhandled attribute '%s' for %s %s.",
2399
                             pszErrorMessageContainerName,
2400
                             psChildSubNode->pszValue, pszKey,
2401
                             pszErrorMessageOptionType);
2402
                }
2403
            }
2404
            psChildSubNode = psChildSubNode->psNext;
124,270✔
2405
        }
2406
#endif
2407

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

2620
    CPLDestroyXMLNode(psNode);
15,696✔
2621
    return bRet ? TRUE : FALSE;
15,696✔
2622
}
2623

2624
/************************************************************************/
2625
/*                         GDALIdentifyDriver()                         */
2626
/************************************************************************/
2627

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

2656
GDALDriverH CPL_STDCALL GDALIdentifyDriver(const char *pszFilename,
24,722✔
2657
                                           CSLConstList papszFileList)
2658

2659
{
2660
    return GDALIdentifyDriverEx(pszFilename, 0, nullptr, papszFileList);
24,722✔
2661
}
2662

2663
/************************************************************************/
2664
/*                         GDALIdentifyDriverEx()                       */
2665
/************************************************************************/
2666

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

2705
GDALDriverH CPL_STDCALL GDALIdentifyDriverEx(
24,812✔
2706
    const char *pszFilename, unsigned int nIdentifyFlags,
2707
    const char *const *papszAllowedDrivers, const char *const *papszFileList)
2708
{
2709
    GDALDriverManager *poDM = GetGDALDriverManager();
24,812✔
2710
    CPLAssert(nullptr != poDM);
24,812✔
2711

2712
    // If no driver kind is specified, assume all are to be probed.
2713
    if ((nIdentifyFlags & GDAL_OF_KIND_MASK) == 0)
24,812✔
2714
        nIdentifyFlags |= GDAL_OF_KIND_MASK & ~GDAL_OF_MULTIDIM_RASTER;
24,769✔
2715

2716
    GDALOpenInfo oOpenInfo(pszFilename, nIdentifyFlags, papszFileList);
49,624✔
2717
    oOpenInfo.papszAllowedDrivers = papszAllowedDrivers;
24,812✔
2718

2719
    CPLErrorStateBackuper oBackuper;
49,624✔
2720
    CPLErrorSetState(CE_None, CPLE_AppDefined, "");
24,812✔
2721

2722
    const int nDriverCount = poDM->GetDriverCount();
24,812✔
2723

2724
    // First pass: only use drivers that have a pfnIdentify implementation.
2725
    std::vector<GDALDriver *> apoSecondPassDrivers;
49,624✔
2726
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,306,640✔
2727
    {
2728
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,283,120✔
2729
        if (papszAllowedDrivers != nullptr &&
5,290,350✔
2730
            CSLFindString(papszAllowedDrivers,
7,226✔
2731
                          GDALGetDriverShortName(poDriver)) == -1)
2732
        {
2733
            continue;
987,826✔
2734
        }
2735

2736
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,277,230✔
2737

2738
        if (poDriver->pfnIdentify == nullptr &&
5,275,940✔
2739
            poDriver->pfnIdentifyEx == nullptr)
976,889✔
2740
        {
2741
            continue;
976,889✔
2742
        }
2743

2744
        if (papszAllowedDrivers != nullptr &&
4,299,090✔
2745
            CSLFindString(papszAllowedDrivers,
44✔
2746
                          GDALGetDriverShortName(poDriver)) == -1)
2747
            continue;
×
2748
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
12,892,500✔
2749
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
4,299,550✔
2750
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
502✔
2751
            continue;
141✔
2752
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
12,900,000✔
2753
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
4,303,740✔
2754
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
4,833✔
2755
            continue;
3,614✔
2756

2757
        if (poDriver->pfnIdentifyEx)
4,295,300✔
2758
        {
2759
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) > 0)
×
2760
                return poDriver;
×
2761
        }
2762
        else
2763
        {
2764
            const int nIdentifyRes = poDriver->pfnIdentify(&oOpenInfo);
4,295,300✔
2765
            if (nIdentifyRes > 0)
4,295,300✔
2766
                return poDriver;
1,294✔
2767
            if (nIdentifyRes < 0 &&
4,318,430✔
2768
                poDriver->GetMetadataItem("IS_NON_LOADED_PLUGIN"))
24,422✔
2769
            {
2770
                // Not loaded plugin
2771
                apoSecondPassDrivers.push_back(poDriver);
36✔
2772
            }
2773
        }
2774
    }
2775

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

2785
    // third pass: slow method.
2786
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
5,207,010✔
2787
    {
2788
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
5,183,580✔
2789
        if (papszAllowedDrivers != nullptr &&
5,187,340✔
2790
            CSLFindString(papszAllowedDrivers,
3,756✔
2791
                          GDALGetDriverShortName(poDriver)) == -1)
2792
        {
2793
            continue;
3,739✔
2794
        }
2795

2796
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
5,179,850✔
2797

2798
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
15,538,100✔
2799
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
5,180,290✔
2800
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
440✔
2801
            continue;
137✔
2802
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
15,539,700✔
2803
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
5,181,250✔
2804
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
1,542✔
2805
            continue;
925✔
2806

2807
        if (poDriver->pfnIdentifyEx != nullptr)
5,178,780✔
2808
        {
2809
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) == 0)
×
2810
                continue;
×
2811
        }
2812
        else if (poDriver->pfnIdentify != nullptr)
5,178,780✔
2813
        {
2814
            if (poDriver->pfnIdentify(&oOpenInfo) == 0)
4,219,370✔
2815
                continue;
4,195,420✔
2816
        }
2817

2818
        GDALDataset *poDS;
2819
        if (poDriver->pfnOpen != nullptr)
983,362✔
2820
        {
2821
            poDS = poDriver->pfnOpen(&oOpenInfo);
936,445✔
2822
            if (poDS != nullptr)
936,445✔
2823
            {
2824
                delete poDS;
91✔
2825
                return GDALDriver::ToHandle(poDriver);
91✔
2826
            }
2827

2828
            if (CPLGetLastErrorType() != CE_None)
936,354✔
2829
                return nullptr;
×
2830
        }
2831
        else if (poDriver->pfnOpenWithDriverArg != nullptr)
46,917✔
2832
        {
2833
            poDS = poDriver->pfnOpenWithDriverArg(poDriver, &oOpenInfo);
×
2834
            if (poDS != nullptr)
×
2835
            {
2836
                delete poDS;
×
2837
                return GDALDriver::ToHandle(poDriver);
×
2838
            }
2839

2840
            if (CPLGetLastErrorType() != CE_None)
×
2841
                return nullptr;
×
2842
        }
2843
    }
2844

2845
    return nullptr;
23,426✔
2846
}
2847

2848
/************************************************************************/
2849
/*                          SetMetadataItem()                           */
2850
/************************************************************************/
2851

2852
CPLErr GDALDriver::SetMetadataItem(const char *pszName, const char *pszValue,
3,959,250✔
2853
                                   const char *pszDomain)
2854

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

2876
/************************************************************************/
2877
/*                         InstantiateAlgorithm()                       */
2878
/************************************************************************/
2879

2880
//! @cond Doxygen_Suppress
2881

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

2891
/************************************************************************/
2892
/*                        DeclareAlgorithm()                            */
2893
/************************************************************************/
2894

2895
void GDALDriver::DeclareAlgorithm(const std::vector<std::string> &aosPath)
6,534✔
2896
{
2897
    const std::string osDriverName = GetDescription();
13,068✔
2898
    auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
6,534✔
2899

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

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

2936
    path.insert(path.end(), aosPath.begin(), aosPath.end());
6,534✔
2937

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

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

2950
    CPL_IGNORE_RET_VAL(osDriverName);
6,534✔
2951
}
6,534✔
2952

2953
//! @endcond
2954

2955
/************************************************************************/
2956
/*                   DoesDriverHandleExtension()                        */
2957
/************************************************************************/
2958

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

2980
/************************************************************************/
2981
/*                     IsOnlyExpectedGDBDrivers()                       */
2982
/************************************************************************/
2983

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

2997
/************************************************************************/
2998
/*                  GDALGetOutputDriversForDatasetName()                */
2999
/************************************************************************/
3000

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

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

3046
    auto poDM = GetGDALDriverManager();
2,506✔
3047
    const int nDriverCount = poDM->GetDriverCount(true);
2,506✔
3048
    GDALDriver *poMissingPluginDriver = nullptr;
2,506✔
3049
    std::string osMatchingPrefix;
5,012✔
3050
    for (int i = 0; i < nDriverCount; i++)
555,942✔
3051
    {
3052
        GDALDriver *poDriver = poDM->GetDriver(i, true);
553,436✔
3053
        bool bOk = false;
553,436✔
3054
        if ((poDriver->GetMetadataItem(GDAL_DCAP_CREATE) != nullptr ||
553,436✔
3055
             poDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY) != nullptr) &&
834,105✔
3056
            (((nFlagRasterVector & GDAL_OF_RASTER) &&
280,669✔
3057
              poDriver->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr) ||
230,495✔
3058
             ((nFlagRasterVector & GDAL_OF_VECTOR) &&
136,610✔
3059
              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)))
50,174✔
3060
        {
3061
            bOk = true;
169,145✔
3062
        }
3063
        else if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR_TRANSLATE_FROM) &&
386,349✔
3064
                 (nFlagRasterVector & GDAL_OF_VECTOR) != 0)
2,058✔
3065
        {
3066
            bOk = true;
×
3067
        }
3068
        if (bOk)
553,436✔
3069
        {
3070
            if (!osExt.empty() &&
335,938✔
3071
                DoesDriverHandleExtension(GDALDriver::ToHandle(poDriver),
166,793✔
3072
                                          osExt.c_str()))
3073
            {
3074
                if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
4,322✔
3075
                {
3076
                    poMissingPluginDriver = poDriver;
×
3077
                    aosMissingDriverNames.AddString(poDriver->GetDescription());
×
3078
                }
3079
                else
3080
                    aosDriverNames.AddString(poDriver->GetDescription());
4,322✔
3081
            }
3082
            else
3083
            {
3084
                const char *pszPrefix =
3085
                    poDriver->GetMetadataItem(GDAL_DMD_CONNECTION_PREFIX);
164,823✔
3086
                if (pszPrefix && STARTS_WITH_CI(pszDestDataset, pszPrefix))
164,823✔
3087
                {
3088
                    if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
9✔
3089
                    {
3090
                        osMatchingPrefix = pszPrefix;
×
3091
                        poMissingPluginDriver = poDriver;
×
3092
                        aosMissingDriverNames.AddString(
3093
                            poDriver->GetDescription());
×
3094
                    }
3095
                    else
3096
                        aosDriverNames.AddString(poDriver->GetDescription());
9✔
3097
                }
3098
            }
3099
        }
3100
    }
3101

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

3112
    if (bSingleMatch)
2,506✔
3113
    {
3114
        if (nFlagRasterVector == GDAL_OF_RASTER)
2,438✔
3115
        {
3116
            if (aosDriverNames.empty())
2,058✔
3117
            {
3118
                if (osExt.empty())
11✔
3119
                {
3120
                    aosDriverNames.AddString("GTiff");
8✔
3121
                }
3122
            }
3123
            else if (aosDriverNames.size() >= 2)
2,047✔
3124
            {
3125
                if (bEmitWarning && !(EQUAL(aosDriverNames[0], "GTiff") &&
3,720✔
3126
                                      EQUAL(aosDriverNames[1], "COG")))
1,860✔
3127
                {
3128
                    CPLError(CE_Warning, CPLE_AppDefined,
×
3129
                             "Several drivers matching %s extension. Using %s",
3130
                             osExt.c_str(), aosDriverNames[0]);
3131
                }
3132
                const std::string osDrvName = aosDriverNames[0];
3,720✔
3133
                aosDriverNames.Clear();
1,860✔
3134
                aosDriverNames.AddString(osDrvName.c_str());
1,860✔
3135
            }
3136
        }
3137
        else if (EQUAL(osExt.c_str(), "gdb") &&
381✔
3138
                 IsOnlyExpectedGDBDrivers(aosDriverNames))
1✔
3139
        {
3140
            // Do not warn about that case given that FileGDB write support
3141
            // forwards to OpenFileGDB one. And also consider GPSBabel as too
3142
            // marginal to deserve the warning.
3143
            aosDriverNames.Clear();
1✔
3144
            aosDriverNames.AddString("OpenFileGDB");
1✔
3145
        }
3146
        else if (aosDriverNames.size() >= 2)
379✔
3147
        {
3148
            if (bEmitWarning)
×
3149
            {
3150
                CPLError(CE_Warning, CPLE_AppDefined,
×
3151
                         "Several drivers matching %s %s. Using %s",
3152
                         osMatchingPrefix.empty() ? osExt.c_str()
×
3153
                                                  : osMatchingPrefix.c_str(),
×
3154
                         osMatchingPrefix.empty() ? "extension" : "prefix",
×
3155
                         aosDriverNames[0]);
3156
            }
3157
            const std::string osDrvName = aosDriverNames[0];
×
3158
            aosDriverNames.Clear();
×
3159
            aosDriverNames.AddString(osDrvName.c_str());
×
3160
        }
3161
    }
3162

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

3177
    return aosDriverNames.StealList();
2,506✔
3178
}
3179

3180
/************************************************************************/
3181
/*                GDALGetMessageAboutMissingPluginDriver()              */
3182
/************************************************************************/
3183

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

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