Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdaldriver.cpp
Line
Count
Source
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
#include "gdal_known_connection_prefixes.h"
20
21
#include <algorithm>
22
#include <cerrno>
23
#include <cstdlib>
24
#include <cstring>
25
#include <set>
26
#include <sys/stat.h>
27
28
#include "cpl_conv.h"
29
#include "cpl_error.h"
30
#include "cpl_minixml.h"
31
#include "cpl_multiproc.h"
32
#include "cpl_progress.h"
33
#include "cpl_string.h"
34
#include "cpl_vsi.h"
35
#include "ograpispy.h"
36
#include "ogr_core.h"
37
#include "ogrsf_frmts.h"
38
39
/************************************************************************/
40
/*                             GDALDriver()                             */
41
/************************************************************************/
42
43
0
GDALDriver::GDALDriver() = default;
44
45
/************************************************************************/
46
/*                            ~GDALDriver()                             */
47
/************************************************************************/
48
49
GDALDriver::~GDALDriver()
50
51
0
{
52
0
    if (pfnUnloadDriver != nullptr)
53
0
        pfnUnloadDriver(this);
54
0
}
55
56
/************************************************************************/
57
/*                         GDALCreateDriver()                           */
58
/************************************************************************/
59
60
/**
61
 * \brief Create a GDALDriver.
62
 *
63
 * Creates a driver in the GDAL heap.
64
 */
65
66
GDALDriverH CPL_STDCALL GDALCreateDriver()
67
0
{
68
0
    return new GDALDriver();
69
0
}
70
71
/************************************************************************/
72
/*                         GDALDestroyDriver()                          */
73
/************************************************************************/
74
75
/**
76
 * \brief Destroy a GDALDriver.
77
 *
78
 * This is roughly equivalent to deleting the driver, but is guaranteed
79
 * to take place in the GDAL heap.  It is important this that function
80
 * not be called on a driver that is registered with the GDALDriverManager.
81
 *
82
 * @param hDriver the driver to destroy.
83
 */
84
85
void CPL_STDCALL GDALDestroyDriver(GDALDriverH hDriver)
86
87
0
{
88
0
    if (hDriver != nullptr)
89
0
        delete GDALDriver::FromHandle(hDriver);
90
0
}
91
92
/************************************************************************/
93
/*                               Open()                                 */
94
/************************************************************************/
95
96
//! @cond Doxygen_Suppress
97
98
GDALDataset *GDALDriver::Open(GDALOpenInfo *poOpenInfo, bool bSetOpenOptions)
99
0
{
100
101
0
    GDALDataset *poDS = nullptr;
102
0
    pfnOpen = GetOpenCallback();
103
0
    if (pfnOpen != nullptr)
104
0
    {
105
0
        poDS = pfnOpen(poOpenInfo);
106
0
    }
107
0
    else if (pfnOpenWithDriverArg != nullptr)
108
0
    {
109
0
        poDS = pfnOpenWithDriverArg(this, poOpenInfo);
110
0
    }
111
112
0
    if (poDS)
113
0
    {
114
        // Only set GDAL_OF_THREAD_SAFE if the driver itself has set it in
115
        // poDS->nOpenFlags
116
0
        int nOpenFlags = poOpenInfo->nOpenFlags &
117
0
                         ~(GDAL_OF_FROM_GDALOPEN | GDAL_OF_THREAD_SAFE);
118
0
        if (poDS->nOpenFlags & GDAL_OF_THREAD_SAFE)
119
0
            nOpenFlags |= GDAL_OF_THREAD_SAFE;
120
0
        poDS->nOpenFlags = nOpenFlags;
121
122
0
        if (strlen(poDS->GetDescription()) == 0)
123
0
            poDS->SetDescription(poOpenInfo->pszFilename);
124
125
0
        if (poDS->poDriver == nullptr)
126
0
            poDS->poDriver = this;
127
128
0
        if (poDS->papszOpenOptions == nullptr && bSetOpenOptions)
129
0
        {
130
0
            poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
131
0
        }
132
133
0
        if (!(poOpenInfo->nOpenFlags & GDAL_OF_INTERNAL))
134
0
        {
135
0
            if (CPLGetPID() != GDALGetResponsiblePIDForCurrentThread())
136
0
                CPLDebug(
137
0
                    "GDAL",
138
0
                    "GDALOpen(%s, this=%p) succeeds as "
139
0
                    "%s (pid=%d, responsiblePID=%d).",
140
0
                    poOpenInfo->pszFilename, poDS, GetDescription(),
141
0
                    static_cast<int>(CPLGetPID()),
142
0
                    static_cast<int>(GDALGetResponsiblePIDForCurrentThread()));
143
0
            else
144
0
                CPLDebug("GDAL", "GDALOpen(%s, this=%p) succeeds as %s.",
145
0
                         poOpenInfo->pszFilename, poDS, GetDescription());
146
147
0
            poDS->AddToDatasetOpenList();
148
0
        }
149
0
    }
150
151
0
    return poDS;
152
0
}
153
154
//! @endcond
155
156
/************************************************************************/
157
/*                               Create()                               */
158
/************************************************************************/
159
160
/**
161
 * \brief Create a new dataset with this driver.
162
 *
163
 * What argument values are legal for particular drivers is driver specific,
164
 * and there is no way to query in advance to establish legal values.
165
 *
166
 * That function will try to validate the creation option list passed to the
167
 * driver with the GDALValidateCreationOptions() method. This check can be
168
 * disabled by defining the configuration option
169
 * GDAL_VALIDATE_CREATION_OPTIONS=NO.
170
 *
171
 * After you have finished working with the returned dataset, it is
172
 * <b>required</b> to close it with GDALClose(). This does not only close the
173
 * file handle, but also ensures that all the data and metadata has been written
174
 * to the dataset (GDALFlushCache() is not sufficient for that purpose).
175
 *
176
 * The arguments nXSize, nYSize and nBands can be passed to 0 when
177
 * creating a vector-only dataset for a compatible driver.
178
 *
179
 * Equivalent of the C function GDALCreate().
180
 *
181
 * @param pszFilename the name of the dataset to create.  UTF-8 encoded.
182
 * @param nXSize width of created raster in pixels.
183
 * @param nYSize height of created raster in pixels.
184
 * @param nBands number of bands.
185
 * @param eType type of raster.
186
 * @param papszOptions list of driver specific control parameters.
187
 * The APPEND_SUBDATASET=YES option can be
188
 * specified to avoid prior destruction of existing dataset.
189
 *
190
 * @return NULL on failure, or a new GDALDataset.
191
 */
192
193
GDALDataset *GDALDriver::Create(const char *pszFilename, int nXSize, int nYSize,
194
                                int nBands, GDALDataType eType,
195
                                CSLConstList papszOptions)
196
197
0
{
198
    /* -------------------------------------------------------------------- */
199
    /*      Does this format support creation.                              */
200
    /* -------------------------------------------------------------------- */
201
0
    pfnCreate = GetCreateCallback();
202
0
    if (CPL_UNLIKELY(pfnCreate == nullptr && pfnCreateEx == nullptr &&
203
0
                     pfnCreateVectorOnly == nullptr))
204
0
    {
205
0
        CPLError(CE_Failure, CPLE_NotSupported,
206
0
                 "GDALDriver::Create() ... no create method implemented"
207
0
                 " for this format.");
208
209
0
        return nullptr;
210
0
    }
211
    /* -------------------------------------------------------------------- */
212
    /*      Do some rudimentary argument checking.                          */
213
    /* -------------------------------------------------------------------- */
214
0
    if (CPL_UNLIKELY(nBands < 0))
215
0
    {
216
0
        CPLError(CE_Failure, CPLE_AppDefined,
217
0
                 "Attempt to create dataset with %d bands is illegal,"
218
0
                 "Must be >= 0.",
219
0
                 nBands);
220
0
        return nullptr;
221
0
    }
222
223
0
    if (CPL_UNLIKELY(GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
224
0
                     GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
225
0
                     (nXSize < 1 || nYSize < 1)))
226
0
    {
227
0
        CPLError(CE_Failure, CPLE_AppDefined,
228
0
                 "Attempt to create %dx%d dataset is illegal,"
229
0
                 "sizes must be larger than zero.",
230
0
                 nXSize, nYSize);
231
0
        return nullptr;
232
0
    }
233
234
0
    if (CPL_UNLIKELY(nBands != 0 &&
235
0
                     (eType == GDT_Unknown || eType == GDT_TypeCount)))
236
0
    {
237
0
        CPLError(CE_Failure, CPLE_IllegalArg,
238
0
                 "Illegal GDT_Unknown/GDT_TypeCount argument");
239
0
        return nullptr;
240
0
    }
241
242
    /* -------------------------------------------------------------------- */
243
    /*      Make sure we cleanup if there is an existing dataset of this    */
244
    /*      name.  But even if that seems to fail we will continue since    */
245
    /*      it might just be a corrupt file or something.                   */
246
    /* -------------------------------------------------------------------- */
247
0
    if (!CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false))
248
0
    {
249
        // Someone issuing Create("foo.tif") on a
250
        // memory driver doesn't expect files with those names to be deleted
251
        // on a file system...
252
        // This is somewhat messy. Ideally there should be a way for the
253
        // driver to overload the default behavior
254
0
        if (!EQUAL(GetDescription(), "MEM") &&
255
0
            !EQUAL(GetDescription(), "Memory") &&
256
            // ogr2ogr -f PostgreSQL might reach the Delete method of the
257
            // PostgisRaster driver which is undesirable
258
0
            !EQUAL(GetDescription(), "PostgreSQL"))
259
0
        {
260
0
            QuietDelete(pszFilename);
261
0
        }
262
0
    }
263
264
    /* -------------------------------------------------------------------- */
265
    /*      Validate creation options.                                      */
266
    /* -------------------------------------------------------------------- */
267
0
    if (CPLTestBool(
268
0
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
269
0
        GDALValidateCreationOptions(this, papszOptions);
270
271
    /* -------------------------------------------------------------------- */
272
    /*      Proceed with creation.                                          */
273
    /* -------------------------------------------------------------------- */
274
0
    CPLDebug("GDAL", "GDALDriver::Create(%s,%s,%d,%d,%d,%s,%p)",
275
0
             GetDescription(), pszFilename, nXSize, nYSize, nBands,
276
0
             GDALGetDataTypeName(eType), papszOptions);
277
278
0
    GDALDataset *poDS = nullptr;
279
0
    if (pfnCreateEx != nullptr)
280
0
    {
281
0
        poDS = pfnCreateEx(this, pszFilename, nXSize, nYSize, nBands, eType,
282
0
                           const_cast<char **>(papszOptions));
283
0
    }
284
0
    else if (pfnCreate != nullptr)
285
0
    {
286
0
        poDS = pfnCreate(pszFilename, nXSize, nYSize, nBands, eType,
287
0
                         const_cast<char **>(papszOptions));
288
0
    }
289
0
    else if (nBands < 1)
290
0
    {
291
0
        poDS = pfnCreateVectorOnly(this, pszFilename,
292
0
                                   const_cast<char **>(papszOptions));
293
0
    }
294
295
0
    if (poDS != nullptr)
296
0
    {
297
0
        if (poDS->GetDescription() == nullptr ||
298
0
            strlen(poDS->GetDescription()) == 0)
299
0
            poDS->SetDescription(pszFilename);
300
301
0
        if (poDS->poDriver == nullptr)
302
0
            poDS->poDriver = this;
303
304
0
        poDS->AddToDatasetOpenList();
305
0
    }
306
307
0
    return poDS;
308
0
}
309
310
/************************************************************************/
311
/*                             GDALCreate()                             */
312
/************************************************************************/
313
314
/**
315
 * \brief Create a new dataset with this driver.
316
 *
317
 * @see GDALDriver::Create()
318
 */
319
320
GDALDatasetH CPL_DLL CPL_STDCALL GDALCreate(GDALDriverH hDriver,
321
                                            const char *pszFilename, int nXSize,
322
                                            int nYSize, int nBands,
323
                                            GDALDataType eBandType,
324
                                            CSLConstList papszOptions)
325
326
0
{
327
0
    VALIDATE_POINTER1(hDriver, "GDALCreate", nullptr);
328
329
0
    GDALDatasetH hDS = GDALDriver::FromHandle(hDriver)->Create(
330
0
        pszFilename, nXSize, nYSize, nBands, eBandType, papszOptions);
331
332
0
#ifdef OGRAPISPY_ENABLED
333
0
    if (nBands < 1)
334
0
    {
335
0
        OGRAPISpyCreateDataSource(hDriver, pszFilename,
336
0
                                  const_cast<char **>(papszOptions), hDS);
337
0
    }
338
0
#endif
339
340
0
    return hDS;
341
0
}
342
343
/************************************************************************/
344
/*                       CreateMultiDimensional()                       */
345
/************************************************************************/
346
347
/**
348
 * \brief Create a new multidimensional dataset with this driver.
349
 *
350
 * Only drivers that advertise the GDAL_DCAP_MULTIDIM_RASTER capability and
351
 * implement the pfnCreateMultiDimensional method might return a non nullptr
352
 * GDALDataset.
353
 *
354
 * This is the same as the C function GDALCreateMultiDimensional().
355
 *
356
 * @param pszFilename  the name of the dataset to create.  UTF-8 encoded.
357
 * @param papszRootGroupOptions driver specific options regarding the creation
358
 *                              of the root group. Might be nullptr.
359
 * @param papszOptions driver specific options regarding the creation
360
 *                     of the dataset. Might be nullptr.
361
 * @return a new dataset, or nullptr in case of failure.
362
 *
363
 * @since GDAL 3.1
364
 */
365
366
GDALDataset *
367
GDALDriver::CreateMultiDimensional(const char *pszFilename,
368
                                   CSLConstList papszRootGroupOptions,
369
                                   CSLConstList papszOptions)
370
371
0
{
372
    /* -------------------------------------------------------------------- */
373
    /*      Does this format support creation.                              */
374
    /* -------------------------------------------------------------------- */
375
0
    pfnCreateMultiDimensional = GetCreateMultiDimensionalCallback();
376
0
    if (pfnCreateMultiDimensional == nullptr)
377
0
    {
378
0
        CPLError(CE_Failure, CPLE_NotSupported,
379
0
                 "GDALDriver::CreateMultiDimensional() ... "
380
0
                 "no CreateMultiDimensional method implemented "
381
0
                 "for this format.");
382
383
0
        return nullptr;
384
0
    }
385
386
    /* -------------------------------------------------------------------- */
387
    /*      Validate creation options.                                      */
388
    /* -------------------------------------------------------------------- */
389
0
    if (CPLTestBool(
390
0
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
391
0
    {
392
0
        const char *pszOptionList =
393
0
            GetMetadataItem(GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST);
394
0
        CPLString osDriver;
395
0
        osDriver.Printf("driver %s", GetDescription());
396
0
        GDALValidateOptions(pszOptionList, papszOptions, "creation option",
397
0
                            osDriver);
398
0
    }
399
400
0
    auto poDstDS = pfnCreateMultiDimensional(pszFilename, papszRootGroupOptions,
401
0
                                             papszOptions);
402
403
0
    if (poDstDS != nullptr)
404
0
    {
405
0
        if (poDstDS->GetDescription() == nullptr ||
406
0
            strlen(poDstDS->GetDescription()) == 0)
407
0
            poDstDS->SetDescription(pszFilename);
408
409
0
        if (poDstDS->poDriver == nullptr)
410
0
            poDstDS->poDriver = this;
411
0
    }
412
413
0
    return poDstDS;
414
0
}
415
416
/************************************************************************/
417
/*                       GDALCreateMultiDimensional()                   */
418
/************************************************************************/
419
420
/** \brief Create a new multidimensional dataset with this driver.
421
 *
422
 * This is the same as the C++ method GDALDriver::CreateMultiDimensional().
423
 */
424
GDALDatasetH GDALCreateMultiDimensional(GDALDriverH hDriver,
425
                                        const char *pszName,
426
                                        CSLConstList papszRootGroupOptions,
427
                                        CSLConstList papszOptions)
428
0
{
429
0
    VALIDATE_POINTER1(hDriver, __func__, nullptr);
430
0
    VALIDATE_POINTER1(pszName, __func__, nullptr);
431
0
    return GDALDataset::ToHandle(
432
0
        GDALDriver::FromHandle(hDriver)->CreateMultiDimensional(
433
0
            pszName, papszRootGroupOptions, papszOptions));
434
0
}
435
436
/************************************************************************/
437
/*                  DefaultCreateCopyMultiDimensional()                 */
438
/************************************************************************/
439
440
//! @cond Doxygen_Suppress
441
442
CPLErr GDALDriver::DefaultCreateCopyMultiDimensional(
443
    GDALDataset *poSrcDS, GDALDataset *poDstDS, bool bStrict,
444
    CSLConstList papszOptions, GDALProgressFunc pfnProgress,
445
    void *pProgressData)
446
0
{
447
0
    if (pfnProgress == nullptr)
448
0
        pfnProgress = GDALDummyProgress;
449
450
0
    auto poSrcRG = poSrcDS->GetRootGroup();
451
0
    if (!poSrcRG)
452
0
        return CE_Failure;
453
0
    auto poDstRG = poDstDS->GetRootGroup();
454
0
    if (!poDstRG)
455
0
        return CE_Failure;
456
0
    GUInt64 nCurCost = 0;
457
0
    return poDstRG->CopyFrom(poDstRG, poSrcDS, poSrcRG, bStrict, nCurCost,
458
0
                             poSrcRG->GetTotalCopyCost(), pfnProgress,
459
0
                             pProgressData, papszOptions)
460
0
               ? CE_None
461
0
               : CE_Failure;
462
0
}
463
464
//! @endcond
465
466
/************************************************************************/
467
/*                          DefaultCopyMasks()                          */
468
/************************************************************************/
469
470
//! @cond Doxygen_Suppress
471
CPLErr GDALDriver::DefaultCopyMasks(GDALDataset *poSrcDS, GDALDataset *poDstDS,
472
                                    int bStrict)
473
474
0
{
475
0
    return DefaultCopyMasks(poSrcDS, poDstDS, bStrict, nullptr, nullptr,
476
0
                            nullptr);
477
0
}
478
479
CPLErr GDALDriver::DefaultCopyMasks(GDALDataset *poSrcDS, GDALDataset *poDstDS,
480
                                    int bStrict, CSLConstList /*papszOptions*/,
481
                                    GDALProgressFunc pfnProgress,
482
                                    void *pProgressData)
483
484
0
{
485
0
    if (pfnProgress == nullptr)
486
0
        pfnProgress = GDALDummyProgress;
487
488
0
    int nBands = poSrcDS->GetRasterCount();
489
0
    if (nBands == 0)
490
0
        return CE_None;
491
492
    /* -------------------------------------------------------------------- */
493
    /*      Try to copy mask if it seems appropriate.                       */
494
    /* -------------------------------------------------------------------- */
495
0
    const char *papszOptions[2] = {"COMPRESSED=YES", nullptr};
496
0
    CPLErr eErr = CE_None;
497
498
0
    int nTotalBandsWithMask = 0;
499
0
    for (int iBand = 0; iBand < nBands; ++iBand)
500
0
    {
501
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
502
503
0
        int nMaskFlags = poSrcBand->GetMaskFlags();
504
0
        if (!(nMaskFlags &
505
0
              (GMF_ALL_VALID | GMF_PER_DATASET | GMF_ALPHA | GMF_NODATA)))
506
0
        {
507
0
            nTotalBandsWithMask++;
508
0
        }
509
0
    }
510
511
0
    int iBandWithMask = 0;
512
0
    for (int iBand = 0; eErr == CE_None && iBand < nBands; ++iBand)
513
0
    {
514
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
515
516
0
        int nMaskFlags = poSrcBand->GetMaskFlags();
517
0
        if (eErr == CE_None && !(nMaskFlags & (GMF_ALL_VALID | GMF_PER_DATASET |
518
0
                                               GMF_ALPHA | GMF_NODATA)))
519
0
        {
520
0
            GDALRasterBand *poDstBand = poDstDS->GetRasterBand(iBand + 1);
521
0
            if (poDstBand != nullptr)
522
0
            {
523
0
                eErr = poDstBand->CreateMaskBand(nMaskFlags);
524
0
                if (eErr == CE_None)
525
0
                {
526
0
                    void *pScaledData = GDALCreateScaledProgress(
527
0
                        double(iBandWithMask) /
528
0
                            std::max(1, nTotalBandsWithMask),
529
0
                        double(iBandWithMask + 1) /
530
0
                            std::max(1, nTotalBandsWithMask),
531
0
                        pfnProgress, pProgressData);
532
0
                    eErr = GDALRasterBandCopyWholeRaster(
533
0
                        poSrcBand->GetMaskBand(), poDstBand->GetMaskBand(),
534
0
                        papszOptions, GDALScaledProgress, pScaledData);
535
0
                    GDALDestroyScaledProgress(pScaledData);
536
0
                }
537
0
                else if (!bStrict)
538
0
                {
539
0
                    eErr = CE_None;
540
0
                }
541
0
            }
542
0
        }
543
0
    }
544
545
    /* -------------------------------------------------------------------- */
546
    /*      Try to copy a per-dataset mask if we have one.                  */
547
    /* -------------------------------------------------------------------- */
548
0
    const int nMaskFlags = poSrcDS->GetRasterBand(1)->GetMaskFlags();
549
0
    if (eErr == CE_None &&
550
0
        !(nMaskFlags & (GMF_ALL_VALID | GMF_ALPHA | GMF_NODATA)) &&
551
0
        (nMaskFlags & GMF_PER_DATASET))
552
0
    {
553
0
        eErr = poDstDS->CreateMaskBand(nMaskFlags);
554
0
        if (eErr == CE_None)
555
0
        {
556
0
            eErr = GDALRasterBandCopyWholeRaster(
557
0
                poSrcDS->GetRasterBand(1)->GetMaskBand(),
558
0
                poDstDS->GetRasterBand(1)->GetMaskBand(), papszOptions,
559
0
                pfnProgress, pProgressData);
560
0
        }
561
0
        else if (!bStrict)
562
0
        {
563
0
            eErr = CE_None;
564
0
        }
565
0
    }
566
567
0
    return eErr;
568
0
}
569
570
/************************************************************************/
571
/*                         DefaultCreateCopy()                          */
572
/************************************************************************/
573
574
GDALDataset *GDALDriver::DefaultCreateCopy(const char *pszFilename,
575
                                           GDALDataset *poSrcDS, int bStrict,
576
                                           CSLConstList papszOptions,
577
                                           GDALProgressFunc pfnProgress,
578
                                           void *pProgressData)
579
580
0
{
581
0
    if (pfnProgress == nullptr)
582
0
        pfnProgress = GDALDummyProgress;
583
584
0
    CPLErrorReset();
585
586
    /* -------------------------------------------------------------------- */
587
    /*      Use multidimensional raster API if available.                   */
588
    /* -------------------------------------------------------------------- */
589
0
    auto poSrcGroup = poSrcDS->GetRootGroup();
590
0
    if (poSrcGroup != nullptr && GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
591
0
    {
592
0
        CPLStringList aosDatasetCO;
593
0
        for (const char *pszOption : cpl::Iterate(papszOptions))
594
0
        {
595
0
            if (!STARTS_WITH_CI(pszOption, "ARRAY:"))
596
0
                aosDatasetCO.AddString(pszOption);
597
0
        }
598
0
        auto poDstDS = std::unique_ptr<GDALDataset>(
599
0
            CreateMultiDimensional(pszFilename, nullptr, aosDatasetCO.List()));
600
0
        if (!poDstDS)
601
0
            return nullptr;
602
0
        auto poDstGroup = poDstDS->GetRootGroup();
603
0
        if (!poDstGroup)
604
0
            return nullptr;
605
0
        if (DefaultCreateCopyMultiDimensional(
606
0
                poSrcDS, poDstDS.get(), CPL_TO_BOOL(bStrict), papszOptions,
607
0
                pfnProgress, pProgressData) != CE_None)
608
0
            return nullptr;
609
0
        return poDstDS.release();
610
0
    }
611
612
    /* -------------------------------------------------------------------- */
613
    /*      Validate that we can create the output as requested.            */
614
    /* -------------------------------------------------------------------- */
615
0
    const int nXSize = poSrcDS->GetRasterXSize();
616
0
    const int nYSize = poSrcDS->GetRasterYSize();
617
0
    const int nBands = poSrcDS->GetRasterCount();
618
619
0
    CPLDebug("GDAL", "Using default GDALDriver::CreateCopy implementation.");
620
621
0
    const int nLayerCount = poSrcDS->GetLayerCount();
622
0
    if (nBands == 0 && nLayerCount == 0 &&
623
0
        GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
624
0
    {
625
0
        CPLError(CE_Failure, CPLE_NotSupported,
626
0
                 "GDALDriver::DefaultCreateCopy does not support zero band");
627
0
        return nullptr;
628
0
    }
629
0
    if (poSrcDS->GetDriver() != nullptr &&
630
0
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
631
0
        poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr &&
632
0
        GetMetadataItem(GDAL_DCAP_RASTER) == nullptr &&
633
0
        GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr)
634
0
    {
635
0
        CPLError(CE_Failure, CPLE_NotSupported,
636
0
                 "Source driver is raster-only whereas output driver is "
637
0
                 "vector-only");
638
0
        return nullptr;
639
0
    }
640
0
    else if (poSrcDS->GetDriver() != nullptr &&
641
0
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_RASTER) ==
642
0
                 nullptr &&
643
0
             poSrcDS->GetDriver()->GetMetadataItem(GDAL_DCAP_VECTOR) !=
644
0
                 nullptr &&
645
0
             GetMetadataItem(GDAL_DCAP_RASTER) != nullptr &&
646
0
             GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
647
0
    {
648
0
        CPLError(CE_Failure, CPLE_NotSupported,
649
0
                 "Source driver is vector-only whereas output driver is "
650
0
                 "raster-only");
651
0
        return nullptr;
652
0
    }
653
654
0
    if (!pfnProgress(0.0, nullptr, pProgressData))
655
0
    {
656
0
        CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
657
0
        return nullptr;
658
0
    }
659
660
    /* -------------------------------------------------------------------- */
661
    /*      Propagate some specific structural metadata as options if it    */
662
    /*      appears to be supported by the target driver and the caller     */
663
    /*      didn't provide values.                                          */
664
    /* -------------------------------------------------------------------- */
665
0
    char **papszCreateOptions = CSLDuplicate(papszOptions);
666
0
    const char *const apszOptItems[] = {"NBITS", "IMAGE_STRUCTURE", "PIXELTYPE",
667
0
                                        "IMAGE_STRUCTURE", nullptr};
668
669
0
    for (int iOptItem = 0; nBands > 0 && apszOptItems[iOptItem] != nullptr;
670
0
         iOptItem += 2)
671
0
    {
672
        // does the source have this metadata item on the first band?
673
0
        auto poBand = poSrcDS->GetRasterBand(1);
674
0
        poBand->EnablePixelTypeSignedByteWarning(false);
675
0
        const char *pszValue = poBand->GetMetadataItem(
676
0
            apszOptItems[iOptItem], apszOptItems[iOptItem + 1]);
677
0
        poBand->EnablePixelTypeSignedByteWarning(true);
678
679
0
        if (pszValue == nullptr)
680
0
            continue;
681
682
        // Do not override provided value.
683
0
        if (CSLFetchNameValue(papszCreateOptions, pszValue) != nullptr)
684
0
            continue;
685
686
        // Does this appear to be a supported creation option on this driver?
687
0
        const char *pszOptionList =
688
0
            GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
689
690
0
        if (pszOptionList == nullptr ||
691
0
            strstr(pszOptionList, apszOptItems[iOptItem]) == nullptr)
692
0
            continue;
693
694
0
        papszCreateOptions = CSLSetNameValue(papszCreateOptions,
695
0
                                             apszOptItems[iOptItem], pszValue);
696
0
    }
697
698
    /* -------------------------------------------------------------------- */
699
    /*      Create destination dataset.                                     */
700
    /* -------------------------------------------------------------------- */
701
0
    GDALDataType eType = GDT_Unknown;
702
703
0
    if (nBands > 0)
704
0
        eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
705
0
    GDALDataset *poDstDS =
706
0
        Create(pszFilename, nXSize, nYSize, nBands, eType, papszCreateOptions);
707
708
0
    CSLDestroy(papszCreateOptions);
709
710
0
    if (poDstDS == nullptr)
711
0
        return nullptr;
712
713
0
    int nDstBands = poDstDS->GetRasterCount();
714
0
    CPLErr eErr = CE_None;
715
0
    if (nDstBands != nBands)
716
0
    {
717
0
        if (GetMetadataItem(GDAL_DCAP_RASTER) != nullptr)
718
0
        {
719
            // Should not happen for a well-behaved driver.
720
0
            CPLError(
721
0
                CE_Failure, CPLE_AppDefined,
722
0
                "Output driver created only %d bands whereas %d were expected",
723
0
                nDstBands, nBands);
724
0
            eErr = CE_Failure;
725
0
        }
726
0
        nDstBands = 0;
727
0
    }
728
729
    /* -------------------------------------------------------------------- */
730
    /*      Try setting the projection and geotransform if it seems         */
731
    /*      suitable.                                                       */
732
    /* -------------------------------------------------------------------- */
733
0
    if (nDstBands == 0 && !bStrict)
734
0
        CPLTurnFailureIntoWarning(true);
735
736
0
    GDALGeoTransform gt;
737
0
    if (eErr == CE_None && poSrcDS->GetGeoTransform(gt) == CE_None &&
738
0
        gt != GDALGeoTransform())
739
0
    {
740
0
        eErr = poDstDS->SetGeoTransform(gt);
741
0
        if (!bStrict)
742
0
            eErr = CE_None;
743
0
    }
744
745
0
    if (eErr == CE_None)
746
0
    {
747
0
        const auto poSrcSRS = poSrcDS->GetSpatialRefRasterOnly();
748
0
        if (poSrcSRS && !poSrcSRS->IsEmpty())
749
0
        {
750
0
            eErr = poDstDS->SetSpatialRef(poSrcSRS);
751
0
            if (!bStrict)
752
0
                eErr = CE_None;
753
0
        }
754
0
    }
755
756
    /* -------------------------------------------------------------------- */
757
    /*      Copy GCPs.                                                      */
758
    /* -------------------------------------------------------------------- */
759
0
    if (poSrcDS->GetGCPCount() > 0 && eErr == CE_None)
760
0
    {
761
0
        eErr = poDstDS->SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
762
0
                                poSrcDS->GetGCPProjection());
763
0
        if (!bStrict)
764
0
            eErr = CE_None;
765
0
    }
766
767
0
    if (nDstBands == 0 && !bStrict)
768
0
        CPLTurnFailureIntoWarning(false);
769
770
    /* -------------------------------------------------------------------- */
771
    /*      Copy metadata.                                                  */
772
    /* -------------------------------------------------------------------- */
773
0
    DefaultCopyMetadata(poSrcDS, poDstDS, papszOptions, nullptr);
774
775
    /* -------------------------------------------------------------------- */
776
    /*      Loop copying bands.                                             */
777
    /* -------------------------------------------------------------------- */
778
0
    for (int iBand = 0; eErr == CE_None && iBand < nDstBands; ++iBand)
779
0
    {
780
0
        GDALRasterBand *const poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
781
0
        GDALRasterBand *const poDstBand = poDstDS->GetRasterBand(iBand + 1);
782
783
        /* --------------------------------------------------------------------
784
         */
785
        /*      Do we need to copy a colortable. */
786
        /* --------------------------------------------------------------------
787
         */
788
0
        GDALColorTable *const poCT = poSrcBand->GetColorTable();
789
0
        if (poCT != nullptr)
790
0
            poDstBand->SetColorTable(poCT);
791
792
        /* --------------------------------------------------------------------
793
         */
794
        /*      Do we need to copy other metadata?  Most of this is */
795
        /*      non-critical, so lets not bother folks if it fails are we */
796
        /*      are not in strict mode. */
797
        /* --------------------------------------------------------------------
798
         */
799
0
        if (!bStrict)
800
0
            CPLTurnFailureIntoWarning(true);
801
802
0
        if (strlen(poSrcBand->GetDescription()) > 0)
803
0
            poDstBand->SetDescription(poSrcBand->GetDescription());
804
805
0
        if (CSLCount(poSrcBand->GetMetadata()) > 0)
806
0
            poDstBand->SetMetadata(poSrcBand->GetMetadata());
807
808
0
        int bSuccess = FALSE;
809
0
        double dfValue = poSrcBand->GetOffset(&bSuccess);
810
0
        if (bSuccess && dfValue != 0.0)
811
0
            poDstBand->SetOffset(dfValue);
812
813
0
        dfValue = poSrcBand->GetScale(&bSuccess);
814
0
        if (bSuccess && dfValue != 1.0)
815
0
            poDstBand->SetScale(dfValue);
816
817
0
        GDALCopyNoDataValue(poDstBand, poSrcBand);
818
819
0
        if (poSrcBand->GetColorInterpretation() != GCI_Undefined &&
820
0
            poSrcBand->GetColorInterpretation() !=
821
0
                poDstBand->GetColorInterpretation())
822
0
            poDstBand->SetColorInterpretation(
823
0
                poSrcBand->GetColorInterpretation());
824
825
0
        char **papszCatNames = poSrcBand->GetCategoryNames();
826
0
        if (nullptr != papszCatNames)
827
0
            poDstBand->SetCategoryNames(papszCatNames);
828
829
        // Only copy RAT if it is of reasonable size to fit in memory
830
0
        GDALRasterAttributeTable *poRAT = poSrcBand->GetDefaultRAT();
831
0
        if (poRAT != nullptr && static_cast<GIntBig>(poRAT->GetColumnCount()) *
832
0
                                        poRAT->GetRowCount() <
833
0
                                    1024 * 1024)
834
0
        {
835
0
            poDstBand->SetDefaultRAT(poRAT);
836
0
        }
837
838
0
        if (!bStrict)
839
0
        {
840
0
            CPLTurnFailureIntoWarning(false);
841
0
        }
842
0
        else
843
0
        {
844
0
            eErr = CPLGetLastErrorType();
845
0
        }
846
0
    }
847
848
    /* -------------------------------------------------------------------- */
849
    /*      Copy image data.                                                */
850
    /* -------------------------------------------------------------------- */
851
0
    if (eErr == CE_None && nDstBands > 0)
852
0
    {
853
0
        const char *const apszCopyRasterOptionsSkipHoles[] = {"SKIP_HOLES=YES",
854
0
                                                              nullptr};
855
0
        const bool bSkipHoles = CPLTestBool(
856
0
            CSLFetchNameValueDef(papszOptions, "SKIP_HOLES", "FALSE"));
857
0
        eErr = GDALDatasetCopyWholeRaster(
858
0
            poSrcDS, poDstDS,
859
0
            bSkipHoles ? apszCopyRasterOptionsSkipHoles : nullptr, pfnProgress,
860
0
            pProgressData);
861
0
    }
862
863
    /* -------------------------------------------------------------------- */
864
    /*      Should we copy some masks over?                                 */
865
    /* -------------------------------------------------------------------- */
866
0
    if (eErr == CE_None && nDstBands > 0)
867
0
        eErr = DefaultCopyMasks(poSrcDS, poDstDS, eErr);
868
869
    /* -------------------------------------------------------------------- */
870
    /*      Copy vector layers                                              */
871
    /* -------------------------------------------------------------------- */
872
0
    if (eErr == CE_None)
873
0
    {
874
0
        if (nLayerCount > 0 && poDstDS->TestCapability(ODsCCreateLayer))
875
0
        {
876
0
            for (int iLayer = 0; iLayer < nLayerCount; ++iLayer)
877
0
            {
878
0
                OGRLayer *poLayer = poSrcDS->GetLayer(iLayer);
879
880
0
                if (poLayer == nullptr)
881
0
                    continue;
882
883
0
                poDstDS->CopyLayer(poLayer, poLayer->GetName(), nullptr);
884
0
            }
885
0
        }
886
0
    }
887
888
    /* -------------------------------------------------------------------- */
889
    /*      Try to cleanup the output dataset if the translation failed.    */
890
    /* -------------------------------------------------------------------- */
891
0
    if (eErr != CE_None)
892
0
    {
893
0
        delete poDstDS;
894
0
        if (!CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false))
895
0
        {
896
            // Only delete if creating a new file
897
0
            Delete(pszFilename);
898
0
        }
899
0
        return nullptr;
900
0
    }
901
0
    else
902
0
    {
903
0
        CPLErrorReset();
904
0
    }
905
906
0
    return poDstDS;
907
0
}
908
909
/************************************************************************/
910
/*                       DefaultCopyMetadata()                          */
911
/************************************************************************/
912
913
void GDALDriver::DefaultCopyMetadata(GDALDataset *poSrcDS, GDALDataset *poDstDS,
914
                                     CSLConstList papszOptions,
915
                                     CSLConstList papszExcludedDomains)
916
0
{
917
0
    const char *pszCopySrcMDD =
918
0
        CSLFetchNameValueDef(papszOptions, "COPY_SRC_MDD", "AUTO");
919
0
    char **papszSrcMDD = CSLFetchNameValueMultiple(papszOptions, "SRC_MDD");
920
0
    if (EQUAL(pszCopySrcMDD, "AUTO") || CPLTestBool(pszCopySrcMDD) ||
921
0
        papszSrcMDD)
922
0
    {
923
0
        if ((!papszSrcMDD || CSLFindString(papszSrcMDD, "") >= 0 ||
924
0
             CSLFindString(papszSrcMDD, "_DEFAULT_") >= 0) &&
925
0
            CSLFindString(papszExcludedDomains, "") < 0 &&
926
0
            CSLFindString(papszExcludedDomains, "_DEFAULT_") < 0)
927
0
        {
928
0
            if (poSrcDS->GetMetadata() != nullptr)
929
0
                poDstDS->SetMetadata(poSrcDS->GetMetadata());
930
0
        }
931
932
        /* -------------------------------------------------------------------- */
933
        /*      Copy transportable special domain metadata.                     */
934
        /*      It would be nice to copy geolocation, but it is pretty fragile. */
935
        /* -------------------------------------------------------------------- */
936
0
        constexpr const char *apszDefaultDomains[] = {
937
0
            "RPC", "xml:XMP", "json:ISIS3", "json:VICAR"};
938
0
        for (const char *pszDomain : apszDefaultDomains)
939
0
        {
940
0
            if ((!papszSrcMDD || CSLFindString(papszSrcMDD, pszDomain) >= 0) &&
941
0
                CSLFindString(papszExcludedDomains, pszDomain) < 0)
942
0
            {
943
0
                char **papszMD = poSrcDS->GetMetadata(pszDomain);
944
0
                if (papszMD)
945
0
                    poDstDS->SetMetadata(papszMD, pszDomain);
946
0
            }
947
0
        }
948
949
0
        if ((!EQUAL(pszCopySrcMDD, "AUTO") && CPLTestBool(pszCopySrcMDD)) ||
950
0
            papszSrcMDD)
951
0
        {
952
0
            for (const char *pszDomain :
953
0
                 CPLStringList(poSrcDS->GetMetadataDomainList()))
954
0
            {
955
0
                if (pszDomain[0] != 0 &&
956
0
                    (!papszSrcMDD ||
957
0
                     CSLFindString(papszSrcMDD, pszDomain) >= 0))
958
0
                {
959
0
                    bool bCanCopy = true;
960
0
                    if (CSLFindString(papszExcludedDomains, pszDomain) >= 0)
961
0
                    {
962
0
                        bCanCopy = false;
963
0
                    }
964
0
                    else
965
0
                    {
966
0
                        for (const char *pszOtherDomain : apszDefaultDomains)
967
0
                        {
968
0
                            if (EQUAL(pszDomain, pszOtherDomain))
969
0
                            {
970
0
                                bCanCopy = false;
971
0
                                break;
972
0
                            }
973
0
                        }
974
0
                        if (!papszSrcMDD)
975
0
                        {
976
0
                            constexpr const char *const apszReservedDomains[] =
977
0
                                {"IMAGE_STRUCTURE", "DERIVED_SUBDATASETS"};
978
0
                            for (const char *pszOtherDomain :
979
0
                                 apszReservedDomains)
980
0
                            {
981
0
                                if (EQUAL(pszDomain, pszOtherDomain))
982
0
                                {
983
0
                                    bCanCopy = false;
984
0
                                    break;
985
0
                                }
986
0
                            }
987
0
                        }
988
0
                    }
989
0
                    if (bCanCopy)
990
0
                    {
991
0
                        poDstDS->SetMetadata(poSrcDS->GetMetadata(pszDomain),
992
0
                                             pszDomain);
993
0
                    }
994
0
                }
995
0
            }
996
0
        }
997
0
    }
998
0
    CSLDestroy(papszSrcMDD);
999
0
}
1000
1001
/************************************************************************/
1002
/*                      QuietDeleteForCreateCopy()                      */
1003
/************************************************************************/
1004
1005
CPLErr GDALDriver::QuietDeleteForCreateCopy(const char *pszFilename,
1006
                                            GDALDataset *poSrcDS)
1007
0
{
1008
    // Someone issuing CreateCopy("foo.tif") on a
1009
    // memory driver doesn't expect files with those names to be deleted
1010
    // on a file system...
1011
    // This is somewhat messy. Ideally there should be a way for the
1012
    // driver to overload the default behavior
1013
0
    if (!EQUAL(GetDescription(), "MEM") && !EQUAL(GetDescription(), "Memory") &&
1014
        // Also exclude database formats for which there's no file list
1015
        // and whose opening might be slow (GeoRaster in particular)
1016
0
        !EQUAL(GetDescription(), "GeoRaster") &&
1017
0
        !EQUAL(GetDescription(), "PostGISRaster"))
1018
0
    {
1019
        /* --------------------------------------------------------------------
1020
         */
1021
        /*      Establish list of files of output dataset if it already
1022
         * exists. */
1023
        /* --------------------------------------------------------------------
1024
         */
1025
0
        std::set<std::string> oSetExistingDestFiles;
1026
0
        {
1027
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1028
0
            const char *const apszAllowedDrivers[] = {GetDescription(),
1029
0
                                                      nullptr};
1030
0
            auto poExistingOutputDS =
1031
0
                std::unique_ptr<GDALDataset>(GDALDataset::Open(
1032
0
                    pszFilename, GDAL_OF_RASTER, apszAllowedDrivers));
1033
0
            if (poExistingOutputDS)
1034
0
            {
1035
0
                for (const char *pszFileInList :
1036
0
                     CPLStringList(poExistingOutputDS->GetFileList()))
1037
0
                {
1038
0
                    oSetExistingDestFiles.insert(
1039
0
                        CPLString(pszFileInList).replaceAll('\\', '/'));
1040
0
                }
1041
0
            }
1042
0
        }
1043
1044
        /* --------------------------------------------------------------------
1045
         */
1046
        /*      Check if the source dataset shares some files with the dest
1047
         * one.*/
1048
        /* --------------------------------------------------------------------
1049
         */
1050
0
        std::set<std::string> oSetExistingDestFilesFoundInSource;
1051
0
        if (!oSetExistingDestFiles.empty())
1052
0
        {
1053
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1054
            // We need to reopen in a temporary dataset for the particular
1055
            // case of overwritten a .tif.ovr file from a .tif
1056
            // If we probe the file list of the .tif, it will then open the
1057
            // .tif.ovr !
1058
0
            const char *const apszAllowedDrivers[] = {
1059
0
                poSrcDS->GetDriver() ? poSrcDS->GetDriver()->GetDescription()
1060
0
                                     : nullptr,
1061
0
                nullptr};
1062
0
            auto poSrcDSTmp = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1063
0
                poSrcDS->GetDescription(), GDAL_OF_RASTER, apszAllowedDrivers,
1064
0
                poSrcDS->papszOpenOptions));
1065
0
            if (poSrcDSTmp)
1066
0
            {
1067
0
                for (const char *pszFileInList :
1068
0
                     CPLStringList(poSrcDSTmp->GetFileList()))
1069
0
                {
1070
0
                    CPLString osFilename(pszFileInList);
1071
0
                    osFilename.replaceAll('\\', '/');
1072
0
                    if (cpl::contains(oSetExistingDestFiles, osFilename))
1073
0
                    {
1074
0
                        oSetExistingDestFilesFoundInSource.insert(
1075
0
                            std::move(osFilename));
1076
0
                    }
1077
0
                }
1078
0
            }
1079
0
        }
1080
1081
        // If the source file(s) and the dest one share some files in
1082
        // common, only remove the files that are *not* in common
1083
0
        if (!oSetExistingDestFilesFoundInSource.empty())
1084
0
        {
1085
0
            for (const std::string &osFilename : oSetExistingDestFiles)
1086
0
            {
1087
0
                if (!cpl::contains(oSetExistingDestFilesFoundInSource,
1088
0
                                   osFilename))
1089
0
                {
1090
0
                    VSIUnlink(osFilename.c_str());
1091
0
                }
1092
0
            }
1093
0
        }
1094
1095
0
        QuietDelete(pszFilename);
1096
0
    }
1097
1098
0
    return CE_None;
1099
0
}
1100
1101
//! @endcond
1102
1103
/************************************************************************/
1104
/*                             CreateCopy()                             */
1105
/************************************************************************/
1106
1107
/**
1108
 * \brief Create a copy of a dataset.
1109
 *
1110
 * This method will attempt to create a copy of a raster dataset with the
1111
 * indicated filename, and in this drivers format.  Band number, size,
1112
 * type, projection, geotransform and so forth are all to be copied from
1113
 * the provided template dataset.
1114
 *
1115
 * Note that many sequential write once formats (such as JPEG and PNG) don't
1116
 * implement the Create() method but do implement this CreateCopy() method.
1117
 * If the driver doesn't implement CreateCopy(), but does implement Create()
1118
 * then the default CreateCopy() mechanism built on calling Create() will
1119
 * be used.
1120
 * So to test if CreateCopy() is available, you can test if GDAL_DCAP_CREATECOPY
1121
 * or GDAL_DCAP_CREATE is set in the GDAL metadata.
1122
 *
1123
 * It is intended that CreateCopy() will often be used with a source dataset
1124
 * which is a virtual dataset allowing configuration of band types, and other
1125
 * information without actually duplicating raster data (see the VRT driver).
1126
 * This is what is done by the gdal_translate utility for example.
1127
 *
1128
 * That function will try to validate the creation option list passed to the
1129
 * driver with the GDALValidateCreationOptions() method. This check can be
1130
 * disabled by defining the configuration option
1131
 * GDAL_VALIDATE_CREATION_OPTIONS=NO.
1132
 *
1133
 * This function copy all metadata from the default domain ("")
1134
 *
1135
 * Even is bStrict is TRUE, only the <b>value</b> of the data is equivalent,
1136
 * but the data layout (INTERLEAVE as PIXEL/LINE/BAND) of the dst dataset is
1137
 * controlled by the papszOptions creation options, and may differ from the
1138
 * poSrcDS src dataset.
1139
 * Starting from GDAL 3.5, if no INTERLEAVE and COMPRESS creation option has
1140
 * been specified in papszOptions, and if the driver supports equivalent
1141
 * interleaving as the src dataset, the CreateCopy() will internally add the
1142
 * proper creation option to get the same data interleaving.
1143
 *
1144
 * After you have finished working with the returned dataset, it is
1145
 * <b>required</b> to close it with GDALClose(). This does not only close the
1146
 * file handle, but also ensures that all the data and metadata has been written
1147
 * to the dataset (GDALFlushCache() is not sufficient for that purpose).
1148
 *
1149
 * For multidimensional datasets, papszOptions can contain array creation
1150
 * options, if they are prefixed with "ARRAY:". \see GDALGroup::CopyFrom()
1151
 * documentation for further details regarding such options.
1152
 *
1153
 * @param pszFilename the name for the new dataset.  UTF-8 encoded.
1154
 * @param poSrcDS the dataset being duplicated.
1155
 * @param bStrict TRUE if the copy must be strictly equivalent, or more
1156
 * normally FALSE indicating that the copy may adapt as needed for the
1157
 * output format.
1158
 * @param papszOptions additional format dependent options controlling
1159
 * creation of the output file.
1160
 * The APPEND_SUBDATASET=YES option can be specified to avoid prior destruction
1161
 * of existing dataset.
1162
 * Starting with GDAL 3.8.0, the following options are recognized by the
1163
 * GTiff, COG, VRT, PNG au JPEG drivers:
1164
 * <ul>
1165
 * <li>COPY_SRC_MDD=AUTO/YES/NO: whether metadata domains of the source dataset
1166
 * should be copied to the destination dataset. In the default AUTO mode, only
1167
 * "safe" domains will be copied, which include the default metadata domain
1168
 * (some drivers may include other domains such as IMD, RPC, GEOLOCATION). When
1169
 * setting YES, all domains will be copied (but a few reserved ones like
1170
 * IMAGE_STRUCTURE or DERIVED_SUBDATASETS). When setting NO, no source metadata
1171
 * will be copied.
1172
 * </li>
1173
 *<li>SRC_MDD=domain_name: which source metadata domain should be copied.
1174
 * This option restricts the list of source metadata domains to be copied
1175
 * (it implies COPY_SRC_MDD=YES if it is not set). This option may be specified
1176
 * as many times as they are source domains. The default metadata domain is the
1177
 * empty string "" ("_DEFAULT_") may also be used when empty string is not practical)
1178
 * </li>
1179
 * </ul>
1180
 * @param pfnProgress a function to be used to report progress of the copy.
1181
 * @param pProgressData application data passed into progress function.
1182
 *
1183
 * @return a pointer to the newly created dataset (may be read-only access).
1184
 */
1185
1186
GDALDataset *GDALDriver::CreateCopy(const char *pszFilename,
1187
                                    GDALDataset *poSrcDS, int bStrict,
1188
                                    CSLConstList papszOptions,
1189
                                    GDALProgressFunc pfnProgress,
1190
                                    void *pProgressData)
1191
1192
0
{
1193
0
    if (pfnProgress == nullptr)
1194
0
        pfnProgress = GDALDummyProgress;
1195
1196
0
    const int nBandCount = poSrcDS->GetRasterCount();
1197
1198
    /* -------------------------------------------------------------------- */
1199
    /*      If no INTERLEAVE creation option is given, we will try to add   */
1200
    /*      one that matches the current srcDS interleaving                 */
1201
    /* -------------------------------------------------------------------- */
1202
0
    char **papszOptionsToDelete = nullptr;
1203
0
    const char *srcInterleave =
1204
0
        poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
1205
0
    if (nBandCount > 1 && srcInterleave != nullptr &&
1206
0
        CSLFetchNameValue(papszOptions, "INTERLEAVE") == nullptr &&
1207
0
        EQUAL(CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE"), "NONE"))
1208
0
    {
1209
1210
        // look for INTERLEAVE values of the driver
1211
0
        char **interleavesCSL = nullptr;
1212
0
        const char *pszOptionList =
1213
0
            this->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST);
1214
0
        CPLXMLNode *xmlNode =
1215
0
            !pszOptionList ? nullptr : CPLParseXMLString(pszOptionList);
1216
0
        for (CPLXMLNode *child = !xmlNode ? nullptr : xmlNode->psChild;
1217
0
             child != nullptr; child = child->psNext)
1218
0
        {
1219
0
            if ((child->eType == CXT_Element) &&
1220
0
                EQUAL(child->pszValue, "Option"))
1221
0
            {
1222
0
                const char *nameAttribute =
1223
0
                    CPLGetXMLValue(child, "name", nullptr);
1224
0
                const bool isInterleaveAttribute =
1225
0
                    nameAttribute && EQUAL(nameAttribute, "INTERLEAVE");
1226
0
                if (isInterleaveAttribute)
1227
0
                {
1228
0
                    for (CPLXMLNode *optionChild = child->psChild;
1229
0
                         optionChild != nullptr;
1230
0
                         optionChild = optionChild->psNext)
1231
0
                    {
1232
0
                        if ((optionChild->eType == CXT_Element) &&
1233
0
                            EQUAL(optionChild->pszValue, "Value"))
1234
0
                        {
1235
0
                            CPLXMLNode *optionChildValue = optionChild->psChild;
1236
0
                            if (optionChildValue &&
1237
0
                                (optionChildValue->eType == CXT_Text))
1238
0
                            {
1239
0
                                interleavesCSL = CSLAddString(
1240
0
                                    interleavesCSL, optionChildValue->pszValue);
1241
0
                            }
1242
0
                        }
1243
0
                    }
1244
0
                }
1245
0
            }
1246
0
        }
1247
0
        CPLDestroyXMLNode(xmlNode);
1248
1249
0
        const char *dstInterleaveBand =
1250
0
            (CSLFindString(interleavesCSL, "BAND") >= 0)  ? "BAND"
1251
0
            : (CSLFindString(interleavesCSL, "BSQ") >= 0) ? "BSQ"
1252
0
                                                          : nullptr;
1253
0
        const char *dstInterleaveLine =
1254
0
            (CSLFindString(interleavesCSL, "LINE") >= 0)  ? "LINE"
1255
0
            : (CSLFindString(interleavesCSL, "BIL") >= 0) ? "BIL"
1256
0
                                                          : nullptr;
1257
0
        const char *dstInterleavePixel =
1258
0
            (CSLFindString(interleavesCSL, "PIXEL") >= 0) ? "PIXEL"
1259
0
            : (CSLFindString(interleavesCSL, "BIP") >= 0) ? "BIP"
1260
0
                                                          : nullptr;
1261
0
        const char *dstInterleave =
1262
0
            EQUAL(srcInterleave, "BAND")    ? dstInterleaveBand
1263
0
            : EQUAL(srcInterleave, "LINE")  ? dstInterleaveLine
1264
0
            : EQUAL(srcInterleave, "PIXEL") ? dstInterleavePixel
1265
0
                                            : nullptr;
1266
0
        CSLDestroy(interleavesCSL);
1267
1268
0
        if (dstInterleave != nullptr)
1269
0
        {
1270
0
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1271
0
            papszOptionsToDelete = CSLSetNameValue(papszOptionsToDelete,
1272
0
                                                   "INTERLEAVE", dstInterleave);
1273
0
            papszOptionsToDelete = CSLSetNameValue(
1274
0
                papszOptionsToDelete, "@INTERLEAVE_ADDED_AUTOMATICALLY", "YES");
1275
0
            papszOptions = papszOptionsToDelete;
1276
0
        }
1277
0
    }
1278
1279
    /* -------------------------------------------------------------------- */
1280
    /*      Make sure we cleanup if there is an existing dataset of this    */
1281
    /*      name.  But even if that seems to fail we will continue since    */
1282
    /*      it might just be a corrupt file or something.                   */
1283
    /* -------------------------------------------------------------------- */
1284
0
    const bool bAppendSubdataset =
1285
0
        CPLFetchBool(papszOptions, "APPEND_SUBDATASET", false);
1286
    // Note: @QUIET_DELETE_ON_CREATE_COPY is set to NO by the KMLSuperOverlay
1287
    // driver when writing a .kmz file. Also by GDALTranslate() if it has
1288
    // already done a similar job.
1289
0
    if (!bAppendSubdataset &&
1290
0
        CPLFetchBool(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY", true))
1291
0
    {
1292
0
        QuietDeleteForCreateCopy(pszFilename, poSrcDS);
1293
0
    }
1294
1295
0
    int iIdxQuietDeleteOnCreateCopy =
1296
0
        CSLPartialFindString(papszOptions, "@QUIET_DELETE_ON_CREATE_COPY=");
1297
0
    if (iIdxQuietDeleteOnCreateCopy >= 0)
1298
0
    {
1299
0
        if (papszOptionsToDelete == nullptr)
1300
0
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1301
0
        papszOptionsToDelete = CSLRemoveStrings(
1302
0
            papszOptionsToDelete, iIdxQuietDeleteOnCreateCopy, 1, nullptr);
1303
0
        papszOptions = papszOptionsToDelete;
1304
0
    }
1305
1306
    /* -------------------------------------------------------------------- */
1307
    /*      If _INTERNAL_DATASET=YES, the returned dataset will not be      */
1308
    /*      registered in the global list of open datasets.                 */
1309
    /* -------------------------------------------------------------------- */
1310
0
    const int iIdxInternalDataset =
1311
0
        CSLPartialFindString(papszOptions, "_INTERNAL_DATASET=");
1312
0
    bool bInternalDataset = false;
1313
0
    if (iIdxInternalDataset >= 0)
1314
0
    {
1315
0
        bInternalDataset =
1316
0
            CPLFetchBool(papszOptions, "_INTERNAL_DATASET", false);
1317
0
        if (papszOptionsToDelete == nullptr)
1318
0
            papszOptionsToDelete = CSLDuplicate(papszOptions);
1319
0
        papszOptionsToDelete = CSLRemoveStrings(
1320
0
            papszOptionsToDelete, iIdxInternalDataset, 1, nullptr);
1321
0
        papszOptions = papszOptionsToDelete;
1322
0
    }
1323
1324
    /* -------------------------------------------------------------------- */
1325
    /*      Validate creation options.                                      */
1326
    /* -------------------------------------------------------------------- */
1327
0
    if (CPLTestBool(
1328
0
            CPLGetConfigOption("GDAL_VALIDATE_CREATION_OPTIONS", "YES")))
1329
0
    {
1330
0
        auto poSrcGroup = poSrcDS->GetRootGroup();
1331
0
        if (poSrcGroup != nullptr && GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
1332
0
        {
1333
0
            CPLStringList aosDatasetCO;
1334
0
            for (const char *pszOption : cpl::Iterate(papszOptions))
1335
0
            {
1336
0
                if (!STARTS_WITH_CI(pszOption, "ARRAY:"))
1337
0
                    aosDatasetCO.AddString(pszOption);
1338
0
            }
1339
0
            GDALValidateCreationOptions(this, aosDatasetCO.List());
1340
0
        }
1341
0
        else
1342
0
        {
1343
0
            GDALValidateCreationOptions(this, papszOptions);
1344
0
        }
1345
0
    }
1346
1347
    /* -------------------------------------------------------------------- */
1348
    /*      Advise the source raster that we are going to read it completely */
1349
    /* -------------------------------------------------------------------- */
1350
1351
0
    const int nXSize = poSrcDS->GetRasterXSize();
1352
0
    const int nYSize = poSrcDS->GetRasterYSize();
1353
0
    GDALDataType eDT = GDT_Unknown;
1354
0
    if (nBandCount > 0)
1355
0
    {
1356
0
        GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(1);
1357
0
        if (poSrcBand)
1358
0
            eDT = poSrcBand->GetRasterDataType();
1359
0
    }
1360
0
    poSrcDS->AdviseRead(0, 0, nXSize, nYSize, nXSize, nYSize, eDT, nBandCount,
1361
0
                        nullptr, nullptr);
1362
1363
    /* -------------------------------------------------------------------- */
1364
    /*      If the format provides a CreateCopy() method use that,          */
1365
    /*      otherwise fallback to the internal implementation using the     */
1366
    /*      Create() method.                                                */
1367
    /* -------------------------------------------------------------------- */
1368
0
    GDALDataset *poDstDS = nullptr;
1369
0
    auto l_pfnCreateCopy = GetCreateCopyCallback();
1370
0
    if (l_pfnCreateCopy != nullptr &&
1371
0
        !CPLTestBool(CPLGetConfigOption("GDAL_DEFAULT_CREATE_COPY", "NO")))
1372
0
    {
1373
0
        poDstDS = l_pfnCreateCopy(pszFilename, poSrcDS, bStrict,
1374
0
                                  const_cast<char **>(papszOptions),
1375
0
                                  pfnProgress, pProgressData);
1376
0
        if (poDstDS != nullptr)
1377
0
        {
1378
0
            if (poDstDS->GetDescription() == nullptr ||
1379
0
                strlen(poDstDS->GetDescription()) == 0)
1380
0
                poDstDS->SetDescription(pszFilename);
1381
1382
0
            if (poDstDS->poDriver == nullptr)
1383
0
                poDstDS->poDriver = this;
1384
1385
0
            if (!bInternalDataset)
1386
0
                poDstDS->AddToDatasetOpenList();
1387
0
        }
1388
0
    }
1389
0
    else
1390
0
    {
1391
0
        poDstDS = DefaultCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1392
0
                                    pfnProgress, pProgressData);
1393
0
    }
1394
1395
0
    CSLDestroy(papszOptionsToDelete);
1396
0
    return poDstDS;
1397
0
}
1398
1399
/************************************************************************/
1400
/*                           GDALCreateCopy()                           */
1401
/************************************************************************/
1402
1403
/**
1404
 * \brief Create a copy of a dataset.
1405
 *
1406
 * @see GDALDriver::CreateCopy()
1407
 */
1408
1409
GDALDatasetH CPL_STDCALL GDALCreateCopy(GDALDriverH hDriver,
1410
                                        const char *pszFilename,
1411
                                        GDALDatasetH hSrcDS, int bStrict,
1412
                                        CSLConstList papszOptions,
1413
                                        GDALProgressFunc pfnProgress,
1414
                                        void *pProgressData)
1415
1416
0
{
1417
0
    VALIDATE_POINTER1(hDriver, "GDALCreateCopy", nullptr);
1418
0
    VALIDATE_POINTER1(hSrcDS, "GDALCreateCopy", nullptr);
1419
1420
0
    return GDALDriver::FromHandle(hDriver)->CreateCopy(
1421
0
        pszFilename, GDALDataset::FromHandle(hSrcDS), bStrict, papszOptions,
1422
0
        pfnProgress, pProgressData);
1423
0
}
1424
1425
/************************************************************************/
1426
/*                      CanVectorTranslateFrom()                        */
1427
/************************************************************************/
1428
1429
/** Returns whether the driver can translate from a vector dataset,
1430
 * using the arguments passed to GDALVectorTranslate() stored in
1431
 * papszVectorTranslateArguments.
1432
 *
1433
 * This is used to determine if the driver supports the VectorTranslateFrom()
1434
 * operation.
1435
 *
1436
 * @param pszDestName Target dataset name
1437
 * @param poSourceDS  Source dataset
1438
 * @param papszVectorTranslateArguments Non-positional arguments passed to
1439
 *                                      GDALVectorTranslate() (may be nullptr)
1440
 * @param[out] ppapszFailureReasons nullptr, or a pointer to an null-terminated
1441
 * array of strings to record the reason(s) for the impossibility.
1442
 * @return true if VectorTranslateFrom() can be called with the same arguments.
1443
 * @since GDAL 3.8
1444
 */
1445
bool GDALDriver::CanVectorTranslateFrom(
1446
    const char *pszDestName, GDALDataset *poSourceDS,
1447
    CSLConstList papszVectorTranslateArguments, char ***ppapszFailureReasons)
1448
1449
0
{
1450
0
    if (ppapszFailureReasons)
1451
0
    {
1452
0
        *ppapszFailureReasons = nullptr;
1453
0
    }
1454
1455
0
    if (!pfnCanVectorTranslateFrom)
1456
0
    {
1457
0
        if (ppapszFailureReasons)
1458
0
        {
1459
0
            *ppapszFailureReasons = CSLAddString(
1460
0
                nullptr,
1461
0
                "CanVectorTranslateFrom() not implemented for this driver");
1462
0
        }
1463
0
        return false;
1464
0
    }
1465
1466
0
    char **papszFailureReasons = nullptr;
1467
0
    bool bRet = pfnCanVectorTranslateFrom(
1468
0
        pszDestName, poSourceDS, papszVectorTranslateArguments,
1469
0
        ppapszFailureReasons ? ppapszFailureReasons : &papszFailureReasons);
1470
0
    if (!ppapszFailureReasons)
1471
0
    {
1472
0
        for (const char *pszReason :
1473
0
             cpl::Iterate(static_cast<CSLConstList>(papszFailureReasons)))
1474
0
        {
1475
0
            CPLDebug("GDAL", "%s", pszReason);
1476
0
        }
1477
0
        CSLDestroy(papszFailureReasons);
1478
0
    }
1479
0
    return bRet;
1480
0
}
1481
1482
bool GDALDriver::HasOpenOption(const char *pszOpenOptionName) const
1483
0
{
1484
0
    if (pszOpenOptionName == nullptr)
1485
0
        return false;
1486
1487
    // Const cast is safe here since we are only reading the metadata
1488
0
    auto pszOOMd{const_cast<GDALDriver *>(this)->GetMetadataItem(
1489
0
        GDAL_DMD_OPENOPTIONLIST)};
1490
0
    if (pszOOMd == nullptr)
1491
0
        return false;
1492
1493
0
    const CPLXMLTreeCloser oXml{CPLParseXMLString(pszOOMd)};
1494
0
    for (CPLXMLNode *option = oXml->psChild; option != nullptr;
1495
0
         option = option->psNext)
1496
0
    {
1497
0
        if (EQUAL(CPLGetXMLValue(CPLGetXMLNode(option, "name"), nullptr, ""),
1498
0
                  pszOpenOptionName))
1499
0
            return true;
1500
0
    }
1501
0
    return false;
1502
0
}
1503
1504
/************************************************************************/
1505
/*                         VectorTranslateFrom()                        */
1506
/************************************************************************/
1507
1508
/** Create a copy of a vector dataset, using the arguments passed to
1509
 * GDALVectorTranslate() stored in papszVectorTranslateArguments.
1510
 *
1511
 * This may be implemented by some drivers that can convert from an existing
1512
 * dataset in an optimized way.
1513
 *
1514
 * This is for example used by the PMTiles to convert from MBTiles.
1515
 *
1516
 * @param pszDestName Target dataset name
1517
 * @param poSourceDS  Source dataset
1518
 * @param papszVectorTranslateArguments Non-positional arguments passed to
1519
 *                                      GDALVectorTranslate() (may be nullptr)
1520
 * @param pfnProgress a function to be used to report progress of the copy.
1521
 * @param pProgressData application data passed into progress function.
1522
 * @return a new dataset in case of success, or nullptr in case of error.
1523
 * @since GDAL 3.8
1524
 */
1525
GDALDataset *GDALDriver::VectorTranslateFrom(
1526
    const char *pszDestName, GDALDataset *poSourceDS,
1527
    CSLConstList papszVectorTranslateArguments, GDALProgressFunc pfnProgress,
1528
    void *pProgressData)
1529
1530
0
{
1531
0
    if (!pfnVectorTranslateFrom)
1532
0
    {
1533
0
        CPLError(CE_Failure, CPLE_AppDefined,
1534
0
                 "VectorTranslateFrom() not implemented for this driver");
1535
0
        return nullptr;
1536
0
    }
1537
1538
0
    return pfnVectorTranslateFrom(pszDestName, poSourceDS,
1539
0
                                  papszVectorTranslateArguments, pfnProgress,
1540
0
                                  pProgressData);
1541
0
}
1542
1543
/************************************************************************/
1544
/*                            QuietDelete()                             */
1545
/************************************************************************/
1546
1547
/**
1548
 * \brief Delete dataset if found.
1549
 *
1550
 * This is a helper method primarily used by Create() and
1551
 * CreateCopy() to predelete any dataset of the name soon to be
1552
 * created.  It will attempt to delete the named dataset if
1553
 * one is found, otherwise it does nothing.  An error is only
1554
 * returned if the dataset is found but the delete fails.
1555
 *
1556
 * This is a static method and it doesn't matter what driver instance
1557
 * it is invoked on.  It will attempt to discover the correct driver
1558
 * using Identify().
1559
 *
1560
 * @param pszName the dataset name to try and delete.
1561
 * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL
1562
 * terminated list of strings with the driver short names that must be
1563
 * considered. (Note: implemented only starting with GDAL 3.4.1)
1564
 * @return CE_None if the dataset does not exist, or is deleted without issues.
1565
 */
1566
1567
CPLErr GDALDriver::QuietDelete(const char *pszName,
1568
                               CSLConstList papszAllowedDrivers)
1569
1570
0
{
1571
0
    VSIStatBufL sStat;
1572
0
    const bool bExists =
1573
0
        VSIStatExL(pszName, &sStat,
1574
0
                   VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0;
1575
1576
0
#ifdef S_ISFIFO
1577
0
    if (bExists && S_ISFIFO(sStat.st_mode))
1578
0
        return CE_None;
1579
0
#endif
1580
1581
0
    GDALDriver *poDriver = nullptr;
1582
0
    if (papszAllowedDrivers)
1583
0
    {
1584
0
        GDALOpenInfo oOpenInfo(pszName, GDAL_OF_ALL);
1585
0
        for (const char *pszDriverName : cpl::Iterate(papszAllowedDrivers))
1586
0
        {
1587
0
            GDALDriver *poTmpDriver =
1588
0
                GDALDriver::FromHandle(GDALGetDriverByName(pszDriverName));
1589
0
            if (poTmpDriver)
1590
0
            {
1591
0
                const bool bIdentifyRes =
1592
0
                    poTmpDriver->pfnIdentifyEx
1593
0
                        ? poTmpDriver->pfnIdentifyEx(poTmpDriver, &oOpenInfo) >
1594
0
                              0
1595
0
                        : poTmpDriver->pfnIdentify &&
1596
0
                              poTmpDriver->pfnIdentify(&oOpenInfo) > 0;
1597
0
                if (bIdentifyRes)
1598
0
                {
1599
0
                    poDriver = poTmpDriver;
1600
0
                    break;
1601
0
                }
1602
0
            }
1603
0
        }
1604
0
    }
1605
0
    else
1606
0
    {
1607
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1608
0
        poDriver = GDALDriver::FromHandle(GDALIdentifyDriver(pszName, nullptr));
1609
0
    }
1610
1611
0
    if (poDriver == nullptr)
1612
0
        return CE_None;
1613
1614
0
    if (bExists && VSI_ISDIR(sStat.st_mode) &&
1615
0
        (EQUAL(poDriver->GetDescription(), "MapInfo File") ||
1616
0
         EQUAL(poDriver->GetDescription(), "ESRI Shapefile")))
1617
0
    {
1618
        // Those drivers are a bit special and handle directories as container
1619
        // of layers, but it is quite common to found other files too, and
1620
        // removing the directory might be non-desirable.
1621
0
        return CE_None;
1622
0
    }
1623
1624
0
    CPLDebug("GDAL", "QuietDelete(%s) invoking Delete()", pszName);
1625
1626
0
    poDriver->pfnDelete = poDriver->GetDeleteCallback();
1627
0
    const bool bQuiet = !bExists && poDriver->pfnDelete == nullptr &&
1628
0
                        poDriver->pfnDeleteDataSource == nullptr;
1629
0
    if (bQuiet)
1630
0
    {
1631
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1632
0
        return poDriver->Delete(pszName);
1633
0
    }
1634
0
    else
1635
0
    {
1636
0
        return poDriver->Delete(pszName);
1637
0
    }
1638
0
}
1639
1640
/************************************************************************/
1641
/*                               Delete()                               */
1642
/************************************************************************/
1643
1644
/**
1645
 * \brief Delete named dataset.
1646
 *
1647
 * The driver will attempt to delete the named dataset in a driver specific
1648
 * fashion.  Full featured drivers will delete all associated files,
1649
 * database objects, or whatever is appropriate.  The default behavior when
1650
 * no driver specific behavior is provided is to attempt to delete all the
1651
 * files that are returned by GDALGetFileList() on the dataset handle.
1652
 *
1653
 * It is unwise to have open dataset handles on this dataset when it is
1654
 * deleted.
1655
 *
1656
 * Equivalent of the C function GDALDeleteDataset().
1657
 *
1658
 * @param pszFilename name of dataset to delete.
1659
 *
1660
 * @return CE_None on success, or CE_Failure if the operation fails.
1661
 */
1662
1663
CPLErr GDALDriver::Delete(const char *pszFilename)
1664
1665
0
{
1666
0
    pfnDelete = GetDeleteCallback();
1667
0
    if (pfnDelete != nullptr)
1668
0
        return pfnDelete(pszFilename);
1669
0
    else if (pfnDeleteDataSource != nullptr)
1670
0
        return pfnDeleteDataSource(this, pszFilename);
1671
1672
    /* -------------------------------------------------------------------- */
1673
    /*      Collect file list.                                              */
1674
    /* -------------------------------------------------------------------- */
1675
0
    GDALDatasetH hDS = GDALOpenEx(pszFilename, 0, nullptr, nullptr, nullptr);
1676
1677
0
    if (hDS == nullptr)
1678
0
    {
1679
0
        if (CPLGetLastErrorNo() == 0)
1680
0
            CPLError(CE_Failure, CPLE_OpenFailed,
1681
0
                     "Unable to open %s to obtain file list.", pszFilename);
1682
1683
0
        return CE_Failure;
1684
0
    }
1685
1686
0
    const CPLStringList aosFileList(GDALGetFileList(hDS));
1687
1688
0
    GDALClose(hDS);
1689
0
    hDS = nullptr;
1690
1691
0
    if (aosFileList.empty())
1692
0
    {
1693
0
        CPLError(CE_Failure, CPLE_NotSupported,
1694
0
                 "Unable to determine files associated with %s, "
1695
0
                 "delete fails.",
1696
0
                 pszFilename);
1697
0
        return CE_Failure;
1698
0
    }
1699
1700
0
    return Delete(nullptr, aosFileList.List());
1701
0
}
1702
1703
/************************************************************************/
1704
/*                               Delete()                               */
1705
/************************************************************************/
1706
1707
/**
1708
 * \brief Delete a currently opened dataset
1709
 *
1710
 * The driver will attempt to delete the passed dataset in a driver specific
1711
 * fashion.  Full featured drivers will delete all associated files,
1712
 * database objects, or whatever is appropriate.  The default behavior when
1713
 * no driver specific behavior is provided is to attempt to delete all the
1714
 * files that are returned by GDALGetFileList() on the dataset handle.
1715
 *
1716
 * Note that this will generally not work on Windows systems that don't accept
1717
 * deleting opened files.
1718
 *
1719
 * At least one of poDS or papszFileList must not be NULL
1720
 *
1721
 * @param poDS dataset to delete, or NULL
1722
 * @param papszFileList File list to delete, typically obtained with
1723
 *                      poDS->GetFileList(), or NULL
1724
 *
1725
 * @return CE_None on success, or CE_Failure if the operation fails.
1726
 *
1727
 * @since 3.12
1728
 */
1729
1730
CPLErr GDALDriver::Delete(GDALDataset *poDS, CSLConstList papszFileList)
1731
1732
0
{
1733
0
    if (poDS)
1734
0
    {
1735
0
        pfnDelete = GetDeleteCallback();
1736
0
        if (pfnDelete != nullptr)
1737
0
            return pfnDelete(poDS->GetDescription());
1738
0
        else if (pfnDeleteDataSource != nullptr)
1739
0
            return pfnDeleteDataSource(this, poDS->GetDescription());
1740
0
    }
1741
1742
    /* -------------------------------------------------------------------- */
1743
    /*      Delete all files.                                               */
1744
    /* -------------------------------------------------------------------- */
1745
0
    CPLErr eErr = CE_None;
1746
0
    for (int i = 0; papszFileList && papszFileList[i]; ++i)
1747
0
    {
1748
0
        if (VSIUnlink(papszFileList[i]) != 0)
1749
0
        {
1750
0
            CPLError(CE_Failure, CPLE_AppDefined, "Deleting %s failed:\n%s",
1751
0
                     papszFileList[i], VSIStrerror(errno));
1752
0
            eErr = CE_Failure;
1753
0
        }
1754
0
    }
1755
1756
0
    return eErr;
1757
0
}
1758
1759
/************************************************************************/
1760
/*                         GDALDeleteDataset()                          */
1761
/************************************************************************/
1762
1763
/**
1764
 * \brief Delete named dataset.
1765
 *
1766
 * @see GDALDriver::Delete()
1767
 */
1768
1769
CPLErr CPL_STDCALL GDALDeleteDataset(GDALDriverH hDriver,
1770
                                     const char *pszFilename)
1771
1772
0
{
1773
0
    if (hDriver == nullptr)
1774
0
        hDriver = GDALIdentifyDriver(pszFilename, nullptr);
1775
1776
0
    if (hDriver == nullptr)
1777
0
    {
1778
0
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1779
0
                 pszFilename);
1780
0
        return CE_Failure;
1781
0
    }
1782
1783
0
#ifdef OGRAPISPY_ENABLED
1784
0
    if (GDALGetMetadataItem(hDriver, GDAL_DCAP_VECTOR, nullptr))
1785
0
    {
1786
0
        OGRAPISpyDeleteDataSource(hDriver, pszFilename);
1787
0
    }
1788
0
#endif
1789
1790
0
    return GDALDriver::FromHandle(hDriver)->Delete(pszFilename);
1791
0
}
1792
1793
/************************************************************************/
1794
/*                           DefaultRename()                            */
1795
/*                                                                      */
1796
/*      The generic implementation based on the file list used when     */
1797
/*      there is no format specific implementation.                     */
1798
/************************************************************************/
1799
1800
//! @cond Doxygen_Suppress
1801
CPLErr GDALDriver::DefaultRename(const char *pszNewName, const char *pszOldName)
1802
1803
0
{
1804
    /* -------------------------------------------------------------------- */
1805
    /*      Collect file list.                                              */
1806
    /* -------------------------------------------------------------------- */
1807
0
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
1808
1809
0
    if (hDS == nullptr)
1810
0
    {
1811
0
        if (CPLGetLastErrorNo() == 0)
1812
0
            CPLError(CE_Failure, CPLE_OpenFailed,
1813
0
                     "Unable to open %s to obtain file list.", pszOldName);
1814
1815
0
        return CE_Failure;
1816
0
    }
1817
1818
0
    char **papszFileList = GDALGetFileList(hDS);
1819
1820
0
    GDALClose(hDS);
1821
1822
0
    if (CSLCount(papszFileList) == 0)
1823
0
    {
1824
0
        CPLError(CE_Failure, CPLE_NotSupported,
1825
0
                 "Unable to determine files associated with %s,\n"
1826
0
                 "rename fails.",
1827
0
                 pszOldName);
1828
1829
0
        return CE_Failure;
1830
0
    }
1831
1832
    /* -------------------------------------------------------------------- */
1833
    /*      Produce a list of new filenames that correspond to the old      */
1834
    /*      names.                                                          */
1835
    /* -------------------------------------------------------------------- */
1836
0
    CPLErr eErr = CE_None;
1837
0
    char **papszNewFileList =
1838
0
        CPLCorrespondingPaths(pszOldName, pszNewName, papszFileList);
1839
1840
0
    if (papszNewFileList == nullptr)
1841
0
        return CE_Failure;
1842
1843
0
    for (int i = 0; papszFileList[i] != nullptr; ++i)
1844
0
    {
1845
0
        if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
1846
0
        {
1847
0
            eErr = CE_Failure;
1848
            // Try to put the ones we moved back.
1849
0
            for (--i; i >= 0; i--)
1850
0
            {
1851
                // Nothing we can do if the moving back doesn't work...
1852
0
                CPL_IGNORE_RET_VAL(
1853
0
                    CPLMoveFile(papszFileList[i], papszNewFileList[i]));
1854
0
            }
1855
0
            break;
1856
0
        }
1857
0
    }
1858
1859
0
    CSLDestroy(papszNewFileList);
1860
0
    CSLDestroy(papszFileList);
1861
1862
0
    return eErr;
1863
0
}
1864
1865
//! @endcond
1866
1867
/************************************************************************/
1868
/*                               Rename()                               */
1869
/************************************************************************/
1870
1871
/**
1872
 * \brief Rename a dataset.
1873
 *
1874
 * Rename a dataset. This may including moving the dataset to a new directory
1875
 * or even a new filesystem.
1876
 *
1877
 * It is unwise to have open dataset handles on this dataset when it is
1878
 * being renamed.
1879
 *
1880
 * Equivalent of the C function GDALRenameDataset().
1881
 *
1882
 * @param pszNewName new name for the dataset.
1883
 * @param pszOldName old name for the dataset.
1884
 *
1885
 * @return CE_None on success, or CE_Failure if the operation fails.
1886
 */
1887
1888
CPLErr GDALDriver::Rename(const char *pszNewName, const char *pszOldName)
1889
1890
0
{
1891
0
    pfnRename = GetRenameCallback();
1892
0
    if (pfnRename != nullptr)
1893
0
        return pfnRename(pszNewName, pszOldName);
1894
1895
0
    return DefaultRename(pszNewName, pszOldName);
1896
0
}
1897
1898
/************************************************************************/
1899
/*                         GDALRenameDataset()                          */
1900
/************************************************************************/
1901
1902
/**
1903
 * \brief Rename a dataset.
1904
 *
1905
 * @see GDALDriver::Rename()
1906
 */
1907
1908
CPLErr CPL_STDCALL GDALRenameDataset(GDALDriverH hDriver,
1909
                                     const char *pszNewName,
1910
                                     const char *pszOldName)
1911
1912
0
{
1913
0
    if (hDriver == nullptr)
1914
0
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
1915
1916
0
    if (hDriver == nullptr)
1917
0
    {
1918
0
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
1919
0
                 pszOldName);
1920
0
        return CE_Failure;
1921
0
    }
1922
1923
0
    return GDALDriver::FromHandle(hDriver)->Rename(pszNewName, pszOldName);
1924
0
}
1925
1926
/************************************************************************/
1927
/*                          DefaultCopyFiles()                          */
1928
/*                                                                      */
1929
/*      The default implementation based on file lists used when        */
1930
/*      there is no format specific implementation.                     */
1931
/************************************************************************/
1932
1933
//! @cond Doxygen_Suppress
1934
CPLErr GDALDriver::DefaultCopyFiles(const char *pszNewName,
1935
                                    const char *pszOldName)
1936
1937
0
{
1938
    /* -------------------------------------------------------------------- */
1939
    /*      Collect file list.                                              */
1940
    /* -------------------------------------------------------------------- */
1941
0
    GDALDatasetH hDS = GDALOpen(pszOldName, GA_ReadOnly);
1942
1943
0
    if (hDS == nullptr)
1944
0
    {
1945
0
        if (CPLGetLastErrorNo() == 0)
1946
0
            CPLError(CE_Failure, CPLE_OpenFailed,
1947
0
                     "Unable to open %s to obtain file list.", pszOldName);
1948
1949
0
        return CE_Failure;
1950
0
    }
1951
1952
0
    char **papszFileList = GDALGetFileList(hDS);
1953
1954
0
    GDALClose(hDS);
1955
0
    hDS = nullptr;
1956
1957
0
    if (CSLCount(papszFileList) == 0)
1958
0
    {
1959
0
        CPLError(CE_Failure, CPLE_NotSupported,
1960
0
                 "Unable to determine files associated with %s,\n"
1961
0
                 "rename fails.",
1962
0
                 pszOldName);
1963
1964
0
        return CE_Failure;
1965
0
    }
1966
1967
    /* -------------------------------------------------------------------- */
1968
    /*      Produce a list of new filenames that correspond to the old      */
1969
    /*      names.                                                          */
1970
    /* -------------------------------------------------------------------- */
1971
0
    CPLErr eErr = CE_None;
1972
0
    char **papszNewFileList =
1973
0
        CPLCorrespondingPaths(pszOldName, pszNewName, papszFileList);
1974
1975
0
    if (papszNewFileList == nullptr)
1976
0
        return CE_Failure;
1977
1978
0
    for (int i = 0; papszFileList[i] != nullptr; ++i)
1979
0
    {
1980
0
        if (CPLCopyFile(papszNewFileList[i], papszFileList[i]) != 0)
1981
0
        {
1982
0
            eErr = CE_Failure;
1983
            // Try to put the ones we moved back.
1984
0
            for (--i; i >= 0; --i)
1985
0
                VSIUnlink(papszNewFileList[i]);
1986
0
            break;
1987
0
        }
1988
0
    }
1989
1990
0
    CSLDestroy(papszNewFileList);
1991
0
    CSLDestroy(papszFileList);
1992
1993
0
    return eErr;
1994
0
}
1995
1996
//! @endcond
1997
1998
/************************************************************************/
1999
/*                             CopyFiles()                              */
2000
/************************************************************************/
2001
2002
/**
2003
 * \brief Copy the files of a dataset.
2004
 *
2005
 * Copy all the files associated with a dataset.
2006
 *
2007
 * Equivalent of the C function GDALCopyDatasetFiles().
2008
 *
2009
 * @param pszNewName new name for the dataset.
2010
 * @param pszOldName old name for the dataset.
2011
 *
2012
 * @return CE_None on success, or CE_Failure if the operation fails.
2013
 */
2014
2015
CPLErr GDALDriver::CopyFiles(const char *pszNewName, const char *pszOldName)
2016
2017
0
{
2018
0
    pfnCopyFiles = GetCopyFilesCallback();
2019
0
    if (pfnCopyFiles != nullptr)
2020
0
        return pfnCopyFiles(pszNewName, pszOldName);
2021
2022
0
    return DefaultCopyFiles(pszNewName, pszOldName);
2023
0
}
2024
2025
/************************************************************************/
2026
/*                        GDALCopyDatasetFiles()                        */
2027
/************************************************************************/
2028
2029
/**
2030
 * \brief Copy the files of a dataset.
2031
 *
2032
 * @see GDALDriver::CopyFiles()
2033
 */
2034
2035
CPLErr CPL_STDCALL GDALCopyDatasetFiles(GDALDriverH hDriver,
2036
                                        const char *pszNewName,
2037
                                        const char *pszOldName)
2038
2039
0
{
2040
0
    if (hDriver == nullptr)
2041
0
        hDriver = GDALIdentifyDriver(pszOldName, nullptr);
2042
2043
0
    if (hDriver == nullptr)
2044
0
    {
2045
0
        CPLError(CE_Failure, CPLE_AppDefined, "No identifiable driver for %s.",
2046
0
                 pszOldName);
2047
0
        return CE_Failure;
2048
0
    }
2049
2050
0
    return GDALDriver::FromHandle(hDriver)->CopyFiles(pszNewName, pszOldName);
2051
0
}
2052
2053
/************************************************************************/
2054
/*                       GDALDriverHasOpenOption()                      */
2055
/************************************************************************/
2056
2057
/**
2058
 * \brief Returns TRUE if the given open option is supported by the driver.
2059
 * @param hDriver the handle of the driver
2060
 * @param pszOpenOptionName name of the open option to be checked
2061
 * @return TRUE if the driver supports the open option
2062
 * @since GDAL 3.11
2063
 */
2064
bool GDALDriverHasOpenOption(GDALDriverH hDriver, const char *pszOpenOptionName)
2065
0
{
2066
0
    VALIDATE_POINTER1(hDriver, "GDALDriverHasOpenOption", false);
2067
0
    return GDALDriver::FromHandle(hDriver)->HasOpenOption(pszOpenOptionName);
2068
0
}
2069
2070
/************************************************************************/
2071
/*                       GDALGetDriverShortName()                       */
2072
/************************************************************************/
2073
2074
/**
2075
 * \brief Return the short name of a driver
2076
 *
2077
 * This is the string that can be
2078
 * passed to the GDALGetDriverByName() function.
2079
 *
2080
 * For the GeoTIFF driver, this is "GTiff"
2081
 *
2082
 * @param hDriver the handle of the driver
2083
 * @return the short name of the driver. The
2084
 *         returned string should not be freed and is owned by the driver.
2085
 */
2086
2087
const char *CPL_STDCALL GDALGetDriverShortName(GDALDriverH hDriver)
2088
2089
0
{
2090
0
    VALIDATE_POINTER1(hDriver, "GDALGetDriverShortName", nullptr);
2091
2092
0
    return GDALDriver::FromHandle(hDriver)->GetDescription();
2093
0
}
2094
2095
/************************************************************************/
2096
/*                       GDALGetDriverLongName()                        */
2097
/************************************************************************/
2098
2099
/**
2100
 * \brief Return the long name of a driver
2101
 *
2102
 * For the GeoTIFF driver, this is "GeoTIFF"
2103
 *
2104
 * @param hDriver the handle of the driver
2105
 * @return the long name of the driver or empty string. The
2106
 *         returned string should not be freed and is owned by the driver.
2107
 */
2108
2109
const char *CPL_STDCALL GDALGetDriverLongName(GDALDriverH hDriver)
2110
2111
0
{
2112
0
    VALIDATE_POINTER1(hDriver, "GDALGetDriverLongName", nullptr);
2113
2114
0
    const char *pszLongName =
2115
0
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_LONGNAME);
2116
2117
0
    if (pszLongName == nullptr)
2118
0
        return "";
2119
2120
0
    return pszLongName;
2121
0
}
2122
2123
/************************************************************************/
2124
/*                       GDALGetDriverHelpTopic()                       */
2125
/************************************************************************/
2126
2127
/**
2128
 * \brief Return the URL to the help that describes the driver
2129
 *
2130
 * That URL is relative to the GDAL documentation directory.
2131
 *
2132
 * For the GeoTIFF driver, this is "frmt_gtiff.html"
2133
 *
2134
 * @param hDriver the handle of the driver
2135
 * @return the URL to the help that describes the driver or NULL. The
2136
 *         returned string should not be freed and is owned by the driver.
2137
 */
2138
2139
const char *CPL_STDCALL GDALGetDriverHelpTopic(GDALDriverH hDriver)
2140
2141
0
{
2142
0
    VALIDATE_POINTER1(hDriver, "GDALGetDriverHelpTopic", nullptr);
2143
2144
0
    return GDALDriver::FromHandle(hDriver)->GetMetadataItem(GDAL_DMD_HELPTOPIC);
2145
0
}
2146
2147
/************************************************************************/
2148
/*                   GDALGetDriverCreationOptionList()                  */
2149
/************************************************************************/
2150
2151
/**
2152
 * \brief Return the list of creation options of the driver
2153
 *
2154
 * Return the list of creation options of the driver used by Create() and
2155
 * CreateCopy() as an XML string
2156
 *
2157
 * @param hDriver the handle of the driver
2158
 * @return an XML string that describes the list of creation options or
2159
 *         empty string. The returned string should not be freed and is
2160
 *         owned by the driver.
2161
 */
2162
2163
const char *CPL_STDCALL GDALGetDriverCreationOptionList(GDALDriverH hDriver)
2164
2165
0
{
2166
0
    VALIDATE_POINTER1(hDriver, "GDALGetDriverCreationOptionList", nullptr);
2167
2168
0
    const char *pszOptionList =
2169
0
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
2170
0
            GDAL_DMD_CREATIONOPTIONLIST);
2171
2172
0
    if (pszOptionList == nullptr)
2173
0
        return "";
2174
2175
0
    return pszOptionList;
2176
0
}
2177
2178
/************************************************************************/
2179
/*                   GDALValidateCreationOptions()                      */
2180
/************************************************************************/
2181
2182
/**
2183
 * \brief Validate the list of creation options that are handled by a driver
2184
 *
2185
 * This is a helper method primarily used by Create() and
2186
 * CreateCopy() to validate that the passed in list of creation options
2187
 * is compatible with the GDAL_DMD_CREATIONOPTIONLIST metadata item defined
2188
 * by some drivers. @see GDALGetDriverCreationOptionList()
2189
 *
2190
 * If the GDAL_DMD_CREATIONOPTIONLIST metadata item is not defined, this
2191
 * function will return TRUE. Otherwise it will check that the keys and values
2192
 * in the list of creation options are compatible with the capabilities declared
2193
 * by the GDAL_DMD_CREATIONOPTIONLIST metadata item. In case of incompatibility
2194
 * a (non fatal) warning will be emitted and FALSE will be returned.
2195
 *
2196
 * @param hDriver the handle of the driver with whom the lists of creation
2197
 * option must be validated
2198
 * @param papszCreationOptions the list of creation options. An array of
2199
 * strings, whose last element is a NULL pointer
2200
 * @return TRUE if the list of creation options is compatible with the Create()
2201
 *         and CreateCopy() method of the driver, FALSE otherwise.
2202
 */
2203
2204
int CPL_STDCALL GDALValidateCreationOptions(GDALDriverH hDriver,
2205
                                            CSLConstList papszCreationOptions)
2206
0
{
2207
0
    VALIDATE_POINTER1(hDriver, "GDALValidateCreationOptions", FALSE);
2208
0
    const char *pszOptionList =
2209
0
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
2210
0
            GDAL_DMD_CREATIONOPTIONLIST);
2211
0
    CPLString osDriver;
2212
0
    osDriver.Printf("driver %s",
2213
0
                    GDALDriver::FromHandle(hDriver)->GetDescription());
2214
0
    bool bFoundOptionToRemove = false;
2215
0
    constexpr const char *const apszExcludedOptions[] = {
2216
0
        "APPEND_SUBDATASET", "COPY_SRC_MDD", "SRC_MDD", "SKIP_HOLES"};
2217
0
    for (const char *pszCO : cpl::Iterate(papszCreationOptions))
2218
0
    {
2219
0
        for (const char *pszExcludedOptions : apszExcludedOptions)
2220
0
        {
2221
0
            if (STARTS_WITH_CI(pszCO, pszExcludedOptions) &&
2222
0
                pszCO[strlen(pszExcludedOptions)] == '=')
2223
0
            {
2224
0
                bFoundOptionToRemove = true;
2225
0
                break;
2226
0
            }
2227
0
        }
2228
0
        if (bFoundOptionToRemove)
2229
0
            break;
2230
0
    }
2231
0
    CSLConstList papszOptionsToValidate = papszCreationOptions;
2232
0
    char **papszOptionsToFree = nullptr;
2233
0
    if (bFoundOptionToRemove)
2234
0
    {
2235
0
        for (const char *pszCO : cpl::Iterate(papszCreationOptions))
2236
0
        {
2237
0
            bool bMatch = false;
2238
0
            for (const char *pszExcludedOptions : apszExcludedOptions)
2239
0
            {
2240
0
                if (STARTS_WITH_CI(pszCO, pszExcludedOptions) &&
2241
0
                    pszCO[strlen(pszExcludedOptions)] == '=')
2242
0
                {
2243
0
                    bMatch = true;
2244
0
                    break;
2245
0
                }
2246
0
            }
2247
0
            if (!bMatch)
2248
0
                papszOptionsToFree = CSLAddString(papszOptionsToFree, pszCO);
2249
0
        }
2250
0
        papszOptionsToValidate = papszOptionsToFree;
2251
0
    }
2252
2253
0
    const bool bRet = CPL_TO_BOOL(GDALValidateOptions(
2254
0
        pszOptionList, papszOptionsToValidate, "creation option", osDriver));
2255
0
    CSLDestroy(papszOptionsToFree);
2256
0
    return bRet;
2257
0
}
2258
2259
/************************************************************************/
2260
/*                     GDALValidateOpenOptions()                        */
2261
/************************************************************************/
2262
2263
int GDALValidateOpenOptions(GDALDriverH hDriver,
2264
                            const char *const *papszOpenOptions)
2265
0
{
2266
0
    VALIDATE_POINTER1(hDriver, "GDALValidateOpenOptions", FALSE);
2267
0
    const char *pszOptionList =
2268
0
        GDALDriver::FromHandle(hDriver)->GetMetadataItem(
2269
0
            GDAL_DMD_OPENOPTIONLIST);
2270
0
    CPLString osDriver;
2271
0
    osDriver.Printf("driver %s",
2272
0
                    GDALDriver::FromHandle(hDriver)->GetDescription());
2273
0
    return GDALValidateOptions(pszOptionList, papszOpenOptions, "open option",
2274
0
                               osDriver);
2275
0
}
2276
2277
/************************************************************************/
2278
/*                           GDALValidateOptions()                      */
2279
/************************************************************************/
2280
2281
int GDALValidateOptions(const char *pszOptionList,
2282
                        const char *const *papszOptionsToValidate,
2283
                        const char *pszErrorMessageOptionType,
2284
                        const char *pszErrorMessageContainerName)
2285
0
{
2286
0
    if (papszOptionsToValidate == nullptr || *papszOptionsToValidate == nullptr)
2287
0
        return TRUE;
2288
0
    if (pszOptionList == nullptr)
2289
0
        return TRUE;
2290
2291
0
    CPLXMLNode *psNode = CPLParseXMLString(pszOptionList);
2292
0
    if (psNode == nullptr)
2293
0
    {
2294
0
        CPLError(CE_Warning, CPLE_AppDefined,
2295
0
                 "Could not parse %s list of %s. Assuming options are valid.",
2296
0
                 pszErrorMessageOptionType, pszErrorMessageContainerName);
2297
0
        return TRUE;
2298
0
    }
2299
2300
0
    bool bRet = true;
2301
0
    while (*papszOptionsToValidate)
2302
0
    {
2303
0
        char *pszKey = nullptr;
2304
0
        const char *pszValue =
2305
0
            CPLParseNameValue(*papszOptionsToValidate, &pszKey);
2306
0
        if (pszKey == nullptr)
2307
0
        {
2308
0
            CPLError(CE_Warning, CPLE_NotSupported,
2309
0
                     "%s '%s' is not formatted with the key=value format",
2310
0
                     pszErrorMessageOptionType, *papszOptionsToValidate);
2311
0
            bRet = false;
2312
2313
0
            ++papszOptionsToValidate;
2314
0
            continue;
2315
0
        }
2316
2317
0
        if (EQUAL(pszKey, "VALIDATE_OPEN_OPTIONS"))
2318
0
        {
2319
0
            ++papszOptionsToValidate;
2320
0
            CPLFree(pszKey);
2321
0
            continue;
2322
0
        }
2323
2324
        // Must we be forgiving in case of missing option ?
2325
0
        bool bWarnIfMissingKey = true;
2326
0
        if (pszKey[0] == '@')
2327
0
        {
2328
0
            bWarnIfMissingKey = false;
2329
0
            memmove(pszKey, pszKey + 1, strlen(pszKey + 1) + 1);
2330
0
        }
2331
2332
0
        CPLXMLNode *psChildNode = psNode->psChild;
2333
0
        while (psChildNode)
2334
0
        {
2335
0
            if (EQUAL(psChildNode->pszValue, "OPTION"))
2336
0
            {
2337
0
                const char *pszOptionName =
2338
0
                    CPLGetXMLValue(psChildNode, "name", "");
2339
                /* For option names terminated by wildcard (NITF BLOCKA option
2340
                 * names for example) */
2341
0
                if (strlen(pszOptionName) > 0 &&
2342
0
                    pszOptionName[strlen(pszOptionName) - 1] == '*' &&
2343
0
                    EQUALN(pszOptionName, pszKey, strlen(pszOptionName) - 1))
2344
0
                {
2345
0
                    break;
2346
0
                }
2347
2348
                /* For option names beginning by a wildcard */
2349
0
                if (pszOptionName[0] == '*' &&
2350
0
                    strlen(pszKey) > strlen(pszOptionName) &&
2351
0
                    EQUAL(pszKey + strlen(pszKey) - strlen(pszOptionName + 1),
2352
0
                          pszOptionName + 1))
2353
0
                {
2354
0
                    break;
2355
0
                }
2356
2357
                // For options names with * in the middle
2358
0
                const char *pszStarInOptionName = strchr(pszOptionName, '*');
2359
0
                if (pszStarInOptionName &&
2360
0
                    pszStarInOptionName != pszOptionName &&
2361
0
                    pszStarInOptionName !=
2362
0
                        pszOptionName + strlen(pszOptionName) - 1 &&
2363
0
                    strlen(pszKey) > static_cast<size_t>(pszStarInOptionName -
2364
0
                                                         pszOptionName) &&
2365
0
                    EQUALN(pszKey, pszOptionName,
2366
0
                           static_cast<size_t>(pszStarInOptionName -
2367
0
                                               pszOptionName)) &&
2368
0
                    EQUAL(pszKey +
2369
0
                              static_cast<size_t>(pszStarInOptionName -
2370
0
                                                  pszOptionName) +
2371
0
                              1,
2372
0
                          pszStarInOptionName + 1))
2373
0
                {
2374
0
                    break;
2375
0
                }
2376
2377
0
                if (EQUAL(pszOptionName, pszKey))
2378
0
                {
2379
0
                    break;
2380
0
                }
2381
0
                const char *pszAlias = CPLGetXMLValue(
2382
0
                    psChildNode, "alias",
2383
0
                    CPLGetXMLValue(psChildNode, "deprecated_alias", ""));
2384
0
                if (EQUAL(pszAlias, pszKey))
2385
0
                {
2386
0
                    CPLDebug("GDAL",
2387
0
                             "Using deprecated alias '%s'. New name is '%s'",
2388
0
                             pszAlias, pszOptionName);
2389
0
                    break;
2390
0
                }
2391
0
            }
2392
0
            psChildNode = psChildNode->psNext;
2393
0
        }
2394
0
        if (psChildNode == nullptr)
2395
0
        {
2396
0
            if (bWarnIfMissingKey &&
2397
0
                (!EQUAL(pszErrorMessageOptionType, "open option") ||
2398
0
                 CPLFetchBool(papszOptionsToValidate, "VALIDATE_OPEN_OPTIONS",
2399
0
                              true)))
2400
0
            {
2401
0
                CPLError(CE_Warning, CPLE_NotSupported,
2402
0
                         "%s does not support %s %s",
2403
0
                         pszErrorMessageContainerName,
2404
0
                         pszErrorMessageOptionType, pszKey);
2405
0
                bRet = false;
2406
0
            }
2407
2408
0
            CPLFree(pszKey);
2409
0
            ++papszOptionsToValidate;
2410
0
            continue;
2411
0
        }
2412
2413
0
#ifdef DEBUG
2414
0
        CPLXMLNode *psChildSubNode = psChildNode->psChild;
2415
0
        while (psChildSubNode)
2416
0
        {
2417
0
            if (psChildSubNode->eType == CXT_Attribute)
2418
0
            {
2419
0
                if (!(EQUAL(psChildSubNode->pszValue, "name") ||
2420
0
                      EQUAL(psChildSubNode->pszValue, "alias") ||
2421
0
                      EQUAL(psChildSubNode->pszValue, "deprecated_alias") ||
2422
0
                      EQUAL(psChildSubNode->pszValue, "alt_config_option") ||
2423
0
                      EQUAL(psChildSubNode->pszValue, "description") ||
2424
0
                      EQUAL(psChildSubNode->pszValue, "type") ||
2425
0
                      EQUAL(psChildSubNode->pszValue, "min") ||
2426
0
                      EQUAL(psChildSubNode->pszValue, "max") ||
2427
0
                      EQUAL(psChildSubNode->pszValue, "default") ||
2428
0
                      EQUAL(psChildSubNode->pszValue, "maxsize") ||
2429
0
                      EQUAL(psChildSubNode->pszValue, "required") ||
2430
0
                      EQUAL(psChildSubNode->pszValue, "scope")))
2431
0
                {
2432
                    /* Driver error */
2433
0
                    CPLError(CE_Warning, CPLE_NotSupported,
2434
0
                             "%s : unhandled attribute '%s' for %s %s.",
2435
0
                             pszErrorMessageContainerName,
2436
0
                             psChildSubNode->pszValue, pszKey,
2437
0
                             pszErrorMessageOptionType);
2438
0
                }
2439
0
            }
2440
0
            psChildSubNode = psChildSubNode->psNext;
2441
0
        }
2442
0
#endif
2443
2444
0
        const char *pszType = CPLGetXMLValue(psChildNode, "type", nullptr);
2445
0
        const char *pszMin = CPLGetXMLValue(psChildNode, "min", nullptr);
2446
0
        const char *pszMax = CPLGetXMLValue(psChildNode, "max", nullptr);
2447
0
        if (pszType != nullptr)
2448
0
        {
2449
0
            if (EQUAL(pszType, "INT") || EQUAL(pszType, "INTEGER"))
2450
0
            {
2451
0
                const char *pszValueIter = pszValue;
2452
0
                while (*pszValueIter)
2453
0
                {
2454
0
                    if (!((*pszValueIter >= '0' && *pszValueIter <= '9') ||
2455
0
                          *pszValueIter == '+' || *pszValueIter == '-'))
2456
0
                    {
2457
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2458
0
                                 "'%s' is an unexpected value for %s %s of "
2459
0
                                 "type int.",
2460
0
                                 pszValue, pszKey, pszErrorMessageOptionType);
2461
0
                        bRet = false;
2462
0
                        break;
2463
0
                    }
2464
0
                    ++pszValueIter;
2465
0
                }
2466
0
                if (*pszValueIter == '\0')
2467
0
                {
2468
0
                    if (pszMin && atoi(pszValue) < atoi(pszMin))
2469
0
                    {
2470
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2471
0
                                 "'%s' is an unexpected value for %s %s that "
2472
0
                                 "should be >= %s.",
2473
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2474
0
                                 pszMin);
2475
0
                        bRet = false;
2476
0
                    }
2477
0
                    if (pszMax && atoi(pszValue) > atoi(pszMax))
2478
0
                    {
2479
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2480
0
                                 "'%s' is an unexpected value for %s %s that "
2481
0
                                 "should be <= %s.",
2482
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2483
0
                                 pszMax);
2484
0
                        bRet = false;
2485
0
                    }
2486
0
                }
2487
0
            }
2488
0
            else if (EQUAL(pszType, "UNSIGNED INT"))
2489
0
            {
2490
0
                const char *pszValueIter = pszValue;
2491
0
                while (*pszValueIter)
2492
0
                {
2493
0
                    if (!((*pszValueIter >= '0' && *pszValueIter <= '9') ||
2494
0
                          *pszValueIter == '+'))
2495
0
                    {
2496
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2497
0
                                 "'%s' is an unexpected value for %s %s of "
2498
0
                                 "type unsigned int.",
2499
0
                                 pszValue, pszKey, pszErrorMessageOptionType);
2500
0
                        bRet = false;
2501
0
                        break;
2502
0
                    }
2503
0
                    ++pszValueIter;
2504
0
                }
2505
0
                if (*pszValueIter == '\0')
2506
0
                {
2507
0
                    if (pszMin && atoi(pszValue) < atoi(pszMin))
2508
0
                    {
2509
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2510
0
                                 "'%s' is an unexpected value for %s %s that "
2511
0
                                 "should be >= %s.",
2512
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2513
0
                                 pszMin);
2514
0
                        bRet = false;
2515
0
                    }
2516
0
                    if (pszMax && atoi(pszValue) > atoi(pszMax))
2517
0
                    {
2518
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2519
0
                                 "'%s' is an unexpected value for %s %s that "
2520
0
                                 "should be <= %s.",
2521
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2522
0
                                 pszMax);
2523
0
                        bRet = false;
2524
0
                    }
2525
0
                }
2526
0
            }
2527
0
            else if (EQUAL(pszType, "FLOAT"))
2528
0
            {
2529
0
                char *endPtr = nullptr;
2530
0
                double dfVal = CPLStrtod(pszValue, &endPtr);
2531
0
                if (!(endPtr == nullptr || *endPtr == '\0'))
2532
0
                {
2533
0
                    CPLError(
2534
0
                        CE_Warning, CPLE_NotSupported,
2535
0
                        "'%s' is an unexpected value for %s %s of type float.",
2536
0
                        pszValue, pszKey, pszErrorMessageOptionType);
2537
0
                    bRet = false;
2538
0
                }
2539
0
                else
2540
0
                {
2541
0
                    if (pszMin && dfVal < CPLAtof(pszMin))
2542
0
                    {
2543
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2544
0
                                 "'%s' is an unexpected value for %s %s that "
2545
0
                                 "should be >= %s.",
2546
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2547
0
                                 pszMin);
2548
0
                        bRet = false;
2549
0
                    }
2550
0
                    if (pszMax && dfVal > CPLAtof(pszMax))
2551
0
                    {
2552
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2553
0
                                 "'%s' is an unexpected value for %s %s that "
2554
0
                                 "should be <= %s.",
2555
0
                                 pszValue, pszKey, pszErrorMessageOptionType,
2556
0
                                 pszMax);
2557
0
                        bRet = false;
2558
0
                    }
2559
0
                }
2560
0
            }
2561
0
            else if (EQUAL(pszType, "BOOLEAN"))
2562
0
            {
2563
0
                if (!(EQUAL(pszValue, "ON") || EQUAL(pszValue, "TRUE") ||
2564
0
                      EQUAL(pszValue, "YES") || EQUAL(pszValue, "OFF") ||
2565
0
                      EQUAL(pszValue, "FALSE") || EQUAL(pszValue, "NO")))
2566
0
                {
2567
0
                    CPLError(CE_Warning, CPLE_NotSupported,
2568
0
                             "'%s' is an unexpected value for %s %s of type "
2569
0
                             "boolean.",
2570
0
                             pszValue, pszKey, pszErrorMessageOptionType);
2571
0
                    bRet = false;
2572
0
                }
2573
0
            }
2574
0
            else if (EQUAL(pszType, "STRING-SELECT"))
2575
0
            {
2576
0
                bool bMatchFound = false;
2577
0
                CPLXMLNode *psStringSelect = psChildNode->psChild;
2578
0
                while (psStringSelect)
2579
0
                {
2580
0
                    if (psStringSelect->eType == CXT_Element &&
2581
0
                        EQUAL(psStringSelect->pszValue, "Value"))
2582
0
                    {
2583
0
                        CPLXMLNode *psOptionNode = psStringSelect->psChild;
2584
0
                        while (psOptionNode)
2585
0
                        {
2586
0
                            if (psOptionNode->eType == CXT_Text &&
2587
0
                                EQUAL(psOptionNode->pszValue, pszValue))
2588
0
                            {
2589
0
                                bMatchFound = true;
2590
0
                                break;
2591
0
                            }
2592
0
                            if (psOptionNode->eType == CXT_Attribute &&
2593
0
                                (EQUAL(psOptionNode->pszValue, "alias") ||
2594
0
                                 EQUAL(psOptionNode->pszValue,
2595
0
                                       "deprecated_alias")) &&
2596
0
                                EQUAL(psOptionNode->psChild->pszValue,
2597
0
                                      pszValue))
2598
0
                            {
2599
0
                                bMatchFound = true;
2600
0
                                break;
2601
0
                            }
2602
0
                            psOptionNode = psOptionNode->psNext;
2603
0
                        }
2604
0
                        if (bMatchFound)
2605
0
                            break;
2606
0
                    }
2607
0
                    psStringSelect = psStringSelect->psNext;
2608
0
                }
2609
0
                if (!bMatchFound)
2610
0
                {
2611
0
                    CPLError(CE_Warning, CPLE_NotSupported,
2612
0
                             "'%s' is an unexpected value for %s %s of type "
2613
0
                             "string-select.",
2614
0
                             pszValue, pszKey, pszErrorMessageOptionType);
2615
0
                    bRet = false;
2616
0
                }
2617
0
            }
2618
0
            else if (EQUAL(pszType, "STRING"))
2619
0
            {
2620
0
                const char *pszMaxSize =
2621
0
                    CPLGetXMLValue(psChildNode, "maxsize", nullptr);
2622
0
                if (pszMaxSize != nullptr)
2623
0
                {
2624
0
                    if (static_cast<int>(strlen(pszValue)) > atoi(pszMaxSize))
2625
0
                    {
2626
0
                        CPLError(CE_Warning, CPLE_NotSupported,
2627
0
                                 "'%s' is of size %d, whereas maximum size for "
2628
0
                                 "%s %s is %d.",
2629
0
                                 pszValue, static_cast<int>(strlen(pszValue)),
2630
0
                                 pszKey, pszErrorMessageOptionType,
2631
0
                                 atoi(pszMaxSize));
2632
0
                        bRet = false;
2633
0
                    }
2634
0
                }
2635
0
            }
2636
0
            else
2637
0
            {
2638
                /* Driver error */
2639
0
                CPLError(CE_Warning, CPLE_NotSupported,
2640
0
                         "%s : type '%s' for %s %s is not recognized.",
2641
0
                         pszErrorMessageContainerName, pszType, pszKey,
2642
0
                         pszErrorMessageOptionType);
2643
0
            }
2644
0
        }
2645
0
        else
2646
0
        {
2647
            /* Driver error */
2648
0
            CPLError(CE_Warning, CPLE_NotSupported, "%s : no type for %s %s.",
2649
0
                     pszErrorMessageContainerName, pszKey,
2650
0
                     pszErrorMessageOptionType);
2651
0
        }
2652
0
        CPLFree(pszKey);
2653
0
        ++papszOptionsToValidate;
2654
0
    }
2655
2656
0
    CPLDestroyXMLNode(psNode);
2657
0
    return bRet ? TRUE : FALSE;
2658
0
}
2659
2660
/************************************************************************/
2661
/*                         GDALIdentifyDriver()                         */
2662
/************************************************************************/
2663
2664
/**
2665
 * \brief Identify the driver that can open a dataset.
2666
 *
2667
 * This function will try to identify the driver that can open the passed file
2668
 * name by invoking the Identify method of each registered GDALDriver in turn.
2669
 * The first driver that successfully identifies the file name will be returned.
2670
 * If all drivers fail then NULL is returned.
2671
 *
2672
 * In order to reduce the need for such searches to touch the operating system
2673
 * file system machinery, it is possible to give an optional list of files.
2674
 * This is the list of all files at the same level in the file system as the
2675
 * target file, including the target file. The filenames will not include any
2676
 * path components, and are essentially just the output of VSIReadDir() on the
2677
 * parent directory. If the target object does not have filesystem semantics
2678
 * then the file list should be NULL.
2679
 *
2680
 * @param pszFilename the name of the file to access.  In the case of
2681
 * exotic drivers this may not refer to a physical file, but instead contain
2682
 * information for the driver on how to access a dataset.
2683
 *
2684
 * @param papszFileList an array of strings, whose last element is the NULL
2685
 * pointer.  These strings are filenames that are auxiliary to the main
2686
 * filename. The passed value may be NULL.
2687
 *
2688
 * @return A GDALDriverH handle or NULL on failure.  For C++ applications
2689
 * this handle can be cast to a GDALDriver *.
2690
 */
2691
2692
GDALDriverH CPL_STDCALL GDALIdentifyDriver(const char *pszFilename,
2693
                                           CSLConstList papszFileList)
2694
2695
0
{
2696
0
    return GDALIdentifyDriverEx(pszFilename, 0, nullptr, papszFileList);
2697
0
}
2698
2699
/************************************************************************/
2700
/*                         GDALIdentifyDriverEx()                       */
2701
/************************************************************************/
2702
2703
/**
2704
 * \brief Identify the driver that can open a dataset.
2705
 *
2706
 * This function will try to identify the driver that can open the passed file
2707
 * name by invoking the Identify method of each registered GDALDriver in turn.
2708
 * The first driver that successfully identifies the file name will be returned.
2709
 * If all drivers fail then NULL is returned.
2710
 *
2711
 * In order to reduce the need for such searches to touch the operating system
2712
 * file system machinery, it is possible to give an optional list of files.
2713
 * This is the list of all files at the same level in the file system as the
2714
 * target file, including the target file. The filenames will not include any
2715
 * path components, and are essentially just the output of VSIReadDir() on the
2716
 * parent directory. If the target object does not have filesystem semantics
2717
 * then the file list should be NULL.
2718
 *
2719
 * @param pszFilename the name of the file to access.  In the case of
2720
 * exotic drivers this may not refer to a physical file, but instead contain
2721
 * information for the driver on how to access a dataset.
2722
 *
2723
 * @param nIdentifyFlags a combination of GDAL_OF_RASTER for raster drivers
2724
 * or GDAL_OF_VECTOR for vector drivers. If none of the value is specified,
2725
 * both kinds are implied.
2726
 *
2727
 * @param papszAllowedDrivers NULL to consider all candidate drivers, or a NULL
2728
 * terminated list of strings with the driver short names that must be
2729
 * considered.
2730
 *
2731
 * @param papszFileList an array of strings, whose last element is the NULL
2732
 * pointer.  These strings are filenames that are auxiliary to the main
2733
 * filename. The passed value may be NULL.
2734
 *
2735
 * @return A GDALDriverH handle or NULL on failure.  For C++ applications
2736
 * this handle can be cast to a GDALDriver *.
2737
 */
2738
2739
GDALDriverH CPL_STDCALL GDALIdentifyDriverEx(
2740
    const char *pszFilename, unsigned int nIdentifyFlags,
2741
    const char *const *papszAllowedDrivers, const char *const *papszFileList)
2742
0
{
2743
0
    GDALDriverManager *poDM = GetGDALDriverManager();
2744
0
    CPLAssert(nullptr != poDM);
2745
2746
    // If no driver kind is specified, assume all are to be probed.
2747
0
    if ((nIdentifyFlags & GDAL_OF_KIND_MASK) == 0)
2748
0
        nIdentifyFlags |= GDAL_OF_KIND_MASK & ~GDAL_OF_MULTIDIM_RASTER;
2749
2750
0
    GDALOpenInfo oOpenInfo(pszFilename, nIdentifyFlags, papszFileList);
2751
0
    oOpenInfo.papszAllowedDrivers = papszAllowedDrivers;
2752
2753
0
    CPLErrorStateBackuper oBackuper;
2754
0
    CPLErrorSetState(CE_None, CPLE_AppDefined, "");
2755
2756
0
    const int nDriverCount = poDM->GetDriverCount();
2757
2758
    // First pass: only use drivers that have a pfnIdentify implementation.
2759
0
    std::vector<GDALDriver *> apoSecondPassDrivers;
2760
0
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
2761
0
    {
2762
0
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
2763
0
        if (papszAllowedDrivers != nullptr &&
2764
0
            CSLFindString(papszAllowedDrivers,
2765
0
                          GDALGetDriverShortName(poDriver)) == -1)
2766
0
        {
2767
0
            continue;
2768
0
        }
2769
2770
0
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
2771
2772
0
        if (poDriver->pfnIdentify == nullptr &&
2773
0
            poDriver->pfnIdentifyEx == nullptr)
2774
0
        {
2775
0
            continue;
2776
0
        }
2777
2778
0
        if (papszAllowedDrivers != nullptr &&
2779
0
            CSLFindString(papszAllowedDrivers,
2780
0
                          GDALGetDriverShortName(poDriver)) == -1)
2781
0
            continue;
2782
0
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
2783
0
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
2784
0
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
2785
0
            continue;
2786
0
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
2787
0
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
2788
0
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
2789
0
            continue;
2790
2791
0
        if (poDriver->pfnIdentifyEx)
2792
0
        {
2793
0
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) > 0)
2794
0
                return poDriver;
2795
0
        }
2796
0
        else
2797
0
        {
2798
0
            const int nIdentifyRes = poDriver->pfnIdentify(&oOpenInfo);
2799
0
            if (nIdentifyRes > 0)
2800
0
                return poDriver;
2801
0
            if (nIdentifyRes < 0 &&
2802
0
                poDriver->GetMetadataItem("IS_NON_LOADED_PLUGIN"))
2803
0
            {
2804
                // Not loaded plugin
2805
0
                apoSecondPassDrivers.push_back(poDriver);
2806
0
            }
2807
0
        }
2808
0
    }
2809
2810
    // second pass: try loading plugin drivers
2811
0
    for (auto poDriver : apoSecondPassDrivers)
2812
0
    {
2813
        // Force plugin driver loading
2814
0
        poDriver->GetMetadata();
2815
0
        if (poDriver->pfnIdentify(&oOpenInfo) > 0)
2816
0
            return poDriver;
2817
0
    }
2818
2819
    // third pass: slow method.
2820
0
    for (int iDriver = 0; iDriver < nDriverCount; ++iDriver)
2821
0
    {
2822
0
        GDALDriver *poDriver = poDM->GetDriver(iDriver);
2823
0
        if (papszAllowedDrivers != nullptr &&
2824
0
            CSLFindString(papszAllowedDrivers,
2825
0
                          GDALGetDriverShortName(poDriver)) == -1)
2826
0
        {
2827
0
            continue;
2828
0
        }
2829
2830
0
        VALIDATE_POINTER1(poDriver, "GDALIdentifyDriver", nullptr);
2831
2832
0
        if ((nIdentifyFlags & GDAL_OF_RASTER) != 0 &&
2833
0
            (nIdentifyFlags & GDAL_OF_VECTOR) == 0 &&
2834
0
            poDriver->GetMetadataItem(GDAL_DCAP_RASTER) == nullptr)
2835
0
            continue;
2836
0
        if ((nIdentifyFlags & GDAL_OF_VECTOR) != 0 &&
2837
0
            (nIdentifyFlags & GDAL_OF_RASTER) == 0 &&
2838
0
            poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) == nullptr)
2839
0
            continue;
2840
2841
0
        if (poDriver->pfnIdentifyEx != nullptr)
2842
0
        {
2843
0
            if (poDriver->pfnIdentifyEx(poDriver, &oOpenInfo) == 0)
2844
0
                continue;
2845
0
        }
2846
0
        else if (poDriver->pfnIdentify != nullptr)
2847
0
        {
2848
0
            if (poDriver->pfnIdentify(&oOpenInfo) == 0)
2849
0
                continue;
2850
0
        }
2851
2852
0
        GDALDataset *poDS;
2853
0
        if (poDriver->pfnOpen != nullptr)
2854
0
        {
2855
0
            poDS = poDriver->pfnOpen(&oOpenInfo);
2856
0
            if (poDS != nullptr)
2857
0
            {
2858
0
                delete poDS;
2859
0
                return GDALDriver::ToHandle(poDriver);
2860
0
            }
2861
2862
0
            if (CPLGetLastErrorType() != CE_None)
2863
0
                return nullptr;
2864
0
        }
2865
0
        else if (poDriver->pfnOpenWithDriverArg != nullptr)
2866
0
        {
2867
0
            poDS = poDriver->pfnOpenWithDriverArg(poDriver, &oOpenInfo);
2868
0
            if (poDS != nullptr)
2869
0
            {
2870
0
                delete poDS;
2871
0
                return GDALDriver::ToHandle(poDriver);
2872
0
            }
2873
2874
0
            if (CPLGetLastErrorType() != CE_None)
2875
0
                return nullptr;
2876
0
        }
2877
0
    }
2878
2879
0
    return nullptr;
2880
0
}
2881
2882
/************************************************************************/
2883
/*                          GetMetadataItem()                           */
2884
/************************************************************************/
2885
2886
const char *GDALDriver::GetMetadataItem(const char *pszName,
2887
                                        const char *pszDomain)
2888
0
{
2889
0
    if (pszDomain == nullptr || pszDomain[0] == '\0')
2890
0
    {
2891
0
        if (EQUAL(pszName, GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST))
2892
0
        {
2893
0
            const char *pszVal = GDALMajorObject::GetMetadataItem(pszName, "");
2894
0
            if (pszVal)
2895
0
                return pszVal;
2896
0
            if (GetMetadataItem(GDAL_DCAP_RASTER))
2897
0
            {
2898
0
                auto poDM = GetGDALDriverManager();
2899
0
                auto poGTiffDrv = poDM->GetDriverByName("GTiff");
2900
0
                if (poGTiffDrv)
2901
0
                {
2902
0
                    const char *pszXML =
2903
0
                        poGTiffDrv->GetMetadataItem(pszName, "");
2904
0
                    if (pszXML)
2905
0
                    {
2906
0
                        CPLString osXML(pszXML);
2907
0
                        osXML.replaceAll("<Value>INTERNAL</Value>", "");
2908
0
                        return CPLSPrintf("%s", osXML.c_str());
2909
0
                    }
2910
0
                }
2911
0
            }
2912
0
        }
2913
0
    }
2914
0
    return GDALMajorObject::GetMetadataItem(pszName, pszDomain);
2915
0
}
2916
2917
/************************************************************************/
2918
/*                          SetMetadataItem()                           */
2919
/************************************************************************/
2920
2921
CPLErr GDALDriver::SetMetadataItem(const char *pszName, const char *pszValue,
2922
                                   const char *pszDomain)
2923
2924
0
{
2925
0
    if (pszDomain == nullptr || pszDomain[0] == '\0')
2926
0
    {
2927
        /* Automatically sets GDAL_DMD_EXTENSIONS from GDAL_DMD_EXTENSION */
2928
0
        if (EQUAL(pszName, GDAL_DMD_EXTENSION) &&
2929
0
            GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSIONS) == nullptr)
2930
0
        {
2931
0
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSIONS, pszValue);
2932
0
        }
2933
        /* and vice-versa if there is a single extension in GDAL_DMD_EXTENSIONS */
2934
0
        else if (EQUAL(pszName, GDAL_DMD_EXTENSIONS) &&
2935
0
                 strchr(pszValue, ' ') == nullptr &&
2936
0
                 GDALMajorObject::GetMetadataItem(GDAL_DMD_EXTENSION) ==
2937
0
                     nullptr)
2938
0
        {
2939
0
            GDALMajorObject::SetMetadataItem(GDAL_DMD_EXTENSION, pszValue);
2940
0
        }
2941
0
    }
2942
0
    return GDALMajorObject::SetMetadataItem(pszName, pszValue, pszDomain);
2943
0
}
2944
2945
/************************************************************************/
2946
/*                         InstantiateAlgorithm()                       */
2947
/************************************************************************/
2948
2949
//! @cond Doxygen_Suppress
2950
2951
GDALAlgorithm *
2952
GDALDriver::InstantiateAlgorithm(const std::vector<std::string> &aosPath)
2953
0
{
2954
0
    pfnInstantiateAlgorithm = GetInstantiateAlgorithmCallback();
2955
0
    if (pfnInstantiateAlgorithm)
2956
0
        return pfnInstantiateAlgorithm(aosPath);
2957
0
    return nullptr;
2958
0
}
2959
2960
/************************************************************************/
2961
/*                        DeclareAlgorithm()                            */
2962
/************************************************************************/
2963
2964
void GDALDriver::DeclareAlgorithm(const std::vector<std::string> &aosPath)
2965
0
{
2966
0
    const std::string osDriverName = GetDescription();
2967
0
    auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
2968
2969
0
    if (!singleton.HasDeclaredSubAlgorithm({"driver"}))
2970
0
    {
2971
0
        singleton.DeclareAlgorithm(
2972
0
            {"driver"},
2973
0
            []() -> std::unique_ptr<GDALAlgorithm>
2974
0
            {
2975
0
                return std::make_unique<GDALContainerAlgorithm>(
2976
0
                    "driver", "Command for driver specific operations.");
2977
0
            });
2978
0
    }
2979
2980
0
    std::vector<std::string> path = {"driver",
2981
0
                                     CPLString(osDriverName).tolower()};
2982
0
    if (!singleton.HasDeclaredSubAlgorithm(path))
2983
0
    {
2984
0
        auto lambda = [osDriverName]() -> std::unique_ptr<GDALAlgorithm>
2985
0
        {
2986
0
            auto poDriver =
2987
0
                GetGDALDriverManager()->GetDriverByName(osDriverName.c_str());
2988
0
            if (poDriver)
2989
0
            {
2990
0
                const char *pszHelpTopic =
2991
0
                    poDriver->GetMetadataItem(GDAL_DMD_HELPTOPIC);
2992
0
                return std::make_unique<GDALContainerAlgorithm>(
2993
0
                    CPLString(osDriverName).tolower(),
2994
0
                    std::string("Command for ")
2995
0
                        .append(osDriverName)
2996
0
                        .append(" driver specific operations."),
2997
0
                    pszHelpTopic ? std::string("/").append(pszHelpTopic)
2998
0
                                 : std::string());
2999
0
            }
3000
0
            return nullptr;
3001
0
        };
3002
0
        singleton.DeclareAlgorithm(path, std::move(lambda));
3003
0
    }
3004
3005
0
    path.insert(path.end(), aosPath.begin(), aosPath.end());
3006
3007
0
    auto lambda = [osDriverName, aosPath]() -> std::unique_ptr<GDALAlgorithm>
3008
0
    {
3009
0
        auto poDriver =
3010
0
            GetGDALDriverManager()->GetDriverByName(osDriverName.c_str());
3011
0
        if (poDriver)
3012
0
            return std::unique_ptr<GDALAlgorithm>(
3013
0
                poDriver->InstantiateAlgorithm(aosPath));
3014
0
        return nullptr;
3015
0
    };
3016
3017
0
    singleton.DeclareAlgorithm(path, std::move(lambda));
3018
3019
0
    CPL_IGNORE_RET_VAL(osDriverName);
3020
0
}
3021
3022
//! @endcond
3023
3024
/************************************************************************/
3025
/*                   DoesDriverHandleExtension()                        */
3026
/************************************************************************/
3027
3028
static bool DoesDriverHandleExtension(GDALDriverH hDriver, const char *pszExt)
3029
0
{
3030
0
    bool bRet = false;
3031
0
    const char *pszDriverExtensions =
3032
0
        GDALGetMetadataItem(hDriver, GDAL_DMD_EXTENSIONS, nullptr);
3033
0
    if (pszDriverExtensions)
3034
0
    {
3035
0
        const CPLStringList aosTokens(CSLTokenizeString(pszDriverExtensions));
3036
0
        const int nTokens = aosTokens.size();
3037
0
        for (int j = 0; j < nTokens; ++j)
3038
0
        {
3039
0
            if (EQUAL(pszExt, aosTokens[j]))
3040
0
            {
3041
0
                bRet = true;
3042
0
                break;
3043
0
            }
3044
0
        }
3045
0
    }
3046
0
    return bRet;
3047
0
}
3048
3049
/************************************************************************/
3050
/*                     IsOnlyExpectedGDBDrivers()                       */
3051
/************************************************************************/
3052
3053
static bool IsOnlyExpectedGDBDrivers(const CPLStringList &aosDriverNames)
3054
0
{
3055
0
    for (const char *pszDrvName : aosDriverNames)
3056
0
    {
3057
0
        if (!EQUAL(pszDrvName, "OpenFileGDB") &&
3058
0
            !EQUAL(pszDrvName, "FileGDB") && !EQUAL(pszDrvName, "GPSBabel"))
3059
0
        {
3060
0
            return false;
3061
0
        }
3062
0
    }
3063
0
    return true;
3064
0
}
3065
3066
/************************************************************************/
3067
/*                  GDALGetOutputDriversForDatasetName()                */
3068
/************************************************************************/
3069
3070
/** Return a list of driver short names that are likely candidates for the
3071
 * provided output file name.
3072
 *
3073
 * @param pszDestDataset Output dataset name (might not exist).
3074
 * @param nDatasetTypeFlag GDAL_OF_RASTER, GDAL_OF_VECTOR, GDAL_OF_MULTIDIM_RASTER
3075
 *                         or a binary-or'ed combination of them
3076
 * @param bSingleMatch Whether a single match is desired, that is to say the
3077
 *                     returned list will contain at most one item, which will
3078
 *                     be the first driver in the order they are registered to
3079
 *                     match the output dataset name. Note that in this mode, if
3080
 *                     nDatasetTypeFlag==GDAL_OF_RASTER and pszDestDataset has
3081
 *                     no extension, GTiff will be selected.
3082
 * @param bEmitWarning Whether a warning should be emitted when bSingleMatch is
3083
 *                     true and there are more than 2 candidates.
3084
 * @return NULL terminated list of driver short names.
3085
 * To be freed with CSLDestroy()
3086
 * @since 3.9
3087
 */
3088
char **GDALGetOutputDriversForDatasetName(const char *pszDestDataset,
3089
                                          int nDatasetTypeFlag,
3090
                                          bool bSingleMatch, bool bEmitWarning)
3091
0
{
3092
0
    CPLStringList aosDriverNames;
3093
0
    CPLStringList aosMissingDriverNames;
3094
3095
0
    std::string osExt = CPLGetExtensionSafe(pszDestDataset);
3096
0
    if (EQUAL(osExt.c_str(), "zip"))
3097
0
    {
3098
0
        const CPLString osLower(CPLString(pszDestDataset).tolower());
3099
0
        if (osLower.endsWith(".shp.zip"))
3100
0
        {
3101
0
            osExt = "shp.zip";
3102
0
        }
3103
0
        else if (osLower.endsWith(".gpkg.zip"))
3104
0
        {
3105
0
            osExt = "gpkg.zip";
3106
0
        }
3107
0
    }
3108
0
    else if (EQUAL(osExt.c_str(), "json"))
3109
0
    {
3110
0
        const CPLString osLower(CPLString(pszDestDataset).tolower());
3111
0
        if (osLower.endsWith(".gdalg.json"))
3112
0
            return nullptr;
3113
0
    }
3114
3115
0
    auto poDM = GetGDALDriverManager();
3116
0
    const int nDriverCount = poDM->GetDriverCount(true);
3117
0
    GDALDriver *poMissingPluginDriver = nullptr;
3118
0
    std::string osMatchingPrefix;
3119
0
    for (int i = 0; i < nDriverCount; i++)
3120
0
    {
3121
0
        GDALDriver *poDriver = poDM->GetDriver(i, true);
3122
0
        bool bOk = false;
3123
0
        if ((poDriver->GetMetadataItem(GDAL_DCAP_CREATE) != nullptr ||
3124
0
             poDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY) != nullptr ||
3125
0
             poDriver->GetMetadataItem(GDAL_DCAP_UPDATE) != nullptr) &&
3126
0
            (((nDatasetTypeFlag & GDAL_OF_RASTER) &&
3127
0
              poDriver->GetMetadataItem(GDAL_DCAP_RASTER) != nullptr) ||
3128
0
             ((nDatasetTypeFlag & GDAL_OF_VECTOR) &&
3129
0
              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR) != nullptr) ||
3130
0
             ((nDatasetTypeFlag & GDAL_OF_MULTIDIM_RASTER) &&
3131
0
              poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER) != nullptr)))
3132
0
        {
3133
0
            bOk = true;
3134
0
        }
3135
0
        else if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR_TRANSLATE_FROM) &&
3136
0
                 (nDatasetTypeFlag & GDAL_OF_VECTOR) != 0)
3137
0
        {
3138
0
            bOk = true;
3139
0
        }
3140
0
        if (bOk)
3141
0
        {
3142
0
            if (!osExt.empty() &&
3143
0
                DoesDriverHandleExtension(GDALDriver::ToHandle(poDriver),
3144
0
                                          osExt.c_str()))
3145
0
            {
3146
0
                if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
3147
0
                {
3148
0
                    poMissingPluginDriver = poDriver;
3149
0
                    aosMissingDriverNames.AddString(poDriver->GetDescription());
3150
0
                }
3151
0
                else
3152
0
                    aosDriverNames.AddString(poDriver->GetDescription());
3153
0
            }
3154
0
            else
3155
0
            {
3156
0
                const char *pszPrefix =
3157
0
                    poDriver->GetMetadataItem(GDAL_DMD_CONNECTION_PREFIX);
3158
0
                if (pszPrefix && STARTS_WITH_CI(pszDestDataset, pszPrefix))
3159
0
                {
3160
0
                    if (poDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME"))
3161
0
                    {
3162
0
                        osMatchingPrefix = pszPrefix;
3163
0
                        poMissingPluginDriver = poDriver;
3164
0
                        aosMissingDriverNames.AddString(
3165
0
                            poDriver->GetDescription());
3166
0
                    }
3167
0
                    else
3168
0
                        aosDriverNames.AddString(poDriver->GetDescription());
3169
0
                }
3170
0
            }
3171
0
        }
3172
0
    }
3173
3174
    // GMT is registered before netCDF for opening reasons, but we want
3175
    // netCDF to be used by default for output.
3176
0
    if (EQUAL(osExt.c_str(), "nc") && aosDriverNames.size() == 2 &&
3177
0
        EQUAL(aosDriverNames[0], "GMT") && EQUAL(aosDriverNames[1], "netCDF"))
3178
0
    {
3179
0
        aosDriverNames.Clear();
3180
0
        aosDriverNames.AddString("netCDF");
3181
0
        aosDriverNames.AddString("GMT");
3182
0
    }
3183
3184
0
    if (bSingleMatch)
3185
0
    {
3186
0
        if (nDatasetTypeFlag == GDAL_OF_RASTER)
3187
0
        {
3188
0
            if (aosDriverNames.empty())
3189
0
            {
3190
0
                if (osExt.empty())
3191
0
                {
3192
0
                    aosDriverNames.AddString("GTiff");
3193
0
                }
3194
0
            }
3195
0
            else if (aosDriverNames.size() >= 2)
3196
0
            {
3197
0
                if (bEmitWarning && !(EQUAL(aosDriverNames[0], "GTiff") &&
3198
0
                                      EQUAL(aosDriverNames[1], "COG")))
3199
0
                {
3200
0
                    CPLError(CE_Warning, CPLE_AppDefined,
3201
0
                             "Several drivers matching %s extension. Using %s",
3202
0
                             osExt.c_str(), aosDriverNames[0]);
3203
0
                }
3204
0
                const std::string osDrvName = aosDriverNames[0];
3205
0
                aosDriverNames.Clear();
3206
0
                aosDriverNames.AddString(osDrvName.c_str());
3207
0
            }
3208
0
        }
3209
0
        else if (EQUAL(osExt.c_str(), "gdb") &&
3210
0
                 IsOnlyExpectedGDBDrivers(aosDriverNames))
3211
0
        {
3212
            // Do not warn about that case given that FileGDB write support
3213
            // forwards to OpenFileGDB one. And also consider GPSBabel as too
3214
            // marginal to deserve the warning.
3215
0
            aosDriverNames.Clear();
3216
0
            aosDriverNames.AddString("OpenFileGDB");
3217
0
        }
3218
0
        else if (aosDriverNames.size() >= 2)
3219
0
        {
3220
0
            if (bEmitWarning)
3221
0
            {
3222
0
                CPLError(CE_Warning, CPLE_AppDefined,
3223
0
                         "Several drivers matching %s %s. Using %s",
3224
0
                         osMatchingPrefix.empty() ? osExt.c_str()
3225
0
                                                  : osMatchingPrefix.c_str(),
3226
0
                         osMatchingPrefix.empty() ? "extension" : "prefix",
3227
0
                         aosDriverNames[0]);
3228
0
            }
3229
0
            const std::string osDrvName = aosDriverNames[0];
3230
0
            aosDriverNames.Clear();
3231
0
            aosDriverNames.AddString(osDrvName.c_str());
3232
0
        }
3233
0
    }
3234
3235
0
    if (aosDriverNames.empty() && bEmitWarning &&
3236
0
        aosMissingDriverNames.size() == 1 && poMissingPluginDriver)
3237
0
    {
3238
0
        CPLError(CE_Failure, CPLE_AppDefined,
3239
0
                 "No installed driver matching %s %s, but %s driver is "
3240
0
                 "known. However plugin %s",
3241
0
                 osMatchingPrefix.empty() ? osExt.c_str()
3242
0
                                          : osMatchingPrefix.c_str(),
3243
0
                 osMatchingPrefix.empty() ? "extension" : "prefix",
3244
0
                 poMissingPluginDriver->GetDescription(),
3245
0
                 GDALGetMessageAboutMissingPluginDriver(poMissingPluginDriver)
3246
0
                     .c_str());
3247
0
    }
3248
0
    else if (aosDriverNames.empty() && bEmitWarning &&
3249
0
             aosMissingDriverNames.empty())
3250
0
    {
3251
0
        for (const auto &sConnectionPrefix : asKnownConnectionPrefixes)
3252
0
        {
3253
0
            if (STARTS_WITH_CI(pszDestDataset, sConnectionPrefix.pszPrefix))
3254
0
            {
3255
0
                CPLError(CE_Failure, CPLE_AppDefined,
3256
0
                         "Filename %s starts with the connection prefix of "
3257
0
                         "driver %s, which is not enabled in this GDAL build. "
3258
0
                         "If that filename is really intended, explicitly "
3259
0
                         "specify its output format.",
3260
0
                         pszDestDataset, sConnectionPrefix.pszDriverName);
3261
0
                break;
3262
0
            }
3263
0
        }
3264
0
    }
3265
3266
0
    return aosDriverNames.StealList();
3267
0
}
3268
3269
/************************************************************************/
3270
/*                GDALGetMessageAboutMissingPluginDriver()              */
3271
/************************************************************************/
3272
3273
std::string
3274
GDALGetMessageAboutMissingPluginDriver(GDALDriver *poMissingPluginDriver)
3275
0
{
3276
0
    std::string osMsg =
3277
0
        poMissingPluginDriver->GetMetadataItem("MISSING_PLUGIN_FILENAME");
3278
0
    osMsg += " is not available in your "
3279
0
             "installation.";
3280
0
    if (const char *pszInstallationMsg = poMissingPluginDriver->GetMetadataItem(
3281
0
            GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE))
3282
0
    {
3283
0
        osMsg += " ";
3284
0
        osMsg += pszInstallationMsg;
3285
0
    }
3286
3287
0
    VSIStatBuf sStat;
3288
0
    if (const char *pszGDALDriverPath =
3289
0
            CPLGetConfigOption("GDAL_DRIVER_PATH", nullptr))
3290
0
    {
3291
0
        if (VSIStat(pszGDALDriverPath, &sStat) != 0)
3292
0
        {
3293
0
            if (osMsg.back() != '.')
3294
0
                osMsg += ".";
3295
0
            osMsg += " Directory '";
3296
0
            osMsg += pszGDALDriverPath;
3297
0
            osMsg += "' pointed by GDAL_DRIVER_PATH does not exist.";
3298
0
        }
3299
0
    }
3300
0
    else
3301
0
    {
3302
0
        if (osMsg.back() != '.')
3303
0
            osMsg += ".";
3304
#ifdef INSTALL_PLUGIN_FULL_DIR
3305
        if (VSIStat(INSTALL_PLUGIN_FULL_DIR, &sStat) != 0)
3306
        {
3307
            osMsg += " Directory '";
3308
            osMsg += INSTALL_PLUGIN_FULL_DIR;
3309
            osMsg += "' hardcoded in the GDAL library does not "
3310
                     "exist and the GDAL_DRIVER_PATH "
3311
                     "configuration option is not set.";
3312
        }
3313
        else
3314
#endif
3315
0
        {
3316
0
            osMsg += " The GDAL_DRIVER_PATH configuration "
3317
0
                     "option is not set.";
3318
0
        }
3319
0
    }
3320
0
    return osMsg;
3321
0
}