Coverage Report

Created: 2026-09-14 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdalalgorithm.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  GDALAlgorithm class
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "cpl_conv.h"
15
#include "cpl_enumerate.h"
16
#include "cpl_error.h"
17
#include "cpl_error_internal.h"
18
#include "cpl_json.h"
19
#include "cpl_levenshtein.h"
20
#include "cpl_minixml.h"
21
#include "cpl_multiproc.h"
22
23
#include "gdalalgorithm.h"
24
#include "gdalalg_abstract_pipeline.h"
25
#include "gdal_priv.h"
26
#include "gdal_thread_pool.h"
27
#include "memdataset.h"
28
#include "ogrsf_frmts.h"
29
#include "ogr_p.h"
30
#include "ogr_spatialref.h"
31
#include "vrtdataset.h"
32
33
#include <algorithm>
34
#include <cassert>
35
#include <cerrno>
36
#include <cmath>
37
#include <cstdlib>
38
#include <limits>
39
#include <map>
40
#include <type_traits>
41
#include <string_view>
42
#include <regex>
43
44
#ifndef _
45
0
#define _(x) (x)
46
#endif
47
48
constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
49
50
constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
51
52
constexpr const char *GDAL_ARG_NAME_BAND = "band";
53
54
//! @cond Doxygen_Suppress
55
struct GDALAlgorithmArgHS
56
{
57
    GDALAlgorithmArg *ptr = nullptr;
58
59
0
    explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
60
0
    {
61
0
    }
62
};
63
64
//! @endcond
65
66
//! @cond Doxygen_Suppress
67
struct GDALArgDatasetValueHS
68
{
69
    GDALArgDatasetValue val{};
70
    GDALArgDatasetValue *ptr = nullptr;
71
72
0
    GDALArgDatasetValueHS() : ptr(&val)
73
0
    {
74
0
    }
75
76
0
    explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
77
0
    {
78
0
    }
79
80
    GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
81
    GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
82
};
83
84
//! @endcond
85
86
/************************************************************************/
87
/*                     GDALAlgorithmArgTypeIsList()                     */
88
/************************************************************************/
89
90
bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
91
0
{
92
0
    switch (type)
93
0
    {
94
0
        case GAAT_BOOLEAN:
95
0
        case GAAT_STRING:
96
0
        case GAAT_INTEGER:
97
0
        case GAAT_REAL:
98
0
        case GAAT_DATASET:
99
0
            break;
100
101
0
        case GAAT_STRING_LIST:
102
0
        case GAAT_INTEGER_LIST:
103
0
        case GAAT_REAL_LIST:
104
0
        case GAAT_DATASET_LIST:
105
0
            return true;
106
0
    }
107
108
0
    return false;
109
0
}
110
111
/************************************************************************/
112
/*                      GDALAlgorithmArgTypeName()                      */
113
/************************************************************************/
114
115
const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
116
0
{
117
0
    switch (type)
118
0
    {
119
0
        case GAAT_BOOLEAN:
120
0
            break;
121
0
        case GAAT_STRING:
122
0
            return "string";
123
0
        case GAAT_INTEGER:
124
0
            return "integer";
125
0
        case GAAT_REAL:
126
0
            return "real";
127
0
        case GAAT_DATASET:
128
0
            return "dataset";
129
0
        case GAAT_STRING_LIST:
130
0
            return "string_list";
131
0
        case GAAT_INTEGER_LIST:
132
0
            return "integer_list";
133
0
        case GAAT_REAL_LIST:
134
0
            return "real_list";
135
0
        case GAAT_DATASET_LIST:
136
0
            return "dataset_list";
137
0
    }
138
139
0
    return "boolean";
140
0
}
141
142
/************************************************************************/
143
/*                  GDALAlgorithmArgDatasetTypeName()                   */
144
/************************************************************************/
145
146
std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
147
0
{
148
0
    std::string ret;
149
0
    if ((type & GDAL_OF_RASTER) != 0)
150
0
        ret = "raster";
151
0
    if ((type & GDAL_OF_VECTOR) != 0)
152
0
    {
153
0
        if (!ret.empty())
154
0
        {
155
0
            if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
156
0
                ret += ", ";
157
0
            else
158
0
                ret += " or ";
159
0
        }
160
0
        ret += "vector";
161
0
    }
162
0
    if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
163
0
    {
164
0
        if (!ret.empty())
165
0
        {
166
0
            ret += " or ";
167
0
        }
168
0
        ret += "multidimensional raster";
169
0
    }
170
0
    return ret;
171
0
}
172
173
/************************************************************************/
174
/*                        GDALAlgorithmArgDecl()                        */
175
/************************************************************************/
176
177
// cppcheck-suppress uninitMemberVar
178
GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
179
                                           char chShortName,
180
                                           const std::string &description,
181
                                           GDALAlgorithmArgType type)
182
0
    : m_longName(longName),
183
0
      m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
184
0
      m_description(description), m_type(type),
185
0
      m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
186
0
                    .toupper()),
187
0
      m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
188
0
{
189
0
    if (m_type == GAAT_BOOLEAN)
190
0
    {
191
0
        m_defaultValue = false;
192
0
    }
193
0
}
194
195
/************************************************************************/
196
/*                 GDALAlgorithmArgDecl::SetMinCount()                  */
197
/************************************************************************/
198
199
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
200
0
{
201
0
    if (!GDALAlgorithmArgTypeIsList(m_type))
202
0
    {
203
0
        CPLError(CE_Failure, CPLE_NotSupported,
204
0
                 "SetMinCount() illegal on scalar argument '%s'",
205
0
                 GetName().c_str());
206
0
    }
207
0
    else
208
0
    {
209
0
        m_minCount = count;
210
0
    }
211
0
    return *this;
212
0
}
213
214
/************************************************************************/
215
/*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
216
/************************************************************************/
217
218
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
219
0
{
220
0
    if (!GDALAlgorithmArgTypeIsList(m_type))
221
0
    {
222
0
        CPLError(CE_Failure, CPLE_NotSupported,
223
0
                 "SetMaxCount() illegal on scalar argument '%s'",
224
0
                 GetName().c_str());
225
0
    }
226
0
    else
227
0
    {
228
0
        m_maxCount = count;
229
0
    }
230
0
    return *this;
231
0
}
232
233
/************************************************************************/
234
/*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
235
/************************************************************************/
236
237
0
GDALAlgorithmArg::~GDALAlgorithmArg() = default;
238
239
/************************************************************************/
240
/*                       GDALAlgorithmArg::Set()                        */
241
/************************************************************************/
242
243
bool GDALAlgorithmArg::Set(bool value)
244
0
{
245
0
    if (m_decl.GetType() != GAAT_BOOLEAN)
246
0
    {
247
0
        CPLError(
248
0
            CE_Failure, CPLE_AppDefined,
249
0
            "Calling Set(bool) on argument '%s' of type %s is not supported",
250
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
251
0
        return false;
252
0
    }
253
0
    return SetInternal(value);
254
0
}
255
256
bool GDALAlgorithmArg::ProcessString(std::string &value) const
257
0
{
258
0
    if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
259
0
        value.front() == '@')
260
0
    {
261
0
        GByte *pabyData = nullptr;
262
0
        if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
263
0
                          10 * 1024 * 1024))
264
0
        {
265
            // Remove UTF-8 BOM
266
0
            size_t offset = 0;
267
0
            if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
268
0
                pabyData[2] == 0xBF)
269
0
            {
270
0
                offset = 3;
271
0
            }
272
0
            value = reinterpret_cast<const char *>(pabyData + offset);
273
0
            VSIFree(pabyData);
274
0
        }
275
0
        else
276
0
        {
277
0
            return false;
278
0
        }
279
0
    }
280
281
0
    if (m_decl.IsRemoveSQLCommentsEnabled())
282
0
        value = CPLRemoveSQLComments(value);
283
284
0
    return true;
285
0
}
286
287
bool GDALAlgorithmArg::Set(const std::string &value)
288
0
{
289
0
    switch (m_decl.GetType())
290
0
    {
291
0
        case GAAT_BOOLEAN:
292
0
            if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
293
0
                EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
294
0
            {
295
0
                return Set(true);
296
0
            }
297
0
            else if (EQUAL(value.c_str(), "0") ||
298
0
                     EQUAL(value.c_str(), "FALSE") ||
299
0
                     EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
300
0
            {
301
0
                return Set(false);
302
0
            }
303
0
            break;
304
305
0
        case GAAT_INTEGER:
306
0
        case GAAT_INTEGER_LIST:
307
0
        {
308
0
            errno = 0;
309
0
            char *endptr = nullptr;
310
0
            const auto v = std::strtoll(value.c_str(), &endptr, 10);
311
0
            if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
312
0
                endptr == value.c_str() + value.size())
313
0
            {
314
0
                if (m_decl.GetType() == GAAT_INTEGER)
315
0
                    return Set(static_cast<int>(v));
316
0
                else
317
0
                    return Set(std::vector<int>{static_cast<int>(v)});
318
0
            }
319
0
            break;
320
0
        }
321
322
0
        case GAAT_REAL:
323
0
        case GAAT_REAL_LIST:
324
0
        {
325
0
            char *endptr = nullptr;
326
0
            const double v = CPLStrtod(value.c_str(), &endptr);
327
0
            if (endptr == value.c_str() + value.size())
328
0
            {
329
0
                if (m_decl.GetType() == GAAT_REAL)
330
0
                    return Set(v);
331
0
                else
332
0
                    return Set(std::vector<double>{v});
333
0
            }
334
0
            break;
335
0
        }
336
337
0
        case GAAT_STRING:
338
0
            break;
339
340
0
        case GAAT_STRING_LIST:
341
0
            return Set(std::vector<std::string>{value});
342
343
0
        case GAAT_DATASET:
344
0
            return SetDatasetName(value);
345
346
0
        case GAAT_DATASET_LIST:
347
0
        {
348
0
            std::vector<GDALArgDatasetValue> v;
349
0
            v.resize(1);
350
0
            v[0].Set(value);
351
0
            return Set(std::move(v));
352
0
        }
353
0
    }
354
355
0
    if (m_decl.GetType() != GAAT_STRING)
356
0
    {
357
0
        CPLError(CE_Failure, CPLE_AppDefined,
358
0
                 "Calling Set(std::string) on argument '%s' of type %s is not "
359
0
                 "supported",
360
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
361
0
        return false;
362
0
    }
363
364
0
    std::string newValue(value);
365
0
    return ProcessString(newValue) && SetInternal(newValue);
366
0
}
367
368
bool GDALAlgorithmArg::Set(int value)
369
0
{
370
0
    if (m_decl.GetType() == GAAT_BOOLEAN)
371
0
    {
372
0
        if (value == 1)
373
0
            return Set(true);
374
0
        else if (value == 0)
375
0
            return Set(false);
376
0
    }
377
0
    else if (m_decl.GetType() == GAAT_REAL)
378
0
    {
379
0
        return Set(static_cast<double>(value));
380
0
    }
381
0
    else if (m_decl.GetType() == GAAT_STRING)
382
0
    {
383
0
        return Set(std::to_string(value));
384
0
    }
385
0
    else if (m_decl.GetType() == GAAT_INTEGER_LIST)
386
0
    {
387
0
        return Set(std::vector<int>{value});
388
0
    }
389
0
    else if (m_decl.GetType() == GAAT_REAL_LIST)
390
0
    {
391
0
        return Set(std::vector<double>{static_cast<double>(value)});
392
0
    }
393
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
394
0
    {
395
0
        return Set(std::vector<std::string>{std::to_string(value)});
396
0
    }
397
398
0
    if (m_decl.GetType() != GAAT_INTEGER)
399
0
    {
400
0
        CPLError(
401
0
            CE_Failure, CPLE_AppDefined,
402
0
            "Calling Set(int) on argument '%s' of type %s is not supported",
403
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
404
0
        return false;
405
0
    }
406
0
    return SetInternal(value);
407
0
}
408
409
bool GDALAlgorithmArg::Set(double value)
410
0
{
411
0
    if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
412
0
        value <= INT_MAX && static_cast<int>(value) == value)
413
0
    {
414
0
        return Set(static_cast<int>(value));
415
0
    }
416
0
    else if (m_decl.GetType() == GAAT_STRING)
417
0
    {
418
0
        return Set(std::to_string(value));
419
0
    }
420
0
    else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
421
0
             value <= INT_MAX && static_cast<int>(value) == value)
422
0
    {
423
0
        return Set(std::vector<int>{static_cast<int>(value)});
424
0
    }
425
0
    else if (m_decl.GetType() == GAAT_REAL_LIST)
426
0
    {
427
0
        return Set(std::vector<double>{value});
428
0
    }
429
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
430
0
    {
431
0
        return Set(std::vector<std::string>{std::to_string(value)});
432
0
    }
433
0
    else if (m_decl.GetType() != GAAT_REAL)
434
0
    {
435
0
        CPLError(
436
0
            CE_Failure, CPLE_AppDefined,
437
0
            "Calling Set(double) on argument '%s' of type %s is not supported",
438
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
439
0
        return false;
440
0
    }
441
0
    return SetInternal(value);
442
0
}
443
444
static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
445
0
{
446
0
    if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
447
0
        arg->GetDatasetOutputFlags() == GADV_OBJECT)
448
0
    {
449
0
        CPLError(
450
0
            CE_Failure, CPLE_AppDefined,
451
0
            "Dataset object '%s' is created by algorithm and cannot be set "
452
0
            "as an input.",
453
0
            arg->GetName().c_str());
454
0
        return false;
455
0
    }
456
0
    else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
457
0
    {
458
0
        CPLError(CE_Failure, CPLE_AppDefined,
459
0
                 "Dataset%s '%s' must be provided by name, not as object.",
460
0
                 arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
461
0
        return false;
462
0
    }
463
464
0
    return true;
465
0
}
466
467
bool GDALAlgorithmArg::Set(GDALDataset *ds)
468
0
{
469
0
    if (m_decl.GetType() != GAAT_DATASET &&
470
0
        m_decl.GetType() != GAAT_DATASET_LIST)
471
0
    {
472
0
        CPLError(CE_Failure, CPLE_AppDefined,
473
0
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
474
0
                 "is not supported",
475
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
476
0
        return false;
477
0
    }
478
0
    if (!CheckCanSetDatasetObject(this))
479
0
        return false;
480
0
    m_explicitlySet = true;
481
0
    if (m_decl.GetType() == GAAT_DATASET)
482
0
    {
483
0
        auto &val = *std::get<GDALArgDatasetValue *>(m_value);
484
0
        val.Set(ds);
485
0
    }
486
0
    else
487
0
    {
488
0
        CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
489
0
        auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
490
0
        val.resize(1);
491
0
        val[0].Set(ds);
492
0
    }
493
0
    return RunAllActions();
494
0
}
495
496
bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
497
0
{
498
0
    if (m_decl.GetType() != GAAT_DATASET)
499
0
    {
500
0
        CPLError(CE_Failure, CPLE_AppDefined,
501
0
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
502
0
                 "is not supported",
503
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
504
0
        return false;
505
0
    }
506
0
    if (!CheckCanSetDatasetObject(this))
507
0
        return false;
508
0
    m_explicitlySet = true;
509
0
    auto &val = *std::get<GDALArgDatasetValue *>(m_value);
510
0
    val.Set(std::move(ds));
511
0
    return RunAllActions();
512
0
}
513
514
bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
515
0
{
516
0
    if (m_decl.GetType() != GAAT_DATASET)
517
0
    {
518
0
        CPLError(CE_Failure, CPLE_AppDefined,
519
0
                 "Calling SetDatasetName() on argument '%s' of type %s is "
520
0
                 "not supported",
521
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
522
0
        return false;
523
0
    }
524
0
    m_explicitlySet = true;
525
0
    std::get<GDALArgDatasetValue *>(m_value)->Set(name);
526
0
    return RunAllActions();
527
0
}
528
529
bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
530
0
{
531
0
    if (m_decl.GetType() != GAAT_DATASET)
532
0
    {
533
0
        CPLError(CE_Failure, CPLE_AppDefined,
534
0
                 "Calling SetFrom() on argument '%s' of type %s is "
535
0
                 "not supported",
536
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
537
0
        return false;
538
0
    }
539
0
    if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
540
0
        return false;
541
0
    m_explicitlySet = true;
542
0
    std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
543
0
    return RunAllActions();
544
0
}
545
546
bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
547
0
{
548
0
    if (m_decl.GetType() == GAAT_INTEGER_LIST)
549
0
    {
550
0
        std::vector<int> v_i;
551
0
        for (const std::string &s : value)
552
0
        {
553
0
            errno = 0;
554
0
            char *endptr = nullptr;
555
0
            const auto v = std::strtoll(s.c_str(), &endptr, 10);
556
0
            if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
557
0
                endptr == s.c_str() + s.size())
558
0
            {
559
0
                v_i.push_back(static_cast<int>(v));
560
0
            }
561
0
            else
562
0
            {
563
0
                break;
564
0
            }
565
0
        }
566
0
        if (v_i.size() == value.size())
567
0
            return Set(v_i);
568
0
    }
569
0
    else if (m_decl.GetType() == GAAT_REAL_LIST)
570
0
    {
571
0
        std::vector<double> v_d;
572
0
        for (const std::string &s : value)
573
0
        {
574
0
            char *endptr = nullptr;
575
0
            const double v = CPLStrtod(s.c_str(), &endptr);
576
0
            if (endptr == s.c_str() + s.size())
577
0
            {
578
0
                v_d.push_back(v);
579
0
            }
580
0
            else
581
0
            {
582
0
                break;
583
0
            }
584
0
        }
585
0
        if (v_d.size() == value.size())
586
0
            return Set(v_d);
587
0
    }
588
0
    else if ((m_decl.GetType() == GAAT_INTEGER ||
589
0
              m_decl.GetType() == GAAT_REAL ||
590
0
              m_decl.GetType() == GAAT_STRING) &&
591
0
             value.size() == 1)
592
0
    {
593
0
        return Set(value[0]);
594
0
    }
595
0
    else if (m_decl.GetType() == GAAT_DATASET_LIST)
596
0
    {
597
0
        std::vector<GDALArgDatasetValue> dsVector;
598
0
        for (const std::string &s : value)
599
0
            dsVector.emplace_back(s);
600
0
        return Set(std::move(dsVector));
601
0
    }
602
603
0
    if (m_decl.GetType() != GAAT_STRING_LIST)
604
0
    {
605
0
        CPLError(CE_Failure, CPLE_AppDefined,
606
0
                 "Calling Set(const std::vector<std::string> &) on argument "
607
0
                 "'%s' of type %s is not supported",
608
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
609
0
        return false;
610
0
    }
611
612
0
    if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
613
0
        m_decl.IsRemoveSQLCommentsEnabled())
614
0
    {
615
0
        std::vector<std::string> newValue(value);
616
0
        for (auto &s : newValue)
617
0
        {
618
0
            if (!ProcessString(s))
619
0
                return false;
620
0
        }
621
0
        return SetInternal(newValue);
622
0
    }
623
0
    else
624
0
    {
625
0
        return SetInternal(value);
626
0
    }
627
0
}
628
629
bool GDALAlgorithmArg::Set(const std::vector<int> &value)
630
0
{
631
0
    if (m_decl.GetType() == GAAT_REAL_LIST)
632
0
    {
633
0
        std::vector<double> v_d;
634
0
        for (int i : value)
635
0
            v_d.push_back(i);
636
0
        return Set(v_d);
637
0
    }
638
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
639
0
    {
640
0
        std::vector<std::string> v_s;
641
0
        for (int i : value)
642
0
            v_s.push_back(std::to_string(i));
643
0
        return Set(v_s);
644
0
    }
645
0
    else if ((m_decl.GetType() == GAAT_INTEGER ||
646
0
              m_decl.GetType() == GAAT_REAL ||
647
0
              m_decl.GetType() == GAAT_STRING) &&
648
0
             value.size() == 1)
649
0
    {
650
0
        return Set(value[0]);
651
0
    }
652
653
0
    if (m_decl.GetType() != GAAT_INTEGER_LIST)
654
0
    {
655
0
        CPLError(CE_Failure, CPLE_AppDefined,
656
0
                 "Calling Set(const std::vector<int> &) on argument '%s' of "
657
0
                 "type %s is not supported",
658
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
659
0
        return false;
660
0
    }
661
0
    return SetInternal(value);
662
0
}
663
664
bool GDALAlgorithmArg::Set(const std::vector<double> &value)
665
0
{
666
0
    if (m_decl.GetType() == GAAT_INTEGER_LIST)
667
0
    {
668
0
        std::vector<int> v_i;
669
0
        for (double d : value)
670
0
        {
671
0
            if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
672
0
            {
673
0
                v_i.push_back(static_cast<int>(d));
674
0
            }
675
0
            else
676
0
            {
677
0
                break;
678
0
            }
679
0
        }
680
0
        if (v_i.size() == value.size())
681
0
            return Set(v_i);
682
0
    }
683
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
684
0
    {
685
0
        std::vector<std::string> v_s;
686
0
        for (double d : value)
687
0
            v_s.push_back(std::to_string(d));
688
0
        return Set(v_s);
689
0
    }
690
0
    else if ((m_decl.GetType() == GAAT_INTEGER ||
691
0
              m_decl.GetType() == GAAT_REAL ||
692
0
              m_decl.GetType() == GAAT_STRING) &&
693
0
             value.size() == 1)
694
0
    {
695
0
        return Set(value[0]);
696
0
    }
697
698
0
    if (m_decl.GetType() != GAAT_REAL_LIST)
699
0
    {
700
0
        CPLError(CE_Failure, CPLE_AppDefined,
701
0
                 "Calling Set(const std::vector<double> &) on argument '%s' of "
702
0
                 "type %s is not supported",
703
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
704
0
        return false;
705
0
    }
706
0
    return SetInternal(value);
707
0
}
708
709
bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
710
0
{
711
0
    if (m_decl.GetType() != GAAT_DATASET_LIST)
712
0
    {
713
0
        CPLError(CE_Failure, CPLE_AppDefined,
714
0
                 "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
715
0
                 "argument '%s' of type %s is not supported",
716
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
717
0
        return false;
718
0
    }
719
0
    m_explicitlySet = true;
720
0
    *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
721
0
    return RunAllActions();
722
0
}
723
724
GDALAlgorithmArg &
725
GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
726
0
{
727
0
    Set(std::move(value));
728
0
    return *this;
729
0
}
730
731
bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
732
0
{
733
0
    const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
734
0
    return Set(value.exportToWkt(apszOptions));
735
0
}
736
737
bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
738
0
{
739
0
    if (m_decl.GetType() != other.GetType())
740
0
    {
741
0
        CPLError(CE_Failure, CPLE_AppDefined,
742
0
                 "Calling SetFrom() on argument '%s' of type %s whereas "
743
0
                 "other argument type is %s is not supported",
744
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
745
0
                 GDALAlgorithmArgTypeName(other.GetType()));
746
0
        return false;
747
0
    }
748
749
0
    switch (m_decl.GetType())
750
0
    {
751
0
        case GAAT_BOOLEAN:
752
0
            *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
753
0
            break;
754
0
        case GAAT_STRING:
755
0
            *std::get<std::string *>(m_value) =
756
0
                *std::get<std::string *>(other.m_value);
757
0
            break;
758
0
        case GAAT_INTEGER:
759
0
            *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
760
0
            break;
761
0
        case GAAT_REAL:
762
0
            *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
763
0
            break;
764
0
        case GAAT_DATASET:
765
0
            return SetFrom(other.Get<GDALArgDatasetValue>());
766
0
        case GAAT_STRING_LIST:
767
0
            *std::get<std::vector<std::string> *>(m_value) =
768
0
                *std::get<std::vector<std::string> *>(other.m_value);
769
0
            break;
770
0
        case GAAT_INTEGER_LIST:
771
0
            *std::get<std::vector<int> *>(m_value) =
772
0
                *std::get<std::vector<int> *>(other.m_value);
773
0
            break;
774
0
        case GAAT_REAL_LIST:
775
0
            *std::get<std::vector<double> *>(m_value) =
776
0
                *std::get<std::vector<double> *>(other.m_value);
777
0
            break;
778
0
        case GAAT_DATASET_LIST:
779
0
        {
780
0
            std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
781
0
            for (const auto &val :
782
0
                 *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
783
0
            {
784
0
                GDALArgDatasetValue v;
785
0
                v.SetFrom(val);
786
0
                std::get<std::vector<GDALArgDatasetValue> *>(m_value)
787
0
                    ->push_back(std::move(v));
788
0
            }
789
0
            break;
790
0
        }
791
0
    }
792
0
    m_explicitlySet = true;
793
0
    return RunAllActions();
794
0
}
795
796
/************************************************************************/
797
/*                  GDALAlgorithmArg::RunAllActions()                   */
798
/************************************************************************/
799
800
bool GDALAlgorithmArg::RunAllActions()
801
0
{
802
0
    if (!RunValidationActions())
803
0
        return false;
804
0
    RunActions();
805
0
    return true;
806
0
}
807
808
/************************************************************************/
809
/*                    GDALAlgorithmArg::RunActions()                    */
810
/************************************************************************/
811
812
void GDALAlgorithmArg::RunActions()
813
0
{
814
0
    for (const auto &f : m_actions)
815
0
        f();
816
0
}
817
818
/************************************************************************/
819
/*                  GDALAlgorithmArg::ValidateChoice()                  */
820
/************************************************************************/
821
822
// Returns the canonical value if matching a valid choice, or empty string
823
// otherwise.
824
std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
825
0
{
826
0
    for (const std::string &choice : GetChoices())
827
0
    {
828
0
        if (EQUAL(value.c_str(), choice.c_str()))
829
0
        {
830
0
            return choice;
831
0
        }
832
0
    }
833
834
0
    for (const std::string &choice : GetHiddenChoices())
835
0
    {
836
0
        if (EQUAL(value.c_str(), choice.c_str()))
837
0
        {
838
0
            return choice;
839
0
        }
840
0
    }
841
842
0
    std::string expected;
843
0
    for (const auto &choice : GetChoices())
844
0
    {
845
0
        if (!expected.empty())
846
0
            expected += ", ";
847
0
        expected += '\'';
848
0
        expected += choice;
849
0
        expected += '\'';
850
0
    }
851
0
    if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
852
0
    {
853
0
        return "?";
854
0
    }
855
0
    CPLError(CE_Failure, CPLE_IllegalArg,
856
0
             "Invalid value '%s' for string argument '%s'. Should be "
857
0
             "one among %s.",
858
0
             value.c_str(), GetName().c_str(), expected.c_str());
859
0
    return std::string();
860
0
}
861
862
/************************************************************************/
863
/*                 GDALAlgorithmArg::ValidateIntRange()                 */
864
/************************************************************************/
865
866
bool GDALAlgorithmArg::ValidateIntRange(int val) const
867
0
{
868
0
    bool ret = true;
869
870
0
    const auto [minVal, minValIsIncluded] = GetMinValue();
871
0
    if (!std::isnan(minVal))
872
0
    {
873
0
        if (minValIsIncluded && val < minVal)
874
0
        {
875
0
            CPLError(CE_Failure, CPLE_IllegalArg,
876
0
                     "Value of argument '%s' is %d, but should be >= %d",
877
0
                     GetName().c_str(), val, static_cast<int>(minVal));
878
0
            ret = false;
879
0
        }
880
0
        else if (!minValIsIncluded && val <= minVal)
881
0
        {
882
0
            CPLError(CE_Failure, CPLE_IllegalArg,
883
0
                     "Value of argument '%s' is %d, but should be > %d",
884
0
                     GetName().c_str(), val, static_cast<int>(minVal));
885
0
            ret = false;
886
0
        }
887
0
    }
888
889
0
    const auto [maxVal, maxValIsIncluded] = GetMaxValue();
890
0
    if (!std::isnan(maxVal))
891
0
    {
892
893
0
        if (maxValIsIncluded && val > maxVal)
894
0
        {
895
0
            CPLError(CE_Failure, CPLE_IllegalArg,
896
0
                     "Value of argument '%s' is %d, but should be <= %d",
897
0
                     GetName().c_str(), val, static_cast<int>(maxVal));
898
0
            ret = false;
899
0
        }
900
0
        else if (!maxValIsIncluded && val >= maxVal)
901
0
        {
902
0
            CPLError(CE_Failure, CPLE_IllegalArg,
903
0
                     "Value of argument '%s' is %d, but should be < %d",
904
0
                     GetName().c_str(), val, static_cast<int>(maxVal));
905
0
            ret = false;
906
0
        }
907
0
    }
908
909
0
    return ret;
910
0
}
911
912
/************************************************************************/
913
/*                GDALAlgorithmArg::ValidateRealRange()                 */
914
/************************************************************************/
915
916
bool GDALAlgorithmArg::ValidateRealRange(double val) const
917
0
{
918
0
    bool ret = true;
919
920
0
    const auto [minVal, minValIsIncluded] = GetMinValue();
921
0
    if (!std::isnan(minVal))
922
0
    {
923
0
        if (minValIsIncluded && !(val >= minVal))
924
0
        {
925
0
            CPLError(CE_Failure, CPLE_IllegalArg,
926
0
                     "Value of argument '%s' is %g, but should be >= %g",
927
0
                     GetName().c_str(), val, minVal);
928
0
            ret = false;
929
0
        }
930
0
        else if (!minValIsIncluded && !(val > minVal))
931
0
        {
932
0
            CPLError(CE_Failure, CPLE_IllegalArg,
933
0
                     "Value of argument '%s' is %g, but should be > %g",
934
0
                     GetName().c_str(), val, minVal);
935
0
            ret = false;
936
0
        }
937
0
    }
938
939
0
    const auto [maxVal, maxValIsIncluded] = GetMaxValue();
940
0
    if (!std::isnan(maxVal))
941
0
    {
942
943
0
        if (maxValIsIncluded && !(val <= maxVal))
944
0
        {
945
0
            CPLError(CE_Failure, CPLE_IllegalArg,
946
0
                     "Value of argument '%s' is %g, but should be <= %g",
947
0
                     GetName().c_str(), val, maxVal);
948
0
            ret = false;
949
0
        }
950
0
        else if (!maxValIsIncluded && !(val < maxVal))
951
0
        {
952
0
            CPLError(CE_Failure, CPLE_IllegalArg,
953
0
                     "Value of argument '%s' is %g, but should be < %g",
954
0
                     GetName().c_str(), val, maxVal);
955
0
            ret = false;
956
0
        }
957
0
    }
958
959
0
    return ret;
960
0
}
961
962
/************************************************************************/
963
/*                        CheckDuplicateValues()                        */
964
/************************************************************************/
965
966
template <class T>
967
static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
968
                                 const std::vector<T> &values)
969
0
{
970
0
    auto tmpValues = values;
971
0
    bool bHasDupValues = false;
972
    if constexpr (std::is_floating_point_v<T>)
973
0
    {
974
        // Avoid undefined behavior with NaN values
975
0
        std::sort(tmpValues.begin(), tmpValues.end(),
976
0
                  [](T a, T b)
977
0
                  {
978
0
                      if (std::isnan(a) && !std::isnan(b))
979
0
                          return true;
980
0
                      if (std::isnan(b))
981
0
                          return false;
982
0
                      return a < b;
983
0
                  });
984
985
0
        bHasDupValues =
986
0
            std::adjacent_find(tmpValues.begin(), tmpValues.end(),
987
0
                               [](T a, T b)
988
0
                               {
989
0
                                   if (std::isnan(a) && std::isnan(b))
990
0
                                       return true;
991
0
                                   return a == b;
992
0
                               }) != tmpValues.end();
993
    }
994
    else
995
0
    {
996
0
        std::sort(tmpValues.begin(), tmpValues.end());
997
0
        bHasDupValues = std::adjacent_find(tmpValues.begin(),
998
0
                                           tmpValues.end()) != tmpValues.end();
999
0
    }
1000
0
    if (bHasDupValues)
1001
0
    {
1002
0
        CPLError(CE_Failure, CPLE_AppDefined,
1003
0
                 "'%s' must be a list of unique values.",
1004
0
                 arg->GetName().c_str());
1005
0
        return false;
1006
0
    }
1007
0
    return true;
1008
0
}
Unexecuted instantiation: gdalalgorithm.cpp:bool CheckDuplicateValues<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(GDALAlgorithmArg const*, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)
Unexecuted instantiation: gdalalgorithm.cpp:bool CheckDuplicateValues<int>(GDALAlgorithmArg const*, std::__1::vector<int, std::__1::allocator<int> > const&)
Unexecuted instantiation: gdalalgorithm.cpp:bool CheckDuplicateValues<double>(GDALAlgorithmArg const*, std::__1::vector<double, std::__1::allocator<double> > const&)
1009
1010
/************************************************************************/
1011
/*               GDALAlgorithmArg::RunValidationActions()               */
1012
/************************************************************************/
1013
1014
bool GDALAlgorithmArg::RunValidationActions()
1015
0
{
1016
0
    bool ret = true;
1017
1018
0
    if (GetType() == GAAT_STRING && !GetChoices().empty())
1019
0
    {
1020
0
        auto &val = Get<std::string>();
1021
0
        std::string validVal = ValidateChoice(val);
1022
0
        if (validVal.empty())
1023
0
            ret = false;
1024
0
        else
1025
0
            val = std::move(validVal);
1026
0
    }
1027
0
    else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1028
0
    {
1029
0
        auto &values = Get<std::vector<std::string>>();
1030
0
        for (std::string &val : values)
1031
0
        {
1032
0
            std::string validVal = ValidateChoice(val);
1033
0
            if (validVal.empty())
1034
0
                ret = false;
1035
0
            else
1036
0
                val = std::move(validVal);
1037
0
        }
1038
0
    }
1039
1040
0
    const auto CheckMinCharCount =
1041
0
        [this, &ret](const std::string &val, int nMinCharCount)
1042
0
    {
1043
0
        if (val.size() < static_cast<size_t>(nMinCharCount))
1044
0
        {
1045
0
            CPLError(CE_Failure, CPLE_IllegalArg,
1046
0
                     "Value of argument '%s' is '%s', but should have at least "
1047
0
                     "%d character%s",
1048
0
                     GetName().c_str(), val.c_str(), nMinCharCount,
1049
0
                     nMinCharCount > 1 ? "s" : "");
1050
0
            ret = false;
1051
0
        }
1052
0
    };
1053
1054
0
    const auto CheckMaxCharCount =
1055
0
        [this, &ret](const std::string &val, int nMaxCharCount)
1056
0
    {
1057
0
        if (val.size() > static_cast<size_t>(nMaxCharCount))
1058
0
        {
1059
0
            CPLError(
1060
0
                CE_Failure, CPLE_IllegalArg,
1061
0
                "Value of argument '%s' is '%s', but should have no more than "
1062
0
                "%d character%s",
1063
0
                GetName().c_str(), val.c_str(), nMaxCharCount,
1064
0
                nMaxCharCount > 1 ? "s" : "");
1065
0
            ret = false;
1066
0
        }
1067
0
    };
1068
1069
0
    switch (GetType())
1070
0
    {
1071
0
        case GAAT_BOOLEAN:
1072
0
            break;
1073
1074
0
        case GAAT_STRING:
1075
0
        {
1076
0
            const auto &val = Get<std::string>();
1077
0
            const int nMinCharCount = GetMinCharCount();
1078
0
            if (nMinCharCount > 0)
1079
0
            {
1080
0
                CheckMinCharCount(val, nMinCharCount);
1081
0
            }
1082
1083
0
            const int nMaxCharCount = GetMaxCharCount();
1084
0
            CheckMaxCharCount(val, nMaxCharCount);
1085
0
            break;
1086
0
        }
1087
1088
0
        case GAAT_STRING_LIST:
1089
0
        {
1090
0
            const int nMinCharCount = GetMinCharCount();
1091
0
            const int nMaxCharCount = GetMaxCharCount();
1092
0
            const auto &values = Get<std::vector<std::string>>();
1093
0
            for (const auto &val : values)
1094
0
            {
1095
0
                if (nMinCharCount > 0)
1096
0
                    CheckMinCharCount(val, nMinCharCount);
1097
0
                CheckMaxCharCount(val, nMaxCharCount);
1098
0
            }
1099
1100
0
            if (!GetDuplicateValuesAllowed() &&
1101
0
                !CheckDuplicateValues(this, values))
1102
0
                ret = false;
1103
0
            break;
1104
0
        }
1105
1106
0
        case GAAT_INTEGER:
1107
0
        {
1108
0
            ret = ValidateIntRange(Get<int>()) && ret;
1109
0
            break;
1110
0
        }
1111
1112
0
        case GAAT_INTEGER_LIST:
1113
0
        {
1114
0
            const auto &values = Get<std::vector<int>>();
1115
0
            for (int v : values)
1116
0
                ret = ValidateIntRange(v) && ret;
1117
1118
0
            if (!GetDuplicateValuesAllowed() &&
1119
0
                !CheckDuplicateValues(this, values))
1120
0
                ret = false;
1121
0
            break;
1122
0
        }
1123
1124
0
        case GAAT_REAL:
1125
0
        {
1126
0
            ret = ValidateRealRange(Get<double>()) && ret;
1127
0
            break;
1128
0
        }
1129
1130
0
        case GAAT_REAL_LIST:
1131
0
        {
1132
0
            const auto &values = Get<std::vector<double>>();
1133
0
            for (double v : values)
1134
0
                ret = ValidateRealRange(v) && ret;
1135
1136
0
            if (!GetDuplicateValuesAllowed() &&
1137
0
                !CheckDuplicateValues(this, values))
1138
0
                ret = false;
1139
0
            break;
1140
0
        }
1141
1142
0
        case GAAT_DATASET:
1143
0
            break;
1144
1145
0
        case GAAT_DATASET_LIST:
1146
0
        {
1147
0
            if (!GetDuplicateValuesAllowed())
1148
0
            {
1149
0
                const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1150
0
                std::vector<std::string> aosValues;
1151
0
                for (const auto &v : values)
1152
0
                {
1153
0
                    const GDALDataset *poDS = v.GetDatasetRef();
1154
0
                    if (poDS)
1155
0
                    {
1156
0
                        auto poDriver = poDS->GetDriver();
1157
                        // The dataset name for a MEM driver is not relevant,
1158
                        // so use the pointer address
1159
0
                        if ((poDriver &&
1160
0
                             EQUAL(poDriver->GetDescription(), "MEM")) ||
1161
0
                            poDS->GetDescription()[0] == 0)
1162
0
                        {
1163
0
                            aosValues.push_back(CPLSPrintf("%p", poDS));
1164
0
                        }
1165
0
                        else
1166
0
                        {
1167
0
                            aosValues.push_back(poDS->GetDescription());
1168
0
                        }
1169
0
                    }
1170
0
                    else
1171
0
                    {
1172
0
                        aosValues.push_back(v.GetName());
1173
0
                    }
1174
0
                }
1175
0
                if (!CheckDuplicateValues(this, aosValues))
1176
0
                    ret = false;
1177
0
            }
1178
0
            break;
1179
0
        }
1180
0
    }
1181
1182
0
    if (GDALAlgorithmArgTypeIsList(GetType()))
1183
0
    {
1184
0
        int valueCount = 0;
1185
0
        if (GetType() == GAAT_STRING_LIST)
1186
0
        {
1187
0
            valueCount =
1188
0
                static_cast<int>(Get<std::vector<std::string>>().size());
1189
0
        }
1190
0
        else if (GetType() == GAAT_INTEGER_LIST)
1191
0
        {
1192
0
            valueCount = static_cast<int>(Get<std::vector<int>>().size());
1193
0
        }
1194
0
        else if (GetType() == GAAT_REAL_LIST)
1195
0
        {
1196
0
            valueCount = static_cast<int>(Get<std::vector<double>>().size());
1197
0
        }
1198
0
        else if (GetType() == GAAT_DATASET_LIST)
1199
0
        {
1200
0
            valueCount = static_cast<int>(
1201
0
                Get<std::vector<GDALArgDatasetValue>>().size());
1202
0
        }
1203
1204
0
        if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1205
0
        {
1206
0
            ReportError(CE_Failure, CPLE_AppDefined,
1207
0
                        "%d value%s been specified for argument '%s', "
1208
0
                        "whereas exactly %d %s expected.",
1209
0
                        valueCount, valueCount > 1 ? "s have" : " has",
1210
0
                        GetName().c_str(), GetMinCount(),
1211
0
                        GetMinCount() > 1 ? "were" : "was");
1212
0
            ret = false;
1213
0
        }
1214
0
        else if (valueCount < GetMinCount())
1215
0
        {
1216
0
            ReportError(CE_Failure, CPLE_AppDefined,
1217
0
                        "Only %d value%s been specified for argument '%s', "
1218
0
                        "whereas at least %d %s expected.",
1219
0
                        valueCount, valueCount > 1 ? "s have" : " has",
1220
0
                        GetName().c_str(), GetMinCount(),
1221
0
                        GetMinCount() > 1 ? "were" : "was");
1222
0
            ret = false;
1223
0
        }
1224
0
        else if (valueCount > GetMaxCount())
1225
0
        {
1226
0
            ReportError(CE_Failure, CPLE_AppDefined,
1227
0
                        "%d value%s been specified for argument '%s', "
1228
0
                        "whereas at most %d %s expected.",
1229
0
                        valueCount, valueCount > 1 ? "s have" : " has",
1230
0
                        GetName().c_str(), GetMaxCount(),
1231
0
                        GetMaxCount() > 1 ? "were" : "was");
1232
0
            ret = false;
1233
0
        }
1234
0
    }
1235
1236
0
    if (ret)
1237
0
    {
1238
0
        for (const auto &f : m_validationActions)
1239
0
        {
1240
0
            if (!f())
1241
0
                ret = false;
1242
0
        }
1243
0
    }
1244
1245
0
    return ret;
1246
0
}
1247
1248
/************************************************************************/
1249
/*                   GDALAlgorithmArg::ReportError()                    */
1250
/************************************************************************/
1251
1252
void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1253
                                   const char *fmt, ...) const
1254
0
{
1255
0
    va_list args;
1256
0
    va_start(args, fmt);
1257
0
    if (m_owner)
1258
0
    {
1259
0
        m_owner->ReportError(eErrClass, err_no, "%s",
1260
0
                             CPLString().vPrintf(fmt, args).c_str());
1261
0
    }
1262
0
    else
1263
0
    {
1264
0
        CPLError(eErrClass, err_no, "%s",
1265
0
                 CPLString().vPrintf(fmt, args).c_str());
1266
0
    }
1267
0
    va_end(args);
1268
0
}
1269
1270
/************************************************************************/
1271
/*                 GDALAlgorithmArg::GetEscapedString()                 */
1272
/************************************************************************/
1273
1274
/* static */
1275
std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1276
0
{
1277
0
    if (s.find_first_of("\" \\,") != std::string::npos &&
1278
0
        !(s.size() > 4 &&
1279
0
          s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1280
0
          s[1] == ' ' && s[s.size() - 2] == ' ' &&
1281
0
          s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1282
0
    {
1283
0
        return std::string("\"")
1284
0
            .append(
1285
0
                CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1286
0
            .append("\"");
1287
0
    }
1288
0
    else
1289
0
    {
1290
0
        return s;
1291
0
    }
1292
0
}
1293
1294
/************************************************************************/
1295
/*                    GDALAlgorithmArg::Serialize()                     */
1296
/************************************************************************/
1297
1298
bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1299
                                 bool absolutePath) const
1300
0
{
1301
0
    serializedArg.clear();
1302
1303
0
    if (!IsExplicitlySet())
1304
0
    {
1305
0
        return false;
1306
0
    }
1307
1308
0
    std::string ret = "--";
1309
0
    ret += GetName();
1310
0
    if (GetType() == GAAT_BOOLEAN)
1311
0
    {
1312
0
        serializedArg = std::move(ret);
1313
0
        return true;
1314
0
    }
1315
1316
0
    const auto AddListValueSeparator = [this, &ret]()
1317
0
    {
1318
0
        if (GetPackedValuesAllowed())
1319
0
        {
1320
0
            ret += ',';
1321
0
        }
1322
0
        else
1323
0
        {
1324
0
            ret += " --";
1325
0
            ret += GetName();
1326
0
            ret += ' ';
1327
0
        }
1328
0
    };
1329
1330
0
    const auto MakeAbsolutePath = [](const std::string &filename)
1331
0
    {
1332
0
        VSIStatBufL sStat;
1333
0
        if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1334
0
            !CPLIsFilenameRelative(filename.c_str()))
1335
0
            return filename;
1336
0
        char *pszCWD = CPLGetCurrentDir();
1337
0
        if (!pszCWD)
1338
0
            return filename;
1339
0
        const auto absPath =
1340
0
            CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1341
0
        CPLFree(pszCWD);
1342
0
        return absPath;
1343
0
    };
1344
1345
0
    ret += ' ';
1346
0
    switch (GetType())
1347
0
    {
1348
0
        case GAAT_BOOLEAN:
1349
0
            break;
1350
0
        case GAAT_STRING:
1351
0
        {
1352
0
            const auto &val = Get<std::string>();
1353
0
            ret += GetEscapedString(val);
1354
0
            break;
1355
0
        }
1356
0
        case GAAT_INTEGER:
1357
0
        {
1358
0
            ret += CPLSPrintf("%d", Get<int>());
1359
0
            break;
1360
0
        }
1361
0
        case GAAT_REAL:
1362
0
        {
1363
0
            ret += CPLSPrintf("%.17g", Get<double>());
1364
0
            break;
1365
0
        }
1366
0
        case GAAT_DATASET:
1367
0
        {
1368
0
            const auto &val = Get<GDALArgDatasetValue>();
1369
0
            const auto &str = val.GetName();
1370
0
            if (str.empty())
1371
0
            {
1372
0
                return false;
1373
0
            }
1374
0
            ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1375
0
            break;
1376
0
        }
1377
0
        case GAAT_STRING_LIST:
1378
0
        {
1379
0
            const auto &vals = Get<std::vector<std::string>>();
1380
0
            for (size_t i = 0; i < vals.size(); ++i)
1381
0
            {
1382
0
                if (i > 0)
1383
0
                    AddListValueSeparator();
1384
0
                ret += GetEscapedString(vals[i]);
1385
0
            }
1386
0
            break;
1387
0
        }
1388
0
        case GAAT_INTEGER_LIST:
1389
0
        {
1390
0
            const auto &vals = Get<std::vector<int>>();
1391
0
            for (size_t i = 0; i < vals.size(); ++i)
1392
0
            {
1393
0
                if (i > 0)
1394
0
                    AddListValueSeparator();
1395
0
                ret += CPLSPrintf("%d", vals[i]);
1396
0
            }
1397
0
            break;
1398
0
        }
1399
0
        case GAAT_REAL_LIST:
1400
0
        {
1401
0
            const auto &vals = Get<std::vector<double>>();
1402
0
            for (size_t i = 0; i < vals.size(); ++i)
1403
0
            {
1404
0
                if (i > 0)
1405
0
                    AddListValueSeparator();
1406
0
                ret += CPLSPrintf("%.17g", vals[i]);
1407
0
            }
1408
0
            break;
1409
0
        }
1410
0
        case GAAT_DATASET_LIST:
1411
0
        {
1412
0
            const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1413
0
            for (size_t i = 0; i < vals.size(); ++i)
1414
0
            {
1415
0
                if (i > 0)
1416
0
                    AddListValueSeparator();
1417
0
                const auto &val = vals[i];
1418
0
                const auto &str = val.GetName();
1419
0
                if (str.empty())
1420
0
                {
1421
0
                    return false;
1422
0
                }
1423
0
                ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1424
0
                                                     : str);
1425
0
            }
1426
0
            break;
1427
0
        }
1428
0
    }
1429
1430
0
    serializedArg = std::move(ret);
1431
0
    return true;
1432
0
}
1433
1434
/************************************************************************/
1435
/*                  ~GDALInConstructionAlgorithmArg()                   */
1436
/************************************************************************/
1437
1438
GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1439
1440
/************************************************************************/
1441
/*              GDALInConstructionAlgorithmArg::AddAlias()              */
1442
/************************************************************************/
1443
1444
GDALInConstructionAlgorithmArg &
1445
GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1446
0
{
1447
0
    m_decl.AddAlias(alias);
1448
0
    if (m_owner)
1449
0
        m_owner->AddAliasFor(this, alias);
1450
0
    return *this;
1451
0
}
1452
1453
/************************************************************************/
1454
/*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
1455
/************************************************************************/
1456
1457
GDALInConstructionAlgorithmArg &
1458
GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1459
0
{
1460
0
    m_decl.AddHiddenAlias(alias);
1461
0
    if (m_owner)
1462
0
        m_owner->AddAliasFor(this, alias);
1463
0
    return *this;
1464
0
}
1465
1466
/************************************************************************/
1467
/*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
1468
/************************************************************************/
1469
1470
GDALInConstructionAlgorithmArg &
1471
GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1472
0
{
1473
0
    m_decl.AddShortNameAlias(shortNameAlias);
1474
0
    if (m_owner)
1475
0
        m_owner->AddShortNameAliasFor(this, shortNameAlias);
1476
0
    return *this;
1477
0
}
1478
1479
/************************************************************************/
1480
/*           GDALInConstructionAlgorithmArg::SetPositional()            */
1481
/************************************************************************/
1482
1483
GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1484
0
{
1485
0
    m_decl.SetPositional();
1486
0
    if (m_owner)
1487
0
        m_owner->SetPositional(this);
1488
0
    return *this;
1489
0
}
1490
1491
/************************************************************************/
1492
/*              GDALArgDatasetValue::GDALArgDatasetValue()              */
1493
/************************************************************************/
1494
1495
GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1496
0
    : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1497
0
      m_nameSet(true)
1498
0
{
1499
0
    if (m_poDS)
1500
0
        m_poDS->Reference();
1501
0
}
1502
1503
/************************************************************************/
1504
/*                      GDALArgDatasetValue::Set()                      */
1505
/************************************************************************/
1506
1507
void GDALArgDatasetValue::Set(const std::string &name)
1508
0
{
1509
0
    Close();
1510
0
    m_name = name;
1511
0
    m_nameSet = true;
1512
0
    if (m_ownerArg)
1513
0
        m_ownerArg->NotifyValueSet();
1514
0
}
1515
1516
/************************************************************************/
1517
/*                      GDALArgDatasetValue::Set()                      */
1518
/************************************************************************/
1519
1520
void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1521
0
{
1522
0
    Close();
1523
0
    m_poDS = poDS.release();
1524
0
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1525
0
    m_nameSet = true;
1526
0
    if (m_ownerArg)
1527
0
        m_ownerArg->NotifyValueSet();
1528
0
}
1529
1530
/************************************************************************/
1531
/*                      GDALArgDatasetValue::Set()                      */
1532
/************************************************************************/
1533
1534
void GDALArgDatasetValue::Set(GDALDataset *poDS)
1535
0
{
1536
0
    Close();
1537
0
    m_poDS = poDS;
1538
0
    if (m_poDS)
1539
0
        m_poDS->Reference();
1540
0
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1541
0
    m_nameSet = true;
1542
0
    if (m_ownerArg)
1543
0
        m_ownerArg->NotifyValueSet();
1544
0
}
1545
1546
/************************************************************************/
1547
/*                    GDALArgDatasetValue::SetFrom()                    */
1548
/************************************************************************/
1549
1550
void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1551
0
{
1552
0
    Close();
1553
0
    m_name = other.m_name;
1554
0
    m_nameSet = other.m_nameSet;
1555
0
    m_poDS = other.m_poDS;
1556
0
    if (m_poDS)
1557
0
        m_poDS->Reference();
1558
0
}
1559
1560
/************************************************************************/
1561
/*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
1562
/************************************************************************/
1563
1564
GDALArgDatasetValue::~GDALArgDatasetValue()
1565
0
{
1566
0
    Close();
1567
0
}
1568
1569
/************************************************************************/
1570
/*                     GDALArgDatasetValue::Close()                     */
1571
/************************************************************************/
1572
1573
bool GDALArgDatasetValue::Close()
1574
0
{
1575
0
    bool ret = true;
1576
0
    if (m_poDS && m_poDS->Dereference() == 0)
1577
0
    {
1578
0
        ret = m_poDS->Close() == CE_None;
1579
0
        delete m_poDS;
1580
0
    }
1581
0
    m_poDS = nullptr;
1582
0
    return ret;
1583
0
}
1584
1585
/************************************************************************/
1586
/*                   GDALArgDatasetValue::operator=()                   */
1587
/************************************************************************/
1588
1589
GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1590
0
{
1591
0
    Close();
1592
0
    m_poDS = other.m_poDS;
1593
0
    m_name = other.m_name;
1594
0
    m_nameSet = other.m_nameSet;
1595
0
    other.m_poDS = nullptr;
1596
0
    other.m_name.clear();
1597
0
    other.m_nameSet = false;
1598
0
    return *this;
1599
0
}
1600
1601
/************************************************************************/
1602
/*                  GDALArgDatasetValue::GetDataset()                   */
1603
/************************************************************************/
1604
1605
GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1606
0
{
1607
0
    if (m_poDS)
1608
0
        m_poDS->Reference();
1609
0
    return m_poDS;
1610
0
}
1611
1612
/************************************************************************/
1613
/*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
1614
/************************************************************************/
1615
1616
GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1617
0
    : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1618
0
{
1619
0
    other.m_poDS = nullptr;
1620
0
    other.m_name.clear();
1621
0
}
1622
1623
/************************************************************************/
1624
/*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
1625
/************************************************************************/
1626
1627
GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1628
    bool noneAllowed, const std::vector<std::string> &specialValues)
1629
0
{
1630
0
    if (GetType() != GAAT_STRING)
1631
0
    {
1632
0
        CPLError(CE_Failure, CPLE_AppDefined,
1633
0
                 "SetIsCRSArg() can only be called on a String argument");
1634
0
        return *this;
1635
0
    }
1636
0
    AddValidationAction(
1637
0
        [this, noneAllowed, specialValues]()
1638
0
        {
1639
0
            const std::string &osVal =
1640
0
                static_cast<const GDALInConstructionAlgorithmArg *>(this)
1641
0
                    ->Get<std::string>();
1642
0
            if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1643
0
                return true;
1644
1645
0
            if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1646
0
                std::find(specialValues.begin(), specialValues.end(), osVal) ==
1647
0
                    specialValues.end())
1648
0
            {
1649
0
                OGRSpatialReference oSRS;
1650
0
                if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1651
0
                {
1652
0
                    m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1653
0
                                         "Invalid value for '%s' argument",
1654
0
                                         GetName().c_str());
1655
0
                    return false;
1656
0
                }
1657
0
            }
1658
0
            return true;
1659
0
        });
1660
1661
0
    SetAutoCompleteFunction(
1662
0
        [this, noneAllowed, specialValues](const std::string &currentValue)
1663
0
        {
1664
0
            bool bIsRaster = false;
1665
0
            OGREnvelope sDatasetLongLatEnv;
1666
0
            std::string osCelestialBodyName;
1667
0
            if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1668
0
            {
1669
0
                auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1670
0
                if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1671
0
                {
1672
0
                    auto &val =
1673
0
                        inputArg->Get<std::vector<GDALArgDatasetValue>>();
1674
0
                    if (val.size() == 1)
1675
0
                    {
1676
0
                        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1677
0
                        auto poDS = std::unique_ptr<GDALDataset>(
1678
0
                            GDALDataset::Open(val[0].GetName().c_str()));
1679
0
                        if (poDS)
1680
0
                        {
1681
0
                            bIsRaster = poDS->GetRasterCount() != 0;
1682
0
                            if (auto poCRS = poDS->GetSpatialRef())
1683
0
                            {
1684
0
                                const char *pszCelestialBodyName =
1685
0
                                    poCRS->GetCelestialBodyName();
1686
0
                                if (pszCelestialBodyName)
1687
0
                                    osCelestialBodyName = pszCelestialBodyName;
1688
1689
0
                                if (!pszCelestialBodyName ||
1690
0
                                    !EQUAL(pszCelestialBodyName, "Earth"))
1691
0
                                {
1692
0
                                    OGRSpatialReference oLongLat;
1693
0
                                    oLongLat.CopyGeogCSFrom(poCRS);
1694
0
                                    oLongLat.SetAxisMappingStrategy(
1695
0
                                        OAMS_TRADITIONAL_GIS_ORDER);
1696
0
                                    poDS->GetExtent(&sDatasetLongLatEnv,
1697
0
                                                    &oLongLat);
1698
0
                                }
1699
0
                                else
1700
0
                                {
1701
0
                                    poDS->GetExtentWGS84LongLat(
1702
0
                                        &sDatasetLongLatEnv);
1703
0
                                }
1704
0
                            }
1705
0
                        }
1706
0
                    }
1707
0
                }
1708
0
            }
1709
1710
0
            const auto IsCRSCompatible =
1711
0
                [bIsRaster, &sDatasetLongLatEnv,
1712
0
                 &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1713
0
            {
1714
0
                if (!sDatasetLongLatEnv.IsInit())
1715
0
                    return true;
1716
0
                return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1717
0
                       !(bIsRaster &&
1718
0
                         crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1719
0
                       crsInfo->dfWestLongitudeDeg <
1720
0
                           crsInfo->dfEastLongitudeDeg &&
1721
0
                       sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1722
0
                       sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1723
0
                       sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1724
0
                       sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1725
0
                       ((!osCelestialBodyName.empty() &&
1726
0
                         crsInfo->pszCelestialBodyName &&
1727
0
                         osCelestialBodyName ==
1728
0
                             crsInfo->pszCelestialBodyName) ||
1729
0
                        (osCelestialBodyName.empty() &&
1730
0
                         !crsInfo->pszCelestialBodyName));
1731
0
            };
1732
1733
0
            std::vector<std::string> oRet;
1734
0
            if (noneAllowed)
1735
0
                oRet.push_back("none");
1736
0
            oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1737
0
            if (!currentValue.empty())
1738
0
            {
1739
0
                const CPLStringList aosTokens(
1740
0
                    CSLTokenizeString2(currentValue.c_str(), ":", 0));
1741
0
                int nCount = 0;
1742
0
                std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1743
0
                    pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1744
0
                                                           nullptr, &nCount),
1745
0
                             OSRDestroyCRSInfoList);
1746
0
                std::string osCode;
1747
1748
0
                std::vector<const OSRCRSInfo *> candidates;
1749
0
                for (int i = 0; i < nCount; ++i)
1750
0
                {
1751
0
                    const auto *entry = (pCRSList.get())[i];
1752
0
                    if (!entry->bDeprecated && IsCRSCompatible(entry))
1753
0
                    {
1754
0
                        if (aosTokens.size() == 1 ||
1755
0
                            STARTS_WITH(entry->pszCode, aosTokens[1]))
1756
0
                        {
1757
0
                            if (candidates.empty())
1758
0
                                osCode = entry->pszCode;
1759
0
                            candidates.push_back(entry);
1760
0
                        }
1761
0
                    }
1762
0
                }
1763
0
                if (candidates.size() == 1)
1764
0
                {
1765
0
                    oRet.push_back(std::move(osCode));
1766
0
                }
1767
0
                else
1768
0
                {
1769
0
                    if (sDatasetLongLatEnv.IsInit())
1770
0
                    {
1771
0
                        std::sort(
1772
0
                            candidates.begin(), candidates.end(),
1773
0
                            [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1774
0
                            {
1775
0
                                const double dfXa =
1776
0
                                    a->dfWestLongitudeDeg >
1777
0
                                            a->dfEastLongitudeDeg
1778
0
                                        ? a->dfWestLongitudeDeg -
1779
0
                                              a->dfEastLongitudeDeg
1780
0
                                        : (180 - a->dfWestLongitudeDeg) +
1781
0
                                              (a->dfEastLongitudeDeg - -180);
1782
0
                                const double dfYa = a->dfNorthLatitudeDeg -
1783
0
                                                    a->dfSouthLatitudeDeg;
1784
0
                                const double dfXb =
1785
0
                                    b->dfWestLongitudeDeg >
1786
0
                                            b->dfEastLongitudeDeg
1787
0
                                        ? b->dfWestLongitudeDeg -
1788
0
                                              b->dfEastLongitudeDeg
1789
0
                                        : (180 - b->dfWestLongitudeDeg) +
1790
0
                                              (b->dfEastLongitudeDeg - -180);
1791
0
                                const double dfYb = b->dfNorthLatitudeDeg -
1792
0
                                                    b->dfSouthLatitudeDeg;
1793
0
                                const double diffArea =
1794
0
                                    dfXa * dfYa - dfXb * dfYb;
1795
0
                                if (diffArea < 0)
1796
0
                                    return true;
1797
0
                                if (diffArea == 0)
1798
0
                                {
1799
0
                                    if (std::string_view(a->pszName) ==
1800
0
                                        b->pszName)
1801
0
                                    {
1802
0
                                        if (a->eType ==
1803
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1804
0
                                            b->eType !=
1805
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_2D)
1806
0
                                            return true;
1807
0
                                        if (a->eType ==
1808
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1809
0
                                            b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1810
0
                                            return true;
1811
0
                                        return false;
1812
0
                                    }
1813
0
                                    return std::string_view(a->pszCode) <
1814
0
                                           b->pszCode;
1815
0
                                }
1816
0
                                return false;
1817
0
                            });
1818
0
                    }
1819
1820
0
                    for (const auto *entry : candidates)
1821
0
                    {
1822
0
                        std::string val = std::string(entry->pszCode)
1823
0
                                              .append(" -- ")
1824
0
                                              .append(entry->pszName);
1825
0
                        if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1826
0
                            val.append(" (geographic 2D)");
1827
0
                        else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1828
0
                            val.append(" (geographic 3D)");
1829
0
                        else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1830
0
                            val.append(" (geocentric)");
1831
0
                        oRet.push_back(std::move(val));
1832
0
                    }
1833
0
                }
1834
0
            }
1835
0
            if (currentValue.empty() || oRet.empty())
1836
0
            {
1837
0
                const CPLStringList aosAuthorities(
1838
0
                    OSRGetAuthorityListFromDatabase());
1839
0
                for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1840
0
                {
1841
0
                    int nCount = 0;
1842
0
                    OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1843
0
                        pszAuth, nullptr, &nCount));
1844
0
                    if (nCount)
1845
0
                        oRet.push_back(std::string(pszAuth).append(":"));
1846
0
                }
1847
0
            }
1848
0
            return oRet;
1849
0
        });
1850
1851
0
    return *this;
1852
0
}
1853
1854
/************************************************************************/
1855
/*                    GDALAlgorithm::GDALAlgorithm()                    */
1856
/************************************************************************/
1857
1858
GDALAlgorithm::GDALAlgorithm(const std::string &name,
1859
                             const std::string &description,
1860
                             const std::string &helpURL)
1861
0
    : m_name(name), m_description(description), m_helpURL(helpURL),
1862
0
      m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1863
0
                        ? "https://gdal.org" + m_helpURL
1864
0
                        : m_helpURL)
1865
0
{
1866
0
    auto &helpArg =
1867
0
        AddArg("help", 'h', _("Display help message and exit"),
1868
0
               &m_helpRequested)
1869
0
            .SetHiddenForAPI()
1870
0
            .SetCategory(GAAC_COMMON)
1871
0
            .AddAction([this]()
1872
0
                       { m_specialActionRequested = m_calledFromCommandLine; });
1873
0
    auto &helpDocArg =
1874
0
        AddArg("help-doc", 0,
1875
0
               _("Display help message for use by documentation"),
1876
0
               &m_helpDocRequested)
1877
0
            .SetHidden()
1878
0
            .AddAction([this]()
1879
0
                       { m_specialActionRequested = m_calledFromCommandLine; });
1880
0
    auto &jsonUsageArg =
1881
0
        AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1882
0
               &m_JSONUsageRequested)
1883
0
            .SetHiddenForAPI()
1884
0
            .SetCategory(GAAC_COMMON)
1885
0
            .AddAction([this]()
1886
0
                       { m_specialActionRequested = m_calledFromCommandLine; });
1887
0
    AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1888
0
        .SetMetaVar("<KEY>=<VALUE>")
1889
0
        .SetHiddenForAPI()
1890
0
        .SetCategory(GAAC_COMMON)
1891
0
        .AddAction(
1892
0
            [this]()
1893
0
            {
1894
0
                ReportError(
1895
0
                    CE_Warning, CPLE_AppDefined,
1896
0
                    "Configuration options passed with the 'config' argument "
1897
0
                    "are ignored");
1898
0
            });
1899
1900
0
    AddValidationAction(
1901
0
        [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1902
0
        {
1903
0
            if (!m_calledFromCommandLine && m_specialActionRequested)
1904
0
            {
1905
0
                for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1906
0
                {
1907
0
                    if (arg->IsExplicitlySet())
1908
0
                    {
1909
0
                        ReportError(CE_Failure, CPLE_AppDefined,
1910
0
                                    "'%s' argument only available when called "
1911
0
                                    "from command line",
1912
0
                                    arg->GetName().c_str());
1913
0
                        return false;
1914
0
                    }
1915
0
                }
1916
0
            }
1917
0
            return true;
1918
0
        });
1919
0
}
1920
1921
/************************************************************************/
1922
/*                   GDALAlgorithm::~GDALAlgorithm()                    */
1923
/************************************************************************/
1924
1925
0
GDALAlgorithm::~GDALAlgorithm() = default;
1926
1927
/************************************************************************/
1928
/*                    GDALAlgorithm::ParseArgument()                    */
1929
/************************************************************************/
1930
1931
bool GDALAlgorithm::ParseArgument(
1932
    GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1933
    std::map<
1934
        GDALAlgorithmArg *,
1935
        std::variant<std::vector<std::string>, std::vector<int>,
1936
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
1937
        &inConstructionValues)
1938
0
{
1939
0
    const bool isListArg =
1940
0
        GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1941
0
    if (arg->IsExplicitlySet() && !isListArg)
1942
0
    {
1943
        // Hack for "gdal info" to be able to pass an opened raster dataset
1944
        // by "gdal raster info" to the "gdal vector info" algorithm.
1945
0
        if (arg->SkipIfAlreadySet())
1946
0
        {
1947
0
            arg->SetSkipIfAlreadySet(false);
1948
0
            return true;
1949
0
        }
1950
1951
0
        ReportError(CE_Failure, CPLE_IllegalArg,
1952
0
                    "Argument '%s' has already been specified.", name.c_str());
1953
0
        return false;
1954
0
    }
1955
1956
0
    if (!arg->GetRepeatedArgAllowed() &&
1957
0
        cpl::contains(inConstructionValues, arg))
1958
0
    {
1959
0
        ReportError(CE_Failure, CPLE_IllegalArg,
1960
0
                    "Argument '%s' has already been specified.", name.c_str());
1961
0
        return false;
1962
0
    }
1963
1964
0
    switch (arg->GetType())
1965
0
    {
1966
0
        case GAAT_BOOLEAN:
1967
0
        {
1968
0
            if (value.empty() || value == "true")
1969
0
                return arg->Set(true);
1970
0
            else if (value == "false")
1971
0
                return arg->Set(false);
1972
0
            else
1973
0
            {
1974
0
                ReportError(
1975
0
                    CE_Failure, CPLE_IllegalArg,
1976
0
                    "Invalid value '%s' for boolean argument '%s'. Should be "
1977
0
                    "'true' or 'false'.",
1978
0
                    value.c_str(), name.c_str());
1979
0
                return false;
1980
0
            }
1981
0
        }
1982
1983
0
        case GAAT_STRING:
1984
0
        {
1985
0
            return arg->Set(value);
1986
0
        }
1987
1988
0
        case GAAT_INTEGER:
1989
0
        {
1990
0
            errno = 0;
1991
0
            char *endptr = nullptr;
1992
0
            const auto val = std::strtol(value.c_str(), &endptr, 10);
1993
0
            if (errno == 0 && endptr &&
1994
0
                endptr == value.c_str() + value.size() && val >= INT_MIN &&
1995
0
                val <= INT_MAX)
1996
0
            {
1997
0
                return arg->Set(static_cast<int>(val));
1998
0
            }
1999
0
            else
2000
0
            {
2001
0
                ReportError(CE_Failure, CPLE_IllegalArg,
2002
0
                            "Expected integer value for argument '%s', "
2003
0
                            "but got '%s'.",
2004
0
                            name.c_str(), value.c_str());
2005
0
                return false;
2006
0
            }
2007
0
        }
2008
2009
0
        case GAAT_REAL:
2010
0
        {
2011
0
            char *endptr = nullptr;
2012
0
            double dfValue = CPLStrtod(value.c_str(), &endptr);
2013
0
            if (endptr != value.c_str() + value.size())
2014
0
            {
2015
0
                ReportError(
2016
0
                    CE_Failure, CPLE_IllegalArg,
2017
0
                    "Expected real value for argument '%s', but got '%s'.",
2018
0
                    name.c_str(), value.c_str());
2019
0
                return false;
2020
0
            }
2021
0
            return arg->Set(dfValue);
2022
0
        }
2023
2024
0
        case GAAT_DATASET:
2025
0
        {
2026
0
            return arg->SetDatasetName(value);
2027
0
        }
2028
2029
0
        case GAAT_STRING_LIST:
2030
0
        {
2031
0
            const CPLStringList aosTokens(
2032
0
                arg->GetPackedValuesAllowed()
2033
0
                    ? CSLTokenizeString2(value.c_str(), ",",
2034
0
                                         CSLT_HONOURSTRINGS |
2035
0
                                             CSLT_PRESERVEQUOTES)
2036
0
                    : CSLAddString(nullptr, value.c_str()));
2037
0
            if (!cpl::contains(inConstructionValues, arg))
2038
0
            {
2039
0
                inConstructionValues[arg] = std::vector<std::string>();
2040
0
            }
2041
0
            auto &valueVector =
2042
0
                std::get<std::vector<std::string>>(inConstructionValues[arg]);
2043
0
            for (const char *v : aosTokens)
2044
0
            {
2045
0
                valueVector.push_back(v);
2046
0
            }
2047
0
            if (arg->GetMaxCount() == 1)
2048
0
            {
2049
0
                bool ret = arg->Set(std::move(valueVector));
2050
0
                inConstructionValues.erase(inConstructionValues.find(arg));
2051
0
                return ret;
2052
0
            }
2053
2054
0
            break;
2055
0
        }
2056
2057
0
        case GAAT_INTEGER_LIST:
2058
0
        {
2059
0
            const CPLStringList aosTokens(
2060
0
                arg->GetPackedValuesAllowed()
2061
0
                    ? CSLTokenizeString2(
2062
0
                          value.c_str(), ",",
2063
0
                          CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2064
0
                              CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2065
0
                    : CSLAddString(nullptr, value.c_str()));
2066
0
            if (!cpl::contains(inConstructionValues, arg))
2067
0
            {
2068
0
                inConstructionValues[arg] = std::vector<int>();
2069
0
            }
2070
0
            auto &valueVector =
2071
0
                std::get<std::vector<int>>(inConstructionValues[arg]);
2072
0
            for (const char *v : aosTokens)
2073
0
            {
2074
0
                errno = 0;
2075
0
                char *endptr = nullptr;
2076
0
                const auto val = std::strtol(v, &endptr, 10);
2077
0
                if (errno == 0 && endptr && endptr == v + strlen(v) &&
2078
0
                    val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2079
0
                {
2080
0
                    valueVector.push_back(static_cast<int>(val));
2081
0
                }
2082
0
                else
2083
0
                {
2084
0
                    ReportError(
2085
0
                        CE_Failure, CPLE_IllegalArg,
2086
0
                        "Expected list of integer value for argument '%s', "
2087
0
                        "but got '%s'.",
2088
0
                        name.c_str(), value.c_str());
2089
0
                    return false;
2090
0
                }
2091
0
            }
2092
0
            if (arg->GetMaxCount() == 1)
2093
0
            {
2094
0
                bool ret = arg->Set(std::move(valueVector));
2095
0
                inConstructionValues.erase(inConstructionValues.find(arg));
2096
0
                return ret;
2097
0
            }
2098
2099
0
            break;
2100
0
        }
2101
2102
0
        case GAAT_REAL_LIST:
2103
0
        {
2104
0
            const CPLStringList aosTokens(
2105
0
                arg->GetPackedValuesAllowed()
2106
0
                    ? CSLTokenizeString2(
2107
0
                          value.c_str(), ",",
2108
0
                          CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2109
0
                              CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2110
0
                    : CSLAddString(nullptr, value.c_str()));
2111
0
            if (!cpl::contains(inConstructionValues, arg))
2112
0
            {
2113
0
                inConstructionValues[arg] = std::vector<double>();
2114
0
            }
2115
0
            auto &valueVector =
2116
0
                std::get<std::vector<double>>(inConstructionValues[arg]);
2117
0
            for (const char *v : aosTokens)
2118
0
            {
2119
0
                char *endptr = nullptr;
2120
0
                double dfValue = CPLStrtod(v, &endptr);
2121
0
                if (strlen(v) == 0 || endptr != v + strlen(v))
2122
0
                {
2123
0
                    ReportError(
2124
0
                        CE_Failure, CPLE_IllegalArg,
2125
0
                        "Expected list of real value for argument '%s', "
2126
0
                        "but got '%s'.",
2127
0
                        name.c_str(), value.c_str());
2128
0
                    return false;
2129
0
                }
2130
0
                valueVector.push_back(dfValue);
2131
0
            }
2132
0
            if (arg->GetMaxCount() == 1)
2133
0
            {
2134
0
                bool ret = arg->Set(std::move(valueVector));
2135
0
                inConstructionValues.erase(inConstructionValues.find(arg));
2136
0
                return ret;
2137
0
            }
2138
2139
0
            break;
2140
0
        }
2141
2142
0
        case GAAT_DATASET_LIST:
2143
0
        {
2144
0
            if (!cpl::contains(inConstructionValues, arg))
2145
0
            {
2146
0
                inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2147
0
            }
2148
0
            auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2149
0
                inConstructionValues[arg]);
2150
0
            if (!value.empty() && value[0] == '{' && value.back() == '}')
2151
0
            {
2152
0
                valueVector.push_back(GDALArgDatasetValue(value));
2153
0
            }
2154
0
            else
2155
0
            {
2156
0
                const CPLStringList aosTokens(
2157
0
                    arg->GetPackedValuesAllowed()
2158
0
                        ? CSLTokenizeString2(value.c_str(), ",",
2159
0
                                             CSLT_HONOURSTRINGS |
2160
0
                                                 CSLT_STRIPLEADSPACES)
2161
0
                        : CSLAddString(nullptr, value.c_str()));
2162
0
                for (const char *v : aosTokens)
2163
0
                {
2164
0
                    valueVector.push_back(GDALArgDatasetValue(v));
2165
0
                }
2166
0
            }
2167
0
            if (arg->GetMaxCount() == 1)
2168
0
            {
2169
0
                bool ret = arg->Set(std::move(valueVector));
2170
0
                inConstructionValues.erase(inConstructionValues.find(arg));
2171
0
                return ret;
2172
0
            }
2173
2174
0
            break;
2175
0
        }
2176
0
    }
2177
2178
0
    return true;
2179
0
}
2180
2181
/************************************************************************/
2182
/*                     FormatSuggestionsAsString()                      */
2183
/************************************************************************/
2184
2185
static std::string
2186
FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
2187
                          bool addDashDashPrefix)
2188
0
{
2189
0
    std::string ret;
2190
0
    for (auto [i, suggestion] : cpl::enumerate(suggestions))
2191
0
    {
2192
0
        if (i > 0)
2193
0
        {
2194
0
            ret += (i + 1 < suggestions.size()) ? ", " : " or ";
2195
0
        }
2196
0
        ret += '\'';
2197
0
        if (addDashDashPrefix)
2198
0
            ret += "--";
2199
0
        ret += suggestion;
2200
0
        ret += '\'';
2201
0
    }
2202
0
    return ret;
2203
0
}
2204
2205
/************************************************************************/
2206
/*              GDALAlgorithm::ParseCommandLineArguments()              */
2207
/************************************************************************/
2208
2209
bool GDALAlgorithm::ParseCommandLineArguments(
2210
    const std::vector<std::string> &args)
2211
0
{
2212
0
    if (m_parsedSubStringAlreadyCalled)
2213
0
    {
2214
0
        ReportError(CE_Failure, CPLE_AppDefined,
2215
0
                    "ParseCommandLineArguments() can only be called once per "
2216
0
                    "instance.");
2217
0
        return false;
2218
0
    }
2219
0
    m_parsedSubStringAlreadyCalled = true;
2220
2221
    // AWS like syntax supported too (not advertized)
2222
0
    if (args.size() == 1 && args[0] == "help")
2223
0
    {
2224
0
        auto arg = GetArg("help");
2225
0
        assert(arg);
2226
0
        arg->Set(true);
2227
0
        arg->RunActions();
2228
0
        return true;
2229
0
    }
2230
2231
0
    if (HasSubAlgorithms())
2232
0
    {
2233
0
        if (args.empty())
2234
0
        {
2235
0
            ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2236
0
                        m_callPath.size() == 1 ? "command" : "subcommand");
2237
0
            return false;
2238
0
        }
2239
0
        if (!args[0].empty() && args[0][0] == '-')
2240
0
        {
2241
            // go on argument parsing
2242
0
        }
2243
0
        else
2244
0
        {
2245
0
            const auto nCounter = CPLGetErrorCounter();
2246
0
            m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2247
0
            if (m_selectedSubAlgHolder)
2248
0
            {
2249
0
                m_selectedSubAlg = m_selectedSubAlgHolder.get();
2250
0
                m_selectedSubAlg->SetReferencePathForRelativePaths(
2251
0
                    m_referencePath);
2252
0
                m_selectedSubAlg->m_executionForStreamOutput =
2253
0
                    m_executionForStreamOutput;
2254
0
                m_selectedSubAlg->m_calledFromCommandLine =
2255
0
                    m_calledFromCommandLine;
2256
0
                m_selectedSubAlg->m_skipValidationInParseCommandLine =
2257
0
                    m_skipValidationInParseCommandLine;
2258
0
                bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2259
0
                    std::vector<std::string>(args.begin() + 1, args.end()));
2260
0
                m_selectedSubAlg->PropagateSpecialActionTo(this);
2261
0
                return bRet;
2262
0
            }
2263
0
            else
2264
0
            {
2265
0
                if (!(CPLGetErrorCounter() == nCounter + 1 &&
2266
0
                      strstr(CPLGetLastErrorMsg(), "Do you mean")))
2267
0
                {
2268
0
                    ReportError(CE_Failure, CPLE_AppDefined,
2269
0
                                "Unknown command: '%s'", args[0].c_str());
2270
0
                }
2271
0
                return false;
2272
0
            }
2273
0
        }
2274
0
    }
2275
2276
0
    std::map<
2277
0
        GDALAlgorithmArg *,
2278
0
        std::variant<std::vector<std::string>, std::vector<int>,
2279
0
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
2280
0
        inConstructionValues;
2281
2282
0
    const auto ProcessInConstructionValues = [&inConstructionValues]()
2283
0
    {
2284
0
        for (auto &[arg, value] : inConstructionValues)
2285
0
        {
2286
0
            if (arg->GetType() == GAAT_STRING_LIST)
2287
0
            {
2288
0
                if (!arg->Set(std::get<std::vector<std::string>>(
2289
0
                        inConstructionValues[arg])))
2290
0
                {
2291
0
                    return false;
2292
0
                }
2293
0
            }
2294
0
            else if (arg->GetType() == GAAT_INTEGER_LIST)
2295
0
            {
2296
0
                if (!arg->Set(
2297
0
                        std::get<std::vector<int>>(inConstructionValues[arg])))
2298
0
                {
2299
0
                    return false;
2300
0
                }
2301
0
            }
2302
0
            else if (arg->GetType() == GAAT_REAL_LIST)
2303
0
            {
2304
0
                if (!arg->Set(std::get<std::vector<double>>(
2305
0
                        inConstructionValues[arg])))
2306
0
                {
2307
0
                    return false;
2308
0
                }
2309
0
            }
2310
0
            else if (arg->GetType() == GAAT_DATASET_LIST)
2311
0
            {
2312
0
                if (!arg->Set(
2313
0
                        std::move(std::get<std::vector<GDALArgDatasetValue>>(
2314
0
                            inConstructionValues[arg]))))
2315
0
                {
2316
0
                    return false;
2317
0
                }
2318
0
            }
2319
0
        }
2320
0
        return true;
2321
0
    };
2322
2323
0
    std::vector<std::string> lArgs(args);
2324
0
    bool helpValueRequested = false;
2325
0
    for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2326
0
    {
2327
0
        const auto &strArg = lArgs[i];
2328
0
        GDALAlgorithmArg *arg = nullptr;
2329
0
        std::string name;
2330
0
        std::string value;
2331
0
        bool hasValue = false;
2332
0
        if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2333
0
            helpValueRequested = true;
2334
0
        if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2335
0
        {
2336
0
            const auto equalPos = strArg.find('=');
2337
0
            name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2338
0
                                                   : strArg;
2339
0
            const std::string nameWithoutDash = name.substr(2);
2340
0
            auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2341
0
            if (m_arbitraryLongNameArgsAllowed &&
2342
0
                iterArg == m_mapLongNameToArg.end())
2343
0
            {
2344
0
                GetArg(nameWithoutDash);
2345
0
                iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2346
0
            }
2347
0
            if (iterArg == m_mapLongNameToArg.end())
2348
0
            {
2349
0
                const auto suggestions =
2350
0
                    GetSuggestionsForArgumentName(nameWithoutDash);
2351
0
                if (!suggestions.empty())
2352
0
                {
2353
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
2354
0
                                "Option '%s' is unknown. Do you mean %s?",
2355
0
                                name.c_str(),
2356
0
                                FormatSuggestionsAsString(
2357
0
                                    suggestions, /* addDashDashPrefix = */ true)
2358
0
                                    .c_str());
2359
0
                }
2360
0
                else
2361
0
                {
2362
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
2363
0
                                "Option '%s' is unknown.", name.c_str());
2364
0
                }
2365
0
                return false;
2366
0
            }
2367
0
            arg = iterArg->second;
2368
0
            if (equalPos != std::string::npos)
2369
0
            {
2370
0
                hasValue = true;
2371
0
                value = strArg.substr(equalPos + 1);
2372
0
            }
2373
0
        }
2374
0
        else if (strArg.size() >= 2 && strArg[0] == '-' &&
2375
0
                 CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2376
0
        {
2377
0
            for (size_t j = 1; j < strArg.size(); ++j)
2378
0
            {
2379
0
                name.clear();
2380
0
                name += strArg[j];
2381
0
                const auto iterArg = m_mapShortNameToArg.find(name);
2382
0
                if (iterArg == m_mapShortNameToArg.end())
2383
0
                {
2384
0
                    const std::string nameWithoutDash = strArg.substr(1);
2385
0
                    if (m_mapLongNameToArg.find(nameWithoutDash) !=
2386
0
                        m_mapLongNameToArg.end())
2387
0
                    {
2388
0
                        ReportError(CE_Failure, CPLE_IllegalArg,
2389
0
                                    "Short name option '%s' is unknown. Do you "
2390
0
                                    "mean '--%s' (with leading double dash) ?",
2391
0
                                    name.c_str(), nameWithoutDash.c_str());
2392
0
                    }
2393
0
                    else
2394
0
                    {
2395
0
                        const auto suggestions =
2396
0
                            GetSuggestionsForArgumentName(nameWithoutDash);
2397
0
                        if (!suggestions.empty())
2398
0
                        {
2399
0
                            ReportError(
2400
0
                                CE_Failure, CPLE_IllegalArg,
2401
0
                                "Short name option '%s' is unknown. Do you "
2402
0
                                "mean %s (with leading double dash) ?",
2403
0
                                name.c_str(),
2404
0
                                FormatSuggestionsAsString(
2405
0
                                    suggestions, /* addDashDashPrefix = */ true)
2406
0
                                    .c_str());
2407
0
                        }
2408
0
                        else
2409
0
                        {
2410
0
                            ReportError(CE_Failure, CPLE_IllegalArg,
2411
0
                                        "Short name option '%s' is unknown.",
2412
0
                                        name.c_str());
2413
0
                        }
2414
0
                    }
2415
0
                    return false;
2416
0
                }
2417
0
                arg = iterArg->second;
2418
0
                if (strArg.size() > 2)
2419
0
                {
2420
0
                    if (arg->GetType() != GAAT_BOOLEAN)
2421
0
                    {
2422
0
                        ReportError(CE_Failure, CPLE_IllegalArg,
2423
0
                                    "Invalid argument '%s'. Option '%s' is not "
2424
0
                                    "a boolean option.",
2425
0
                                    strArg.c_str(), name.c_str());
2426
0
                        return false;
2427
0
                    }
2428
2429
0
                    if (!ParseArgument(arg, name, "true", inConstructionValues))
2430
0
                        return false;
2431
0
                }
2432
0
            }
2433
0
            if (strArg.size() > 2)
2434
0
            {
2435
0
                lArgs.erase(lArgs.begin() + i);
2436
0
                continue;
2437
0
            }
2438
0
        }
2439
0
        else
2440
0
        {
2441
0
            ++i;
2442
0
            continue;
2443
0
        }
2444
0
        CPLAssert(arg);
2445
2446
0
        if (arg && arg->GetType() == GAAT_BOOLEAN)
2447
0
        {
2448
0
            if (!hasValue)
2449
0
            {
2450
0
                hasValue = true;
2451
0
                value = "true";
2452
0
            }
2453
0
        }
2454
2455
0
        lArgs.erase(lArgs.begin() + i);
2456
2457
0
        if (!hasValue)
2458
0
        {
2459
0
            if (i == lArgs.size())
2460
0
            {
2461
0
                if (m_parseForAutoCompletion)
2462
0
                {
2463
0
                    break;
2464
0
                }
2465
0
                ReportError(
2466
0
                    CE_Failure, CPLE_IllegalArg,
2467
0
                    "Expected value for argument '%s', but ran short of tokens",
2468
0
                    name.c_str());
2469
0
                return false;
2470
0
            }
2471
0
            value = lArgs[i];
2472
0
            lArgs.erase(lArgs.begin() + i);
2473
0
        }
2474
2475
0
        if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2476
0
        {
2477
0
            return false;
2478
0
        }
2479
2480
        // Consume next strings if it is a positional argument, until finding
2481
        // a value starting with dash.
2482
0
        if (!hasValue && arg && GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2483
0
            std::find(m_positionalArgs.begin(), m_positionalArgs.end(), arg) !=
2484
0
                m_positionalArgs.end())
2485
0
        {
2486
0
            int countVals = 1;
2487
0
            while (i < lArgs.size() && !lArgs[i].empty() && lArgs[i][0] != '-')
2488
0
            {
2489
0
                if (countVals == arg->GetMaxCount())
2490
0
                    break;
2491
0
                if (!ParseArgument(arg, name, lArgs[i], inConstructionValues))
2492
0
                {
2493
0
                    ProcessInConstructionValues();
2494
0
                    return false;
2495
0
                }
2496
0
                lArgs.erase(lArgs.begin() + i);
2497
0
                ++countVals;
2498
0
            }
2499
0
        }
2500
0
    }
2501
2502
0
    if (m_specialActionRequested)
2503
0
    {
2504
0
        return true;
2505
0
    }
2506
2507
    // Process positional arguments that have not been set through their
2508
    // option name.
2509
0
    size_t i = 0;
2510
0
    size_t iCurPosArg = 0;
2511
2512
    // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2513
0
    if (m_positionalArgs.size() == 3 &&
2514
0
        (m_positionalArgs[0]->IsRequired() ||
2515
0
         m_positionalArgs[0]->GetMinCount() == 1) &&
2516
0
        m_positionalArgs[0]->GetMaxCount() == 1 &&
2517
0
        (m_positionalArgs[1]->IsRequired() ||
2518
0
         m_positionalArgs[1]->GetMinCount() == 1) &&
2519
        /* Second argument may have several occurrences */
2520
0
        m_positionalArgs[1]->GetMaxCount() >= 1 &&
2521
0
        (m_positionalArgs[2]->IsRequired() ||
2522
0
         m_positionalArgs[2]->GetMinCount() == 1) &&
2523
0
        m_positionalArgs[2]->GetMaxCount() == 1 &&
2524
0
        !m_positionalArgs[0]->IsExplicitlySet() &&
2525
0
        !m_positionalArgs[1]->IsExplicitlySet() &&
2526
0
        !m_positionalArgs[2]->IsExplicitlySet())
2527
0
    {
2528
0
        if (lArgs.size() - i < 3)
2529
0
        {
2530
0
            ReportError(CE_Failure, CPLE_AppDefined,
2531
0
                        "Not enough positional values.");
2532
0
            return false;
2533
0
        }
2534
0
        bool ok = ParseArgument(m_positionalArgs[0],
2535
0
                                m_positionalArgs[0]->GetName().c_str(),
2536
0
                                lArgs[i], inConstructionValues);
2537
0
        if (ok)
2538
0
        {
2539
0
            ++i;
2540
0
            for (; i + 1 < lArgs.size() && ok; ++i)
2541
0
            {
2542
0
                ok = ParseArgument(m_positionalArgs[1],
2543
0
                                   m_positionalArgs[1]->GetName().c_str(),
2544
0
                                   lArgs[i], inConstructionValues);
2545
0
            }
2546
0
        }
2547
0
        if (ok)
2548
0
        {
2549
0
            ok = ParseArgument(m_positionalArgs[2],
2550
0
                               m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2551
0
                               inConstructionValues);
2552
0
            ++i;
2553
0
        }
2554
0
        if (!ok)
2555
0
        {
2556
0
            ProcessInConstructionValues();
2557
0
            return false;
2558
0
        }
2559
0
    }
2560
2561
0
    if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2562
0
        !m_positionalArgs[0]->IsExplicitlySet() &&
2563
0
        m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2564
0
        (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2565
0
         m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2566
0
    {
2567
0
        ++iCurPosArg;
2568
0
    }
2569
2570
0
    while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2571
0
    {
2572
0
        GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2573
0
        while (arg->IsExplicitlySet())
2574
0
        {
2575
0
            ++iCurPosArg;
2576
0
            if (iCurPosArg == m_positionalArgs.size())
2577
0
                break;
2578
0
            arg = m_positionalArgs[iCurPosArg];
2579
0
        }
2580
0
        if (iCurPosArg == m_positionalArgs.size())
2581
0
        {
2582
0
            break;
2583
0
        }
2584
0
        if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2585
0
            arg->GetMinCount() != arg->GetMaxCount())
2586
0
        {
2587
0
            if (iCurPosArg == 0)
2588
0
            {
2589
0
                size_t nCountAtEnd = 0;
2590
0
                for (size_t j = 1; j < m_positionalArgs.size(); j++)
2591
0
                {
2592
0
                    const auto *otherArg = m_positionalArgs[j];
2593
0
                    if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2594
0
                    {
2595
0
                        if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2596
0
                        {
2597
0
                            ReportError(
2598
0
                                CE_Failure, CPLE_AppDefined,
2599
0
                                "Ambiguity in definition of positional "
2600
0
                                "argument "
2601
0
                                "'%s' given it has a varying number of values, "
2602
0
                                "but follows argument '%s' which also has a "
2603
0
                                "varying number of values",
2604
0
                                otherArg->GetName().c_str(),
2605
0
                                arg->GetName().c_str());
2606
0
                            ProcessInConstructionValues();
2607
0
                            return false;
2608
0
                        }
2609
0
                        nCountAtEnd += otherArg->GetMinCount();
2610
0
                    }
2611
0
                    else
2612
0
                    {
2613
0
                        if (!otherArg->IsRequired())
2614
0
                        {
2615
0
                            ReportError(
2616
0
                                CE_Failure, CPLE_AppDefined,
2617
0
                                "Ambiguity in definition of positional "
2618
0
                                "argument "
2619
0
                                "'%s', given it is not required but follows "
2620
0
                                "argument '%s' which has a varying number of "
2621
0
                                "values",
2622
0
                                otherArg->GetName().c_str(),
2623
0
                                arg->GetName().c_str());
2624
0
                            ProcessInConstructionValues();
2625
0
                            return false;
2626
0
                        }
2627
0
                        nCountAtEnd++;
2628
0
                    }
2629
0
                }
2630
0
                if (lArgs.size() < nCountAtEnd)
2631
0
                {
2632
0
                    ReportError(CE_Failure, CPLE_AppDefined,
2633
0
                                "Not enough positional values.");
2634
0
                    ProcessInConstructionValues();
2635
0
                    return false;
2636
0
                }
2637
0
                for (; i < lArgs.size() - nCountAtEnd; ++i)
2638
0
                {
2639
0
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2640
0
                                       inConstructionValues))
2641
0
                    {
2642
0
                        ProcessInConstructionValues();
2643
0
                        return false;
2644
0
                    }
2645
0
                }
2646
0
            }
2647
0
            else if (iCurPosArg == m_positionalArgs.size() - 1)
2648
0
            {
2649
0
                for (; i < lArgs.size(); ++i)
2650
0
                {
2651
0
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2652
0
                                       inConstructionValues))
2653
0
                    {
2654
0
                        ProcessInConstructionValues();
2655
0
                        return false;
2656
0
                    }
2657
0
                }
2658
0
            }
2659
0
            else
2660
0
            {
2661
0
                ReportError(CE_Failure, CPLE_AppDefined,
2662
0
                            "Ambiguity in definition of positional arguments: "
2663
0
                            "arguments with varying number of values must be "
2664
0
                            "first or last one.");
2665
0
                return false;
2666
0
            }
2667
0
        }
2668
0
        else
2669
0
        {
2670
0
            if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2671
0
            {
2672
0
                ReportError(CE_Failure, CPLE_AppDefined,
2673
0
                            "Not enough positional values.");
2674
0
                return false;
2675
0
            }
2676
0
            const size_t iMax = i + arg->GetMaxCount();
2677
0
            for (; i < iMax; ++i)
2678
0
            {
2679
0
                if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2680
0
                                   inConstructionValues))
2681
0
                {
2682
0
                    ProcessInConstructionValues();
2683
0
                    return false;
2684
0
                }
2685
0
            }
2686
0
        }
2687
0
        ++iCurPosArg;
2688
0
    }
2689
2690
0
    if (i < lArgs.size())
2691
0
    {
2692
0
        ReportError(CE_Failure, CPLE_AppDefined,
2693
0
                    "Positional values starting at '%s' are not expected.",
2694
0
                    lArgs[i].c_str());
2695
0
        return false;
2696
0
    }
2697
2698
0
    if (!ProcessInConstructionValues())
2699
0
    {
2700
0
        return false;
2701
0
    }
2702
2703
    // Skip to first unset positional argument.
2704
0
    while (iCurPosArg < m_positionalArgs.size() &&
2705
0
           m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2706
0
    {
2707
0
        ++iCurPosArg;
2708
0
    }
2709
    // Check if this positional argument is required.
2710
0
    if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2711
0
        (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2712
0
             ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2713
0
             : m_positionalArgs[iCurPosArg]->IsRequired()))
2714
0
    {
2715
0
        ReportError(CE_Failure, CPLE_AppDefined,
2716
0
                    "Positional arguments starting at '%s' have not been "
2717
0
                    "specified.",
2718
0
                    m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2719
0
        return false;
2720
0
    }
2721
2722
0
    if (m_calledFromCommandLine)
2723
0
    {
2724
0
        for (auto &arg : m_args)
2725
0
        {
2726
0
            if (arg->IsExplicitlySet() &&
2727
0
                ((arg->GetType() == GAAT_STRING &&
2728
0
                  arg->Get<std::string>() == "?") ||
2729
0
                 (arg->GetType() == GAAT_STRING_LIST &&
2730
0
                  arg->Get<std::vector<std::string>>().size() == 1 &&
2731
0
                  arg->Get<std::vector<std::string>>()[0] == "?")))
2732
0
            {
2733
0
                {
2734
0
                    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2735
0
                    ValidateArguments();
2736
0
                }
2737
2738
0
                auto choices = arg->GetChoices();
2739
0
                if (choices.empty())
2740
0
                    choices = arg->GetAutoCompleteChoices(std::string());
2741
0
                if (!choices.empty())
2742
0
                {
2743
0
                    if (choices.size() == 1)
2744
0
                    {
2745
0
                        ReportError(
2746
0
                            CE_Failure, CPLE_AppDefined,
2747
0
                            "Single potential value for argument '%s' is '%s'",
2748
0
                            arg->GetName().c_str(), choices.front().c_str());
2749
0
                    }
2750
0
                    else
2751
0
                    {
2752
0
                        std::string msg("Potential values for argument '");
2753
0
                        msg += arg->GetName();
2754
0
                        msg += "' are:";
2755
0
                        for (const auto &v : choices)
2756
0
                        {
2757
0
                            msg += "\n- ";
2758
0
                            msg += v;
2759
0
                        }
2760
0
                        ReportError(CE_Failure, CPLE_AppDefined, "%s",
2761
0
                                    msg.c_str());
2762
0
                    }
2763
0
                    return false;
2764
0
                }
2765
0
            }
2766
0
        }
2767
0
    }
2768
2769
0
    return m_skipValidationInParseCommandLine || ValidateArguments();
2770
0
}
2771
2772
/************************************************************************/
2773
/*                     GDALAlgorithm::ReportError()                     */
2774
/************************************************************************/
2775
2776
//! @cond Doxygen_Suppress
2777
void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2778
                                const char *fmt, ...) const
2779
0
{
2780
0
    va_list args;
2781
0
    va_start(args, fmt);
2782
0
    CPLError(eErrClass, err_no, "%s",
2783
0
             std::string(m_name)
2784
0
                 .append(": ")
2785
0
                 .append(CPLString().vPrintf(fmt, args))
2786
0
                 .c_str());
2787
0
    va_end(args);
2788
0
}
2789
2790
//! @endcond
2791
2792
/************************************************************************/
2793
/*                  GDALAlgorithm::ProcessDatasetArg()                  */
2794
/************************************************************************/
2795
2796
bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2797
                                      GDALAlgorithm *algForOutput)
2798
0
{
2799
0
    bool ret = true;
2800
2801
0
    const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2802
0
    const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2803
0
    const bool update = hasUpdateArg && updateArg->Get<bool>();
2804
2805
0
    const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2806
0
    const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2807
0
    const bool append = hasAppendArg && appendArg->Get<bool>();
2808
2809
0
    const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2810
0
    const bool overwrite =
2811
0
        (arg->IsOutput() && overwriteArg &&
2812
0
         overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2813
2814
0
    auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2815
0
    auto &val = [arg]() -> GDALArgDatasetValue &
2816
0
    {
2817
0
        if (arg->GetType() == GAAT_DATASET_LIST)
2818
0
            return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2819
0
        else
2820
0
            return arg->Get<GDALArgDatasetValue>();
2821
0
    }();
2822
0
    const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2823
0
        arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2824
0
        !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2825
0
        !overwrite;
2826
2827
    // Used for nested pipelines
2828
0
    const auto oIterDatasetNameToDataset =
2829
0
        val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2830
0
                        : m_oMapDatasetNameToDataset.end();
2831
2832
0
    if (!val.GetDatasetRef() && !val.IsNameSet())
2833
0
    {
2834
0
        ReportError(CE_Failure, CPLE_AppDefined,
2835
0
                    "Argument '%s' has no dataset object or dataset name.",
2836
0
                    arg->GetName().c_str());
2837
0
        ret = false;
2838
0
    }
2839
0
    else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2840
0
    {
2841
0
        return false;
2842
0
    }
2843
0
    else if (m_inputDatasetCanBeOmitted &&
2844
0
             val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2845
0
             !arg->IsOutput())
2846
0
    {
2847
0
        return true;
2848
0
    }
2849
0
    else if (!val.GetDatasetRef() &&
2850
0
             (arg->AutoOpenDataset() ||
2851
0
              oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2852
0
             (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2853
0
              onlyInputSpecifiedInUpdateAndOutputNotRequired))
2854
0
    {
2855
0
        int flags = arg->GetDatasetType();
2856
0
        bool assignToOutputArg = false;
2857
2858
        // Check if input and output parameters point to the same
2859
        // filename (for vector datasets)
2860
0
        if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2861
0
            outputArg && outputArg->GetType() == GAAT_DATASET)
2862
0
        {
2863
0
            auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2864
0
            if (!outputVal.GetDatasetRef() &&
2865
0
                outputVal.GetName() == val.GetName() &&
2866
0
                (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2867
0
            {
2868
0
                assignToOutputArg = true;
2869
0
                flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2870
0
            }
2871
0
            else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2872
0
            {
2873
0
                flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2874
0
            }
2875
0
        }
2876
2877
0
        if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2878
0
            flags |= GDAL_OF_VERBOSE_ERROR;
2879
0
        if ((arg == outputArg || !outputArg) && update)
2880
0
        {
2881
0
            flags |= GDAL_OF_UPDATE;
2882
0
            if (!append)
2883
0
                flags |= GDAL_OF_VERBOSE_ERROR;
2884
0
        }
2885
2886
0
        const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2887
0
        const bool readOnly =
2888
0
            (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2889
0
             readOnlyArg->Get<bool>());
2890
0
        if (readOnly)
2891
0
            flags &= ~GDAL_OF_UPDATE;
2892
2893
0
        CPLStringList aosOpenOptions;
2894
0
        CPLStringList aosAllowedDrivers;
2895
0
        if (arg->IsInput())
2896
0
        {
2897
0
            if (arg == outputArg)
2898
0
            {
2899
0
                if (update && !overwrite)
2900
0
                {
2901
0
                    const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2902
0
                    if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2903
0
                        aosOpenOptions = CPLStringList(
2904
0
                            ooArg->Get<std::vector<std::string>>());
2905
0
                }
2906
0
            }
2907
0
            else
2908
0
            {
2909
0
                const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2910
0
                if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2911
0
                    aosOpenOptions =
2912
0
                        CPLStringList(ooArg->Get<std::vector<std::string>>());
2913
2914
0
                const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2915
0
                if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2916
0
                    aosAllowedDrivers =
2917
0
                        CPLStringList(ifArg->Get<std::vector<std::string>>());
2918
0
            }
2919
0
        }
2920
2921
0
        std::string osDatasetName = val.GetName();
2922
0
        if (!m_referencePath.empty())
2923
0
        {
2924
0
            osDatasetName = GDALDataset::BuildFilename(
2925
0
                osDatasetName.c_str(), m_referencePath.c_str(), true);
2926
0
        }
2927
0
        if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2928
0
            osDatasetName = "/vsistdin/";
2929
2930
        // Handle special case of overview delete in GTiff which would fail
2931
        // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2932
0
        if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2933
0
            m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2934
0
            aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2935
0
        {
2936
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2937
0
            GDALDriverH hDrv =
2938
0
                GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2939
0
            if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2940
0
            {
2941
                // Cleaning does not break COG layout
2942
0
                aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2943
0
            }
2944
0
        }
2945
2946
0
        GDALDataset *poDS;
2947
0
        CPLErrorAccumulator oAccumulator;
2948
0
        {
2949
0
            auto oContext = oAccumulator.InstallForCurrentScope();
2950
2951
0
            poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2952
0
                       ? oIterDatasetNameToDataset->second
2953
0
                       : GDALDataset::Open(osDatasetName.c_str(), flags,
2954
0
                                           aosAllowedDrivers.List(),
2955
0
                                           aosOpenOptions.List());
2956
2957
0
            if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
2958
0
                !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
2959
0
            {
2960
0
                auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
2961
0
                    osDatasetName.c_str(), nullptr);
2962
0
                if (eErr == OGRERR_NONE)
2963
0
                {
2964
0
                    auto poMemDS = std::make_unique<MEMDataset>();
2965
0
                    auto *poLayer = poMemDS->CreateLayer(
2966
0
                        "layer", poWktGeom->getSpatialReference(),
2967
0
                        poWktGeom->getGeometryType());
2968
2969
0
                    auto poFeatureDefn = poLayer->GetLayerDefn();
2970
0
                    OGRFeature oFeature(poFeatureDefn);
2971
2972
0
                    oFeature.SetGeometry(std::move(poWktGeom));
2973
0
                    if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
2974
0
                    {
2975
0
                        poDS = poMemDS.release();
2976
0
                        oAccumulator.ClearErrors();
2977
0
                    }
2978
0
                }
2979
0
            }
2980
2981
            // Retry with PostGIS vector driver
2982
0
            if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
2983
0
                cpl::starts_with(osDatasetName, "PG:") &&
2984
0
                GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2985
0
                aosAllowedDrivers.empty() && aosOpenOptions.empty())
2986
0
            {
2987
0
                oAccumulator.ClearErrors();
2988
0
                poDS = GDALDataset::Open(
2989
0
                    osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2990
0
                    aosAllowedDrivers.List(), aosOpenOptions.List());
2991
0
            }
2992
0
        }
2993
0
        oAccumulator.ReplayErrors();
2994
2995
0
        if (poDS)
2996
0
        {
2997
0
            if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2998
0
            {
2999
0
                if (arg->GetType() == GAAT_DATASET)
3000
0
                    arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
3001
0
                poDS->Reference();
3002
0
                m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
3003
0
            }
3004
3005
            // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
3006
            // where the PG: dataset will be first opened with the PostGISRaster
3007
            // driver whereas the PostgreSQL (vector) one is actually wanted.
3008
0
            if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
3009
0
                (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
3010
0
                aosOpenOptions.empty())
3011
0
            {
3012
0
                auto poDrv = poDS->GetDriver();
3013
0
                if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
3014
0
                {
3015
                    // Retry with PostgreSQL (vector) driver
3016
0
                    std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
3017
0
                        osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
3018
0
                    if (poTmpDS)
3019
0
                    {
3020
0
                        poDS->ReleaseRef();
3021
0
                        poDS = poTmpDS.release();
3022
0
                    }
3023
0
                }
3024
0
            }
3025
3026
0
            if (assignToOutputArg)
3027
0
            {
3028
                // Avoid opening twice the same datasource if it is both
3029
                // the input and output.
3030
                // Known to cause problems with at least FGdb, SQLite
3031
                // and GPKG drivers. See #4270
3032
                // Restrict to those 3 drivers. For example it is known
3033
                // to break with the PG driver due to the way it
3034
                // manages transactions.
3035
0
                auto poDriver = poDS->GetDriver();
3036
0
                if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
3037
0
                                 EQUAL(poDriver->GetDescription(), "SQLite") ||
3038
0
                                 EQUAL(poDriver->GetDescription(), "GPKG")))
3039
0
                {
3040
0
                    outputArg->Get<GDALArgDatasetValue>().Set(poDS);
3041
0
                }
3042
0
            }
3043
0
            val.SetDatasetOpenedByAlgorithm();
3044
0
            val.Set(poDS);
3045
0
            poDS->ReleaseRef();
3046
0
        }
3047
0
        else if (!append)
3048
0
        {
3049
0
            ret = false;
3050
0
        }
3051
0
    }
3052
3053
    // Deal with overwriting the output dataset
3054
0
    if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
3055
0
    {
3056
0
        if (!append)
3057
0
        {
3058
            // If outputting to MEM, do not try to erase a real file of the same name!
3059
0
            const auto outputFormatArg =
3060
0
                algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3061
0
            if (!(outputFormatArg &&
3062
0
                  outputFormatArg->GetType() == GAAT_STRING &&
3063
0
                  (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3064
0
                   EQUAL(outputFormatArg->Get<std::string>().c_str(),
3065
0
                         "stream") ||
3066
0
                   EQUAL(outputFormatArg->Get<std::string>().c_str(),
3067
0
                         "Memory"))))
3068
0
            {
3069
0
                const char *pszType = "";
3070
0
                GDALDriver *poDriver = nullptr;
3071
0
                if (!val.GetName().empty() &&
3072
0
                    GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
3073
0
                                               &poDriver))
3074
0
                {
3075
0
                    if (!overwrite)
3076
0
                    {
3077
0
                        std::string options;
3078
0
                        if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
3079
0
                        {
3080
0
                            options += "--";
3081
0
                            options += GDAL_ARG_NAME_OVERWRITE_LAYER;
3082
0
                        }
3083
0
                        if (hasAppendArg)
3084
0
                        {
3085
0
                            if (!options.empty())
3086
0
                                options += '/';
3087
0
                            options += "--";
3088
0
                            options += GDAL_ARG_NAME_APPEND;
3089
0
                        }
3090
0
                        if (hasUpdateArg)
3091
0
                        {
3092
0
                            if (!options.empty())
3093
0
                                options += '/';
3094
0
                            options += "--";
3095
0
                            options += GDAL_ARG_NAME_UPDATE;
3096
0
                        }
3097
3098
0
                        if (poDriver)
3099
0
                        {
3100
0
                            const char *pszPrefix = poDriver->GetMetadataItem(
3101
0
                                GDAL_DMD_CONNECTION_PREFIX);
3102
0
                            if (pszPrefix &&
3103
0
                                STARTS_WITH_CI(val.GetName().c_str(),
3104
0
                                               pszPrefix))
3105
0
                            {
3106
0
                                bool bExists = false;
3107
0
                                {
3108
0
                                    CPLErrorStateBackuper oBackuper(
3109
0
                                        CPLQuietErrorHandler);
3110
0
                                    bExists = std::unique_ptr<GDALDataset>(
3111
0
                                                  GDALDataset::Open(
3112
0
                                                      val.GetName().c_str())) !=
3113
0
                                              nullptr;
3114
0
                                }
3115
0
                                if (bExists)
3116
0
                                {
3117
0
                                    if (!options.empty())
3118
0
                                        options = " You may specify the " +
3119
0
                                                  options + " option.";
3120
0
                                    ReportError(CE_Failure, CPLE_AppDefined,
3121
0
                                                "%s '%s' already exists.%s",
3122
0
                                                pszType, val.GetName().c_str(),
3123
0
                                                options.c_str());
3124
0
                                    return false;
3125
0
                                }
3126
3127
0
                                return true;
3128
0
                            }
3129
0
                        }
3130
3131
0
                        if (!options.empty())
3132
0
                            options = '/' + options;
3133
0
                        ReportError(
3134
0
                            CE_Failure, CPLE_AppDefined,
3135
0
                            "%s '%s' already exists. You may specify the "
3136
0
                            "--overwrite%s option.",
3137
0
                            pszType, val.GetName().c_str(), options.c_str());
3138
0
                        return false;
3139
0
                    }
3140
0
                    else if (EQUAL(pszType, "File"))
3141
0
                    {
3142
0
                        if (VSIUnlink(val.GetName().c_str()) != 0)
3143
0
                        {
3144
0
                            ReportError(CE_Failure, CPLE_AppDefined,
3145
0
                                        "Deleting %s failed: %s",
3146
0
                                        val.GetName().c_str(),
3147
0
                                        VSIStrerror(errno));
3148
0
                            return false;
3149
0
                        }
3150
0
                    }
3151
0
                    else if (EQUAL(pszType, "Directory"))
3152
0
                    {
3153
                        // We don't want the user to accidentally erase a non-GDAL dataset
3154
0
                        ReportError(CE_Failure, CPLE_AppDefined,
3155
0
                                    "Directory '%s' already exists, but is not "
3156
0
                                    "recognized as a valid GDAL dataset. "
3157
0
                                    "Please manually delete it before retrying",
3158
0
                                    val.GetName().c_str());
3159
0
                        return false;
3160
0
                    }
3161
0
                    else if (poDriver)
3162
0
                    {
3163
0
                        bool bDeleteOK;
3164
0
                        {
3165
0
                            CPLErrorStateBackuper oBackuper(
3166
0
                                CPLQuietErrorHandler);
3167
0
                            bDeleteOK = (poDriver->Delete(
3168
0
                                             val.GetName().c_str()) == CE_None);
3169
0
                        }
3170
0
                        VSIStatBufL sStat;
3171
0
                        if (!bDeleteOK &&
3172
0
                            VSIStatL(val.GetName().c_str(), &sStat) == 0)
3173
0
                        {
3174
0
                            if (VSI_ISDIR(sStat.st_mode))
3175
0
                            {
3176
                                // We don't want the user to accidentally erase a non-GDAL dataset
3177
0
                                ReportError(
3178
0
                                    CE_Failure, CPLE_AppDefined,
3179
0
                                    "Directory '%s' already exists, but is not "
3180
0
                                    "recognized as a valid GDAL dataset. "
3181
0
                                    "Please manually delete it before retrying",
3182
0
                                    val.GetName().c_str());
3183
0
                                return false;
3184
0
                            }
3185
0
                            else if (VSIUnlink(val.GetName().c_str()) != 0)
3186
0
                            {
3187
0
                                ReportError(CE_Failure, CPLE_AppDefined,
3188
0
                                            "Deleting %s failed: %s",
3189
0
                                            val.GetName().c_str(),
3190
0
                                            VSIStrerror(errno));
3191
0
                                return false;
3192
0
                            }
3193
0
                        }
3194
0
                    }
3195
0
                }
3196
0
            }
3197
0
        }
3198
0
    }
3199
3200
    // If outputting to stdout, automatically turn off progress bar
3201
0
    if (arg == outputArg && val.GetName() == "/vsistdout/")
3202
0
    {
3203
0
        auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3204
0
        if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3205
0
            quietArg->Set(true);
3206
0
    }
3207
3208
0
    return ret;
3209
0
}
3210
3211
/************************************************************************/
3212
/*                  GDALAlgorithm::ValidateArguments()                  */
3213
/************************************************************************/
3214
3215
bool GDALAlgorithm::ValidateArguments()
3216
0
{
3217
0
    if (m_selectedSubAlg)
3218
0
        return m_selectedSubAlg->ValidateArguments();
3219
3220
0
    if (m_specialActionRequested)
3221
0
        return true;
3222
3223
0
    m_arbitraryLongNameArgsAllowed = false;
3224
3225
    // If only --output=format=MEM/stream is specified and not --output,
3226
    // then set empty name for --output.
3227
0
    auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3228
0
    auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3229
0
    if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3230
0
        !outputArg->IsExplicitlySet() &&
3231
0
        outputFormatArg->GetType() == GAAT_STRING &&
3232
0
        (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3233
0
         EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3234
0
        outputArg->GetType() == GAAT_DATASET &&
3235
0
        (outputArg->GetDatasetInputFlags() & GADV_NAME))
3236
0
    {
3237
0
        outputArg->Get<GDALArgDatasetValue>().Set("");
3238
0
    }
3239
3240
    // The method may emit several errors if several constraints are not met.
3241
0
    bool ret = true;
3242
0
    std::map<std::string, std::string> mutualExclusionGroupUsed;
3243
0
    std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3244
0
    for (auto &arg : m_args)
3245
0
    {
3246
        // Check mutually exclusive/dependent arguments
3247
0
        if (arg->IsExplicitlySet())
3248
0
        {
3249
3250
0
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3251
0
            if (!mutualExclusionGroup.empty())
3252
0
            {
3253
0
                auto oIter =
3254
0
                    mutualExclusionGroupUsed.find(mutualExclusionGroup);
3255
0
                if (oIter != mutualExclusionGroupUsed.end())
3256
0
                {
3257
0
                    ret = false;
3258
0
                    ReportError(
3259
0
                        CE_Failure, CPLE_AppDefined,
3260
0
                        "Argument '%s' is mutually exclusive with '%s'.",
3261
0
                        arg->GetName().c_str(), oIter->second.c_str());
3262
0
                }
3263
0
                else
3264
0
                {
3265
0
                    mutualExclusionGroupUsed[mutualExclusionGroup] =
3266
0
                        arg->GetName();
3267
0
                }
3268
0
            }
3269
3270
0
            const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3271
0
            if (!mutualDependencyGroup.empty())
3272
0
            {
3273
0
                if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3274
0
                    mutualDependencyGroupUsed.end())
3275
0
                {
3276
0
                    mutualDependencyGroupUsed[mutualDependencyGroup] = {
3277
0
                        arg->GetName()};
3278
0
                }
3279
0
                else
3280
0
                {
3281
0
                    mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3282
0
                        arg->GetName());
3283
0
                }
3284
0
            }
3285
3286
            // Check direct dependencies
3287
0
            for (const auto &dependency : arg->GetDirectDependencies())
3288
0
            {
3289
0
                auto depArg = GetArg(dependency);
3290
0
                if (!depArg)
3291
0
                {
3292
0
                    ret = false;
3293
0
                    ReportError(CE_Failure, CPLE_AppDefined,
3294
0
                                "Argument '%s' depends on argument '%s' that "
3295
0
                                "is not defined.",
3296
0
                                arg->GetName().c_str(), dependency.c_str());
3297
0
                }
3298
0
                else if (!depArg->IsExplicitlySet())
3299
0
                {
3300
0
                    ret = false;
3301
0
                    ReportError(CE_Failure, CPLE_AppDefined,
3302
0
                                "Argument '%s' depends on argument '%s' that "
3303
0
                                "has not been specified.",
3304
0
                                arg->GetName().c_str(),
3305
0
                                depArg->GetName().c_str());
3306
0
                }
3307
0
            }
3308
0
        }
3309
3310
0
        if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3311
0
            !arg->HasDefaultValue())
3312
0
        {
3313
0
            bool emitError = true;
3314
0
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3315
0
            if (!mutualExclusionGroup.empty())
3316
0
            {
3317
0
                for (const auto &otherArg : m_args)
3318
0
                {
3319
0
                    if (otherArg->GetMutualExclusionGroup() ==
3320
0
                            mutualExclusionGroup &&
3321
0
                        otherArg->IsExplicitlySet())
3322
0
                    {
3323
0
                        emitError = false;
3324
0
                        break;
3325
0
                    }
3326
0
                }
3327
0
            }
3328
0
            if (emitError && !(m_inputDatasetCanBeOmitted &&
3329
0
                               arg->GetName() == GDAL_ARG_NAME_INPUT &&
3330
0
                               (arg->GetType() == GAAT_DATASET ||
3331
0
                                arg->GetType() == GAAT_DATASET_LIST)))
3332
0
            {
3333
0
                ReportError(CE_Failure, CPLE_AppDefined,
3334
0
                            "Required argument '%s' has not been specified.",
3335
0
                            arg->GetName().c_str());
3336
0
                ret = false;
3337
0
            }
3338
0
        }
3339
0
        else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3340
0
        {
3341
0
            if (!ProcessDatasetArg(arg.get(), this))
3342
0
                ret = false;
3343
0
        }
3344
3345
0
        if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3346
0
        {
3347
0
            auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3348
0
            if (listVal.size() == 1)
3349
0
            {
3350
0
                if (!ProcessDatasetArg(arg.get(), this))
3351
0
                    ret = false;
3352
0
            }
3353
0
            else
3354
0
            {
3355
0
                for (auto &val : listVal)
3356
0
                {
3357
0
                    if (val.GetDatasetRef())
3358
0
                    {
3359
0
                        if (!CheckCanSetDatasetObject(arg.get()))
3360
0
                        {
3361
0
                            ret = false;
3362
0
                        }
3363
0
                        continue;
3364
0
                    }
3365
3366
0
                    if (val.GetName().empty())
3367
0
                    {
3368
0
                        ReportError(CE_Failure, CPLE_AppDefined,
3369
0
                                    "Argument '%s' has no dataset object or "
3370
0
                                    "dataset name.",
3371
0
                                    arg->GetName().c_str());
3372
0
                        ret = false;
3373
0
                        continue;
3374
0
                    }
3375
3376
0
                    auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3377
0
                    if (oIter != m_oMapDatasetNameToDataset.end())
3378
0
                    {
3379
0
                        auto poDS = oIter->second;
3380
0
                        val.SetDatasetOpenedByAlgorithm();
3381
0
                        val.Set(poDS);
3382
0
                        m_oMapDatasetNameToDataset.erase(oIter);
3383
0
                        continue;
3384
0
                    }
3385
3386
0
                    if (!arg->AutoOpenDataset())
3387
0
                        continue;
3388
3389
0
                    int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3390
3391
0
                    CPLStringList aosOpenOptions;
3392
0
                    CPLStringList aosAllowedDrivers;
3393
0
                    if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3394
0
                    {
3395
0
                        const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3396
0
                        if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3397
0
                        {
3398
0
                            aosOpenOptions = CPLStringList(
3399
0
                                ooArg->Get<std::vector<std::string>>());
3400
0
                        }
3401
3402
0
                        const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3403
0
                        if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3404
0
                        {
3405
0
                            aosAllowedDrivers = CPLStringList(
3406
0
                                ifArg->Get<std::vector<std::string>>());
3407
0
                        }
3408
3409
0
                        const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3410
0
                        if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3411
0
                            updateArg->Get<bool>())
3412
0
                        {
3413
0
                            flags |= GDAL_OF_UPDATE;
3414
0
                        }
3415
0
                    }
3416
3417
0
                    auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3418
0
                        val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3419
0
                        aosOpenOptions.List()));
3420
0
                    if (poDS)
3421
0
                    {
3422
0
                        val.Set(std::move(poDS));
3423
0
                    }
3424
0
                    else
3425
0
                    {
3426
0
                        ret = false;
3427
0
                    }
3428
0
                }
3429
0
            }
3430
0
        }
3431
3432
0
        if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3433
0
        {
3434
0
            ret = false;
3435
0
        }
3436
0
    }
3437
3438
    // Check mutual dependency groups
3439
0
    std::vector<std::string> processedGroups;
3440
    // Loop through group map and check there are not required args in the group that are not set
3441
0
    for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3442
0
    {
3443
0
        if (std::find(processedGroups.begin(), processedGroups.end(),
3444
0
                      groupName) != processedGroups.end())
3445
0
            continue;
3446
0
        std::vector<std::string> missingArgs;
3447
0
        for (auto &arg : m_args)
3448
0
        {
3449
0
            const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3450
0
            if (mutualDependencyGroup == groupName &&
3451
0
                std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3452
0
                    argNames.end())
3453
0
            {
3454
0
                missingArgs.push_back(arg->GetName());
3455
0
            }
3456
0
        }
3457
0
        if (!missingArgs.empty())
3458
0
        {
3459
0
            ret = false;
3460
0
            std::string missingArgsStr;
3461
0
            for (const auto &missingArg : missingArgs)
3462
0
            {
3463
0
                if (!missingArgsStr.empty())
3464
0
                    missingArgsStr += ", ";
3465
0
                missingArgsStr += missingArg;
3466
0
            }
3467
0
            std::string givenArgsStr;
3468
0
            for (const auto &givenArg : argNames)
3469
0
            {
3470
0
                if (!givenArgsStr.empty())
3471
0
                    givenArgsStr += ", ";
3472
0
                givenArgsStr += givenArg;
3473
0
            }
3474
0
            ReportError(CE_Failure, CPLE_AppDefined,
3475
0
                        "Argument(s) '%s' require(s) that the following "
3476
0
                        "argument(s) are also specified: %s.",
3477
0
                        givenArgsStr.c_str(), missingArgsStr.c_str());
3478
0
        }
3479
0
        processedGroups.push_back(groupName);
3480
0
    }
3481
3482
0
    for (const auto &f : m_validationActions)
3483
0
    {
3484
0
        if (!f())
3485
0
            ret = false;
3486
0
    }
3487
3488
0
    return ret;
3489
0
}
3490
3491
/************************************************************************/
3492
/*                GDALAlgorithm::InstantiateSubAlgorithm                */
3493
/************************************************************************/
3494
3495
std::unique_ptr<GDALAlgorithm>
3496
GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3497
                                       bool suggestionAllowed) const
3498
0
{
3499
0
    auto ret = m_subAlgRegistry.Instantiate(name);
3500
0
    auto childCallPath = m_callPath;
3501
0
    childCallPath.push_back(name);
3502
0
    if (!ret)
3503
0
    {
3504
0
        ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3505
0
                  .InstantiateDeclaredSubAlgorithm(childCallPath);
3506
0
    }
3507
0
    if (ret)
3508
0
    {
3509
0
        ret->SetCallPath(childCallPath);
3510
0
    }
3511
0
    else if (suggestionAllowed)
3512
0
    {
3513
0
        std::string bestCandidate;
3514
0
        size_t bestDistance = std::numeric_limits<size_t>::max();
3515
0
        for (const std::string &candidate : GetSubAlgorithmNames())
3516
0
        {
3517
0
            const size_t distance =
3518
0
                CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3519
0
                                       /* transpositionAllowed = */ true);
3520
0
            if (distance < bestDistance)
3521
0
            {
3522
0
                bestCandidate = candidate;
3523
0
                bestDistance = distance;
3524
0
            }
3525
0
            else if (distance == bestDistance)
3526
0
            {
3527
0
                bestCandidate.clear();
3528
0
            }
3529
0
        }
3530
0
        if (!bestCandidate.empty() && bestDistance <= 2)
3531
0
        {
3532
0
            CPLError(CE_Failure, CPLE_AppDefined,
3533
0
                     "Algorithm '%s' is unknown. Do you mean '%s'?",
3534
0
                     name.c_str(), bestCandidate.c_str());
3535
0
        }
3536
0
    }
3537
0
    return ret;
3538
0
}
3539
3540
/************************************************************************/
3541
/*            GDALAlgorithm::GetSuggestionForArgumentName()             */
3542
/************************************************************************/
3543
3544
std::string
3545
GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3546
0
{
3547
0
    if (osName.size() >= 3)
3548
0
    {
3549
0
        std::string bestCandidate;
3550
0
        size_t bestDistance = std::numeric_limits<size_t>::max();
3551
0
        for (const auto &[key, value] : m_mapLongNameToArg)
3552
0
        {
3553
0
            CPL_IGNORE_RET_VAL(value);
3554
0
            const size_t distance = CPLLevenshteinDistance(
3555
0
                osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3556
0
            if (distance < bestDistance)
3557
0
            {
3558
0
                bestCandidate = key;
3559
0
                bestDistance = distance;
3560
0
            }
3561
0
            else if (distance == bestDistance)
3562
0
            {
3563
0
                bestCandidate.clear();
3564
0
            }
3565
0
        }
3566
0
        if (!bestCandidate.empty() &&
3567
0
            bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3568
0
        {
3569
0
            return bestCandidate;
3570
0
        }
3571
0
    }
3572
0
    return std::string();
3573
0
}
3574
3575
/************************************************************************/
3576
/*            GDALAlgorithm::GetSuggestionsForArgumentName()            */
3577
/************************************************************************/
3578
3579
std::vector<std::string>
3580
GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
3581
0
{
3582
0
    std::vector<std::string> ret;
3583
0
    std::string suggestion = GetSuggestionForArgumentName(osName);
3584
0
    if (!suggestion.empty())
3585
0
    {
3586
0
        ret.push_back(std::move(suggestion));
3587
0
    }
3588
0
    else if (osName.size() >= 3)
3589
0
    {
3590
        // e.g "crs" for reproject will match "input-crs" and "target-crs"
3591
0
        const std::string dashName = std::string("-").append(osName);
3592
0
        for (const auto &arg : m_args)
3593
0
        {
3594
0
            if (cpl::ends_with(arg->GetName(), dashName))
3595
0
            {
3596
0
                ret.push_back(arg->GetName());
3597
0
            }
3598
0
        }
3599
0
    }
3600
0
    return ret;
3601
0
}
3602
3603
/************************************************************************/
3604
/*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
3605
/************************************************************************/
3606
3607
/* static */
3608
bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3609
0
{
3610
0
    return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3611
0
           osName == GDAL_ARG_NAME_OVERWRITE ||
3612
0
           osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3613
0
}
3614
3615
/************************************************************************/
3616
/*                   GDALAlgorithm::HasOutputString()                   */
3617
/************************************************************************/
3618
3619
bool GDALAlgorithm::HasOutputString() const
3620
0
{
3621
0
    auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3622
0
    return outputStringArg && outputStringArg->IsOutput();
3623
0
}
3624
3625
/************************************************************************/
3626
/*                       GDALAlgorithm::GetArg()                        */
3627
/************************************************************************/
3628
3629
GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3630
                                        bool suggestionAllowed, bool isConst)
3631
0
{
3632
0
    const auto nPos = osName.find_first_not_of('-');
3633
0
    if (nPos == std::string::npos)
3634
0
        return nullptr;
3635
0
    std::string osKey = osName.substr(nPos);
3636
0
    {
3637
0
        const auto oIter = m_mapLongNameToArg.find(osKey);
3638
0
        if (oIter != m_mapLongNameToArg.end())
3639
0
            return oIter->second;
3640
0
    }
3641
0
    {
3642
0
        const auto oIter = m_mapShortNameToArg.find(osKey);
3643
0
        if (oIter != m_mapShortNameToArg.end())
3644
0
            return oIter->second;
3645
0
    }
3646
3647
0
    if (!isConst && m_arbitraryLongNameArgsAllowed)
3648
0
    {
3649
0
        const auto nDotPos = osKey.find('.');
3650
0
        const std::string osKeyEnd =
3651
0
            nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3652
0
        if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3653
0
        {
3654
0
            m_arbitraryLongNameArgsValuesBool.emplace_back(
3655
0
                std::make_unique<bool>());
3656
0
            AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3657
0
                   m_arbitraryLongNameArgsValuesBool.back().get())
3658
0
                .SetUserProvided();
3659
0
        }
3660
0
        else
3661
0
        {
3662
0
            const std::string osKeyInit = osKey;
3663
0
            if (osKey == "oo")
3664
0
                osKey = GDAL_ARG_NAME_OPEN_OPTION;
3665
0
            else if (osKey == "co")
3666
0
                osKey = GDAL_ARG_NAME_CREATION_OPTION;
3667
0
            else if (osKey == "of")
3668
0
                osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3669
0
            else if (osKey == "if")
3670
0
                osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3671
0
            m_arbitraryLongNameArgsValuesStr.emplace_back(
3672
0
                std::make_unique<std::string>());
3673
0
            auto &arg =
3674
0
                AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3675
0
                       m_arbitraryLongNameArgsValuesStr.back().get())
3676
0
                    .SetUserProvided();
3677
0
            if (osKey != osKeyInit)
3678
0
                arg.AddAlias(osKeyInit);
3679
0
        }
3680
0
        const auto oIter = m_mapLongNameToArg.find(osKey);
3681
0
        CPLAssert(oIter != m_mapLongNameToArg.end());
3682
0
        return oIter->second;
3683
0
    }
3684
3685
0
    if (suggestionAllowed)
3686
0
    {
3687
0
        const auto suggestions = GetSuggestionsForArgumentName(osName);
3688
0
        if (!suggestions.empty())
3689
0
        {
3690
0
            CPLError(CE_Failure, CPLE_AppDefined,
3691
0
                     "Argument '%s' is unknown. Do you mean %s?",
3692
0
                     osName.c_str(),
3693
0
                     FormatSuggestionsAsString(suggestions,
3694
0
                                               /* addDashDashPrefix = */ false)
3695
0
                         .c_str());
3696
0
        }
3697
0
    }
3698
3699
0
    return nullptr;
3700
0
}
3701
3702
/************************************************************************/
3703
/*                     GDALAlgorithm::AddAliasFor()                     */
3704
/************************************************************************/
3705
3706
//! @cond Doxygen_Suppress
3707
void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3708
                                const std::string &alias)
3709
0
{
3710
0
    if (cpl::contains(m_mapLongNameToArg, alias))
3711
0
    {
3712
0
        ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3713
0
                    alias.c_str());
3714
0
    }
3715
0
    else
3716
0
    {
3717
0
        m_mapLongNameToArg[alias] = arg;
3718
0
    }
3719
0
}
3720
3721
//! @endcond
3722
3723
/************************************************************************/
3724
/*                GDALAlgorithm::AddShortNameAliasFor()                 */
3725
/************************************************************************/
3726
3727
//! @cond Doxygen_Suppress
3728
void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3729
                                         char shortNameAlias)
3730
0
{
3731
0
    std::string alias;
3732
0
    alias += shortNameAlias;
3733
0
    if (cpl::contains(m_mapShortNameToArg, alias))
3734
0
    {
3735
0
        ReportError(CE_Failure, CPLE_AppDefined,
3736
0
                    "Short name '%s' already declared.", alias.c_str());
3737
0
    }
3738
0
    else
3739
0
    {
3740
0
        m_mapShortNameToArg[alias] = arg;
3741
0
    }
3742
0
}
3743
3744
//! @endcond
3745
3746
/************************************************************************/
3747
/*                    GDALAlgorithm::SetPositional()                    */
3748
/************************************************************************/
3749
3750
//! @cond Doxygen_Suppress
3751
void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3752
0
{
3753
0
    CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3754
0
                        arg) == m_positionalArgs.end());
3755
0
    m_positionalArgs.push_back(arg);
3756
0
}
3757
3758
//! @endcond
3759
3760
/************************************************************************/
3761
/*                  GDALAlgorithm::HasSubAlgorithms()                   */
3762
/************************************************************************/
3763
3764
bool GDALAlgorithm::HasSubAlgorithms() const
3765
0
{
3766
0
    if (!m_subAlgRegistry.empty())
3767
0
        return true;
3768
0
    return !GDALGlobalAlgorithmRegistry::GetSingleton()
3769
0
                .GetDeclaredSubAlgorithmNames(m_callPath)
3770
0
                .empty();
3771
0
}
3772
3773
/************************************************************************/
3774
/*                GDALAlgorithm::GetSubAlgorithmNames()                 */
3775
/************************************************************************/
3776
3777
std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3778
0
{
3779
0
    std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3780
0
    const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3781
0
                           .GetDeclaredSubAlgorithmNames(m_callPath);
3782
0
    ret.insert(ret.end(), other.begin(), other.end());
3783
0
    if (!other.empty())
3784
0
        std::sort(ret.begin(), ret.end());
3785
0
    return ret;
3786
0
}
3787
3788
/************************************************************************/
3789
/*                       GDALAlgorithm::AddArg()                        */
3790
/************************************************************************/
3791
3792
GDALInConstructionAlgorithmArg &
3793
GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3794
0
{
3795
0
    auto argRaw = arg.get();
3796
0
    const auto &longName = argRaw->GetName();
3797
0
    if (!longName.empty())
3798
0
    {
3799
0
        if (longName[0] == '-')
3800
0
        {
3801
0
            ReportError(CE_Failure, CPLE_AppDefined,
3802
0
                        "Long name '%s' should not start with '-'",
3803
0
                        longName.c_str());
3804
0
        }
3805
0
        if (longName.find('=') != std::string::npos)
3806
0
        {
3807
0
            ReportError(CE_Failure, CPLE_AppDefined,
3808
0
                        "Long name '%s' should not contain a '=' character",
3809
0
                        longName.c_str());
3810
0
        }
3811
0
        if (cpl::contains(m_mapLongNameToArg, longName))
3812
0
        {
3813
0
            ReportError(CE_Failure, CPLE_AppDefined,
3814
0
                        "Long name '%s' already declared", longName.c_str());
3815
0
        }
3816
0
        m_mapLongNameToArg[longName] = argRaw;
3817
0
    }
3818
0
    const auto &shortName = argRaw->GetShortName();
3819
0
    if (!shortName.empty())
3820
0
    {
3821
0
        if (shortName.size() != 1 ||
3822
0
            !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3823
0
              (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3824
0
              (shortName[0] >= '0' && shortName[0] <= '9')))
3825
0
        {
3826
0
            ReportError(CE_Failure, CPLE_AppDefined,
3827
0
                        "Short name '%s' should be a single letter or digit",
3828
0
                        shortName.c_str());
3829
0
        }
3830
0
        if (cpl::contains(m_mapShortNameToArg, shortName))
3831
0
        {
3832
0
            ReportError(CE_Failure, CPLE_AppDefined,
3833
0
                        "Short name '%s' already declared", shortName.c_str());
3834
0
        }
3835
0
        m_mapShortNameToArg[shortName] = argRaw;
3836
0
    }
3837
0
    m_args.emplace_back(std::move(arg));
3838
0
    return *(
3839
0
        cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3840
0
}
3841
3842
GDALInConstructionAlgorithmArg &
3843
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3844
                      const std::string &helpMessage, bool *pValue)
3845
0
{
3846
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3847
0
        this,
3848
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3849
0
        pValue));
3850
0
}
3851
3852
GDALInConstructionAlgorithmArg &
3853
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3854
                      const std::string &helpMessage, std::string *pValue)
3855
0
{
3856
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3857
0
        this,
3858
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3859
0
        pValue));
3860
0
}
3861
3862
GDALInConstructionAlgorithmArg &
3863
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3864
                      const std::string &helpMessage, int *pValue)
3865
0
{
3866
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3867
0
        this,
3868
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3869
0
        pValue));
3870
0
}
3871
3872
GDALInConstructionAlgorithmArg &
3873
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3874
                      const std::string &helpMessage, double *pValue)
3875
0
{
3876
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3877
0
        this,
3878
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3879
0
        pValue));
3880
0
}
3881
3882
GDALInConstructionAlgorithmArg &
3883
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3884
                      const std::string &helpMessage,
3885
                      GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3886
0
{
3887
0
    auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3888
0
                           this,
3889
0
                           GDALAlgorithmArgDecl(longName, chShortName,
3890
0
                                                helpMessage, GAAT_DATASET),
3891
0
                           pValue))
3892
0
                    .SetDatasetType(type);
3893
0
    pValue->SetOwnerArgument(&arg);
3894
0
    return arg;
3895
0
}
3896
3897
GDALInConstructionAlgorithmArg &
3898
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3899
                      const std::string &helpMessage,
3900
                      std::vector<std::string> *pValue)
3901
0
{
3902
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3903
0
        this,
3904
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3905
0
                             GAAT_STRING_LIST),
3906
0
        pValue));
3907
0
}
3908
3909
GDALInConstructionAlgorithmArg &
3910
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3911
                      const std::string &helpMessage, std::vector<int> *pValue)
3912
0
{
3913
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3914
0
        this,
3915
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3916
0
                             GAAT_INTEGER_LIST),
3917
0
        pValue));
3918
0
}
3919
3920
GDALInConstructionAlgorithmArg &
3921
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3922
                      const std::string &helpMessage,
3923
                      std::vector<double> *pValue)
3924
0
{
3925
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3926
0
        this,
3927
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3928
0
                             GAAT_REAL_LIST),
3929
0
        pValue));
3930
0
}
3931
3932
GDALInConstructionAlgorithmArg &
3933
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3934
                      const std::string &helpMessage,
3935
                      std::vector<GDALArgDatasetValue> *pValue,
3936
                      GDALArgDatasetType type)
3937
0
{
3938
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3939
0
                      this,
3940
0
                      GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3941
0
                                           GAAT_DATASET_LIST),
3942
0
                      pValue))
3943
0
        .SetDatasetType(type);
3944
0
}
3945
3946
/************************************************************************/
3947
/*                            MsgOrDefault()                            */
3948
/************************************************************************/
3949
3950
inline const char *MsgOrDefault(const char *helpMessage,
3951
                                const char *defaultMessage)
3952
0
{
3953
0
    return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3954
0
}
3955
3956
/************************************************************************/
3957
/*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
3958
/************************************************************************/
3959
3960
/* static */
3961
void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3962
    GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3963
0
{
3964
0
    arg.SetAutoCompleteFunction(
3965
0
        [&arg,
3966
0
         type](const std::string &currentValue) -> std::vector<std::string>
3967
0
        {
3968
0
            std::vector<std::string> oRet;
3969
3970
0
            if (arg.IsHidden())
3971
0
                return oRet;
3972
3973
0
            {
3974
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3975
0
                VSIStatBufL sStat;
3976
0
                if (!currentValue.empty() && currentValue.back() != '/' &&
3977
0
                    VSIStatL(currentValue.c_str(), &sStat) == 0)
3978
0
                {
3979
0
                    return oRet;
3980
0
                }
3981
0
            }
3982
3983
0
            auto poDM = GetGDALDriverManager();
3984
0
            std::set<std::string> oExtensions;
3985
0
            if (type)
3986
0
            {
3987
0
                for (int i = 0; i < poDM->GetDriverCount(); ++i)
3988
0
                {
3989
0
                    auto poDriver = poDM->GetDriver(i);
3990
0
                    if (((type & GDAL_OF_RASTER) != 0 &&
3991
0
                         poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3992
0
                        ((type & GDAL_OF_VECTOR) != 0 &&
3993
0
                         poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3994
0
                        ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3995
0
                         poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3996
0
                    {
3997
0
                        const char *pszExtensions =
3998
0
                            poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3999
0
                        if (pszExtensions)
4000
0
                        {
4001
0
                            const CPLStringList aosExts(
4002
0
                                CSLTokenizeString2(pszExtensions, " ", 0));
4003
0
                            for (const char *pszExt : cpl::Iterate(aosExts))
4004
0
                                oExtensions.insert(CPLString(pszExt).tolower());
4005
0
                        }
4006
0
                    }
4007
0
                }
4008
0
            }
4009
4010
0
            std::string osDir;
4011
0
            const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
4012
0
            std::string osPrefix;
4013
0
            if (STARTS_WITH(currentValue.c_str(), "/vsi"))
4014
0
            {
4015
0
                for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
4016
0
                {
4017
0
                    if (STARTS_WITH(currentValue.c_str(), pszPrefix))
4018
0
                    {
4019
0
                        osPrefix = pszPrefix;
4020
0
                        break;
4021
0
                    }
4022
0
                }
4023
0
                if (osPrefix.empty())
4024
0
                    return aosVSIPrefixes;
4025
0
                if (currentValue == osPrefix)
4026
0
                    osDir = osPrefix;
4027
0
            }
4028
0
            if (osDir.empty())
4029
0
            {
4030
0
                osDir = CPLGetDirnameSafe(currentValue.c_str());
4031
0
                if (!osPrefix.empty() && osDir.size() < osPrefix.size())
4032
0
                    osDir = std::move(osPrefix);
4033
0
            }
4034
4035
0
            auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
4036
0
            const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
4037
0
            if (currentValue.empty())
4038
0
                osDir.clear();
4039
0
            const std::string currentFilename =
4040
0
                CPLGetFilename(currentValue.c_str());
4041
0
            if (psDir)
4042
0
            {
4043
0
                while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
4044
0
                {
4045
0
                    if ((currentFilename.empty() ||
4046
0
                         STARTS_WITH(psEntry->pszName,
4047
0
                                     currentFilename.c_str())) &&
4048
0
                        strcmp(psEntry->pszName, ".") != 0 &&
4049
0
                        strcmp(psEntry->pszName, "..") != 0 &&
4050
0
                        (oExtensions.empty() ||
4051
0
                         !strstr(psEntry->pszName, ".aux.xml")))
4052
0
                    {
4053
0
                        if (oExtensions.empty() ||
4054
0
                            cpl::contains(
4055
0
                                oExtensions,
4056
0
                                CPLString(CPLGetExtensionSafe(psEntry->pszName))
4057
0
                                    .tolower()) ||
4058
0
                            VSI_ISDIR(psEntry->nMode))
4059
0
                        {
4060
0
                            std::string osVal;
4061
0
                            if (osDir.empty() || osDir == ".")
4062
0
                                osVal = psEntry->pszName;
4063
0
                            else
4064
0
                                osVal = CPLFormFilenameSafe(
4065
0
                                    osDir.c_str(), psEntry->pszName, nullptr);
4066
0
                            if (VSI_ISDIR(psEntry->nMode))
4067
0
                                osVal += osSep;
4068
0
                            oRet.push_back(std::move(osVal));
4069
0
                        }
4070
0
                    }
4071
0
                }
4072
0
                VSICloseDir(psDir);
4073
0
            }
4074
0
            return oRet;
4075
0
        });
4076
0
}
4077
4078
/************************************************************************/
4079
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
4080
/************************************************************************/
4081
4082
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4083
    GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4084
    bool positionalAndRequired, const char *helpMessage)
4085
0
{
4086
0
    auto &arg = AddArg(
4087
0
        GDAL_ARG_NAME_INPUT, 'i',
4088
0
        MsgOrDefault(helpMessage,
4089
0
                     CPLSPrintf("Input %s dataset",
4090
0
                                GDALAlgorithmArgDatasetTypeName(type).c_str())),
4091
0
        pValue, type);
4092
0
    if (positionalAndRequired)
4093
0
        arg.SetPositional().SetRequired();
4094
4095
0
    SetAutoCompleteFunctionForFilename(arg, type);
4096
4097
0
    AddValidationAction(
4098
0
        [pValue]()
4099
0
        {
4100
0
            if (pValue->GetName() == "-")
4101
0
                pValue->Set("/vsistdin/");
4102
0
            return true;
4103
0
        });
4104
4105
0
    return arg;
4106
0
}
4107
4108
/************************************************************************/
4109
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
4110
/************************************************************************/
4111
4112
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4113
    std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
4114
    bool positionalAndRequired, const char *helpMessage)
4115
0
{
4116
0
    auto &arg =
4117
0
        AddArg(GDAL_ARG_NAME_INPUT, 'i',
4118
0
               MsgOrDefault(
4119
0
                   helpMessage,
4120
0
                   CPLSPrintf("Input %s datasets",
4121
0
                              GDALAlgorithmArgDatasetTypeName(type).c_str())),
4122
0
               pValue, type)
4123
0
            .SetPackedValuesAllowed(false);
4124
0
    if (positionalAndRequired)
4125
0
        arg.SetPositional().SetRequired();
4126
4127
0
    SetAutoCompleteFunctionForFilename(arg, type);
4128
4129
0
    AddValidationAction(
4130
0
        [pValue]()
4131
0
        {
4132
0
            for (auto &val : *pValue)
4133
0
            {
4134
0
                if (val.GetName() == "-")
4135
0
                    val.Set("/vsistdin/");
4136
0
            }
4137
0
            return true;
4138
0
        });
4139
0
    return arg;
4140
0
}
4141
4142
/************************************************************************/
4143
/*                 GDALAlgorithm::AddOutputDatasetArg()                 */
4144
/************************************************************************/
4145
4146
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4147
    GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4148
    bool positionalAndRequired, const char *helpMessage)
4149
0
{
4150
0
    auto &arg =
4151
0
        AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4152
0
               MsgOrDefault(
4153
0
                   helpMessage,
4154
0
                   CPLSPrintf("Output %s dataset",
4155
0
                              GDALAlgorithmArgDatasetTypeName(type).c_str())),
4156
0
               pValue, type)
4157
0
            .SetIsInput(true)
4158
0
            .SetIsOutput(true)
4159
0
            .SetDatasetInputFlags(GADV_NAME)
4160
0
            .SetDatasetOutputFlags(GADV_OBJECT);
4161
0
    if (positionalAndRequired)
4162
0
        arg.SetPositional().SetRequired();
4163
4164
0
    AddValidationAction(
4165
0
        [this, &arg, pValue]()
4166
0
        {
4167
0
            if (pValue->GetName() == "-")
4168
0
                pValue->Set("/vsistdout/");
4169
4170
0
            auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4171
0
            if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4172
0
                (!outputFormatArg->IsExplicitlySet() ||
4173
0
                 outputFormatArg->Get<std::string>().empty()) &&
4174
0
                arg.IsExplicitlySet())
4175
0
            {
4176
0
                const auto vrtCompatible =
4177
0
                    outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4178
0
                if (vrtCompatible && !vrtCompatible->empty() &&
4179
0
                    vrtCompatible->front() == "false" &&
4180
0
                    EQUAL(
4181
0
                        CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4182
0
                        "VRT"))
4183
0
                {
4184
0
                    ReportError(
4185
0
                        CE_Failure, CPLE_NotSupported,
4186
0
                        "VRT output is not supported.%s",
4187
0
                        outputFormatArg->GetDescription().find("GDALG") !=
4188
0
                                std::string::npos
4189
0
                            ? " Consider using the GDALG driver instead (files "
4190
0
                              "with .gdalg.json extension)"
4191
0
                            : "");
4192
0
                    return false;
4193
0
                }
4194
0
                else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4195
0
                         EQUAL(pValue->GetName()
4196
0
                                   .substr(pValue->GetName().size() -
4197
0
                                           strlen(".gdalg.json"))
4198
0
                                   .c_str(),
4199
0
                               ".gdalg.json") &&
4200
0
                         outputFormatArg->GetDescription().find("GDALG") ==
4201
0
                             std::string::npos)
4202
0
                {
4203
0
                    ReportError(CE_Failure, CPLE_NotSupported,
4204
0
                                "GDALG output is not supported");
4205
0
                    return false;
4206
0
                }
4207
0
            }
4208
0
            return true;
4209
0
        });
4210
4211
0
    return arg;
4212
0
}
4213
4214
/************************************************************************/
4215
/*                   GDALAlgorithm::AddOverwriteArg()                   */
4216
/************************************************************************/
4217
4218
GDALInConstructionAlgorithmArg &
4219
GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4220
0
{
4221
0
    return AddArg(
4222
0
               GDAL_ARG_NAME_OVERWRITE, 0,
4223
0
               MsgOrDefault(
4224
0
                   helpMessage,
4225
0
                   _("Whether overwriting existing output dataset is allowed")),
4226
0
               pValue)
4227
0
        .SetDefault(false);
4228
0
}
4229
4230
/************************************************************************/
4231
/*                GDALAlgorithm::AddOverwriteLayerArg()                 */
4232
/************************************************************************/
4233
4234
GDALInConstructionAlgorithmArg &
4235
GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4236
0
{
4237
0
    AddValidationAction(
4238
0
        [this]
4239
0
        {
4240
0
            auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4241
0
            if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4242
0
            {
4243
0
                ReportError(CE_Failure, CPLE_AppDefined,
4244
0
                            "--update argument must exist for "
4245
0
                            "--overwrite-layer, even if hidden");
4246
0
                return false;
4247
0
            }
4248
0
            return true;
4249
0
        });
4250
0
    return AddArg(
4251
0
               GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4252
0
               MsgOrDefault(
4253
0
                   helpMessage,
4254
0
                   _("Whether overwriting existing output layer is allowed")),
4255
0
               pValue)
4256
0
        .SetDefault(false)
4257
0
        .AddAction(
4258
0
            [this]
4259
0
            {
4260
0
                auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4261
0
                if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4262
0
                {
4263
0
                    updateArg->Set(true);
4264
0
                }
4265
0
            });
4266
0
}
4267
4268
/************************************************************************/
4269
/*                    GDALAlgorithm::AddUpdateArg()                     */
4270
/************************************************************************/
4271
4272
GDALInConstructionAlgorithmArg &
4273
GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4274
0
{
4275
0
    return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4276
0
                  MsgOrDefault(
4277
0
                      helpMessage,
4278
0
                      _("Whether to open existing dataset in update mode")),
4279
0
                  pValue)
4280
0
        .SetDefault(false);
4281
0
}
4282
4283
/************************************************************************/
4284
/*                  GDALAlgorithm::AddAppendLayerArg()                  */
4285
/************************************************************************/
4286
4287
GDALInConstructionAlgorithmArg &
4288
GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4289
0
{
4290
0
    AddValidationAction(
4291
0
        [this]
4292
0
        {
4293
0
            auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4294
0
            if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4295
0
            {
4296
0
                ReportError(CE_Failure, CPLE_AppDefined,
4297
0
                            "--update argument must exist for --append, even "
4298
0
                            "if hidden");
4299
0
                return false;
4300
0
            }
4301
0
            return true;
4302
0
        });
4303
0
    return AddArg(GDAL_ARG_NAME_APPEND, 0,
4304
0
                  MsgOrDefault(
4305
0
                      helpMessage,
4306
0
                      _("Whether appending to existing layer is allowed")),
4307
0
                  pValue)
4308
0
        .SetDefault(false)
4309
0
        .AddAction(
4310
0
            [this]
4311
0
            {
4312
0
                auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4313
0
                if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4314
0
                {
4315
0
                    updateArg->Set(true);
4316
0
                }
4317
0
            });
4318
0
}
4319
4320
/************************************************************************/
4321
/*                GDALAlgorithm::AddOptionsSuggestions()                */
4322
/************************************************************************/
4323
4324
/* static */
4325
bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4326
                                          const std::string &currentValue,
4327
                                          std::vector<std::string> &oRet)
4328
0
{
4329
0
    if (!pszXML)
4330
0
        return false;
4331
0
    CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4332
0
    if (!poTree)
4333
0
        return false;
4334
4335
0
    std::string typedOptionName = currentValue;
4336
0
    const auto posEqual = typedOptionName.find('=');
4337
0
    std::string typedValue;
4338
0
    if (posEqual != 0 && posEqual != std::string::npos)
4339
0
    {
4340
0
        typedValue = currentValue.substr(posEqual + 1);
4341
0
        typedOptionName.resize(posEqual);
4342
0
    }
4343
4344
0
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4345
0
         psChild = psChild->psNext)
4346
0
    {
4347
0
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4348
0
        if (pszName && typedOptionName == pszName &&
4349
0
            (strcmp(psChild->pszValue, "Option") == 0 ||
4350
0
             strcmp(psChild->pszValue, "Argument") == 0))
4351
0
        {
4352
0
            const char *pszType = CPLGetXMLValue(psChild, "type", "");
4353
0
            const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4354
0
            const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4355
0
            if (EQUAL(pszType, "string-select"))
4356
0
            {
4357
0
                for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4358
0
                     psChild2 = psChild2->psNext)
4359
0
                {
4360
0
                    if (EQUAL(psChild2->pszValue, "Value"))
4361
0
                    {
4362
0
                        oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4363
0
                    }
4364
0
                }
4365
0
            }
4366
0
            else if (EQUAL(pszType, "boolean"))
4367
0
            {
4368
0
                if (typedValue == "YES" || typedValue == "NO")
4369
0
                {
4370
0
                    oRet.push_back(currentValue);
4371
0
                    return true;
4372
0
                }
4373
0
                oRet.push_back("NO");
4374
0
                oRet.push_back("YES");
4375
0
            }
4376
0
            else if (EQUAL(pszType, "int"))
4377
0
            {
4378
0
                if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4379
0
                    atoi(pszMax) - atoi(pszMin) < 25)
4380
0
                {
4381
0
                    const int nMax = atoi(pszMax);
4382
0
                    for (int i = atoi(pszMin); i <= nMax; ++i)
4383
0
                        oRet.push_back(std::to_string(i));
4384
0
                }
4385
0
            }
4386
4387
0
            if (oRet.empty())
4388
0
            {
4389
0
                if (pszMin && pszMax)
4390
0
                {
4391
0
                    oRet.push_back(std::string("##"));
4392
0
                    oRet.push_back(std::string("validity range: [")
4393
0
                                       .append(pszMin)
4394
0
                                       .append(",")
4395
0
                                       .append(pszMax)
4396
0
                                       .append("]"));
4397
0
                }
4398
0
                else if (pszMin)
4399
0
                {
4400
0
                    oRet.push_back(std::string("##"));
4401
0
                    oRet.push_back(
4402
0
                        std::string("validity range: >= ").append(pszMin));
4403
0
                }
4404
0
                else if (pszMax)
4405
0
                {
4406
0
                    oRet.push_back(std::string("##"));
4407
0
                    oRet.push_back(
4408
0
                        std::string("validity range: <= ").append(pszMax));
4409
0
                }
4410
0
                else if (const char *pszDescription =
4411
0
                             CPLGetXMLValue(psChild, "description", nullptr))
4412
0
                {
4413
0
                    oRet.push_back(std::string("##"));
4414
0
                    oRet.push_back(std::string("type: ")
4415
0
                                       .append(pszType)
4416
0
                                       .append(", description: ")
4417
0
                                       .append(pszDescription));
4418
0
                }
4419
0
            }
4420
4421
0
            return true;
4422
0
        }
4423
0
    }
4424
4425
0
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4426
0
         psChild = psChild->psNext)
4427
0
    {
4428
0
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4429
0
        if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4430
0
                        strcmp(psChild->pszValue, "Argument") == 0))
4431
0
        {
4432
0
            const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4433
0
            if (!pszScope ||
4434
0
                (EQUAL(pszScope, "raster") &&
4435
0
                 (datasetType & GDAL_OF_RASTER) != 0) ||
4436
0
                (EQUAL(pszScope, "vector") &&
4437
0
                 (datasetType & GDAL_OF_VECTOR) != 0))
4438
0
            {
4439
0
                oRet.push_back(std::string(pszName).append("="));
4440
0
            }
4441
0
        }
4442
0
    }
4443
4444
0
    return false;
4445
0
}
4446
4447
/************************************************************************/
4448
/*             GDALAlgorithm::OpenOptionCompleteFunction()              */
4449
/************************************************************************/
4450
4451
//! @cond Doxygen_Suppress
4452
std::vector<std::string>
4453
GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
4454
0
{
4455
0
    std::vector<std::string> oRet;
4456
4457
0
    int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4458
0
    auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4459
0
    if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4460
0
                     inputArg->GetType() == GAAT_DATASET_LIST))
4461
0
    {
4462
0
        datasetType = inputArg->GetDatasetType();
4463
0
    }
4464
4465
0
    auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4466
0
    if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4467
0
        inputFormat->IsExplicitlySet())
4468
0
    {
4469
0
        const auto &aosAllowedDrivers =
4470
0
            inputFormat->Get<std::vector<std::string>>();
4471
0
        if (aosAllowedDrivers.size() == 1)
4472
0
        {
4473
0
            auto poDriver = GetGDALDriverManager()->GetDriverByName(
4474
0
                aosAllowedDrivers[0].c_str());
4475
0
            if (poDriver)
4476
0
            {
4477
0
                AddOptionsSuggestions(
4478
0
                    poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4479
0
                    datasetType, currentValue, oRet);
4480
0
            }
4481
0
            return oRet;
4482
0
        }
4483
0
    }
4484
4485
0
    const auto AddSuggestions = [datasetType, &currentValue,
4486
0
                                 &oRet](const GDALArgDatasetValue &datasetValue)
4487
0
    {
4488
0
        auto poDM = GetGDALDriverManager();
4489
4490
0
        const auto &osDSName = datasetValue.GetName();
4491
0
        const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4492
0
        if (!osExt.empty())
4493
0
        {
4494
0
            std::set<std::string> oVisitedExtensions;
4495
0
            for (int i = 0; i < poDM->GetDriverCount(); ++i)
4496
0
            {
4497
0
                auto poDriver = poDM->GetDriver(i);
4498
0
                if (((datasetType & GDAL_OF_RASTER) != 0 &&
4499
0
                     poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4500
0
                    ((datasetType & GDAL_OF_VECTOR) != 0 &&
4501
0
                     poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4502
0
                    ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4503
0
                     poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4504
0
                {
4505
0
                    const char *pszExtensions =
4506
0
                        poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4507
0
                    if (pszExtensions)
4508
0
                    {
4509
0
                        const CPLStringList aosExts(
4510
0
                            CSLTokenizeString2(pszExtensions, " ", 0));
4511
0
                        for (const char *pszExt : cpl::Iterate(aosExts))
4512
0
                        {
4513
0
                            if (EQUAL(pszExt, osExt.c_str()) &&
4514
0
                                !cpl::contains(oVisitedExtensions, pszExt))
4515
0
                            {
4516
0
                                oVisitedExtensions.insert(pszExt);
4517
0
                                if (AddOptionsSuggestions(
4518
0
                                        poDriver->GetMetadataItem(
4519
0
                                            GDAL_DMD_OPENOPTIONLIST),
4520
0
                                        datasetType, currentValue, oRet))
4521
0
                                {
4522
0
                                    return;
4523
0
                                }
4524
0
                                break;
4525
0
                            }
4526
0
                        }
4527
0
                    }
4528
0
                }
4529
0
            }
4530
0
        }
4531
0
    };
4532
4533
0
    if (inputArg && inputArg->GetType() == GAAT_DATASET)
4534
0
    {
4535
0
        auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4536
0
        AddSuggestions(datasetValue);
4537
0
    }
4538
0
    else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4539
0
    {
4540
0
        auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4541
0
        if (datasetValues.size() == 1)
4542
0
            AddSuggestions(datasetValues[0]);
4543
0
    }
4544
4545
0
    return oRet;
4546
0
}
4547
4548
//! @endcond
4549
4550
/************************************************************************/
4551
/*                  GDALAlgorithm::AddOpenOptionsArg()                  */
4552
/************************************************************************/
4553
4554
GDALInConstructionAlgorithmArg &
4555
GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4556
                                 const char *helpMessage)
4557
0
{
4558
0
    auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4559
0
                       MsgOrDefault(helpMessage, _("Open options")), pValue)
4560
0
                    .AddAlias("oo")
4561
0
                    .SetMetaVar("<KEY>=<VALUE>")
4562
0
                    .SetPackedValuesAllowed(false)
4563
0
                    .SetCategory(GAAC_ADVANCED);
4564
4565
0
    arg.AddValidationAction([this, &arg]()
4566
0
                            { return ParseAndValidateKeyValue(arg); });
4567
4568
0
    arg.SetAutoCompleteFunction(
4569
0
        [this](const std::string &currentValue)
4570
0
        { return OpenOptionCompleteFunction(currentValue); });
4571
4572
0
    return arg;
4573
0
}
4574
4575
/************************************************************************/
4576
/*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
4577
/************************************************************************/
4578
4579
GDALInConstructionAlgorithmArg &
4580
GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4581
                                       const char *helpMessage)
4582
0
{
4583
0
    auto &arg =
4584
0
        AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4585
0
               MsgOrDefault(helpMessage, _("Output open options")), pValue)
4586
0
            .AddAlias("output-oo")
4587
0
            .SetMetaVar("<KEY>=<VALUE>")
4588
0
            .SetPackedValuesAllowed(false)
4589
0
            .SetCategory(GAAC_ADVANCED);
4590
4591
0
    arg.AddValidationAction([this, &arg]()
4592
0
                            { return ParseAndValidateKeyValue(arg); });
4593
4594
0
    arg.SetAutoCompleteFunction(
4595
0
        [this](const std::string &currentValue)
4596
0
        { return OpenOptionCompleteFunction(currentValue); });
4597
4598
0
    return arg;
4599
0
}
4600
4601
/************************************************************************/
4602
/*                    NormalizeRequiredCapability()                     */
4603
/************************************************************************/
4604
4605
/** Expands the GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER alias into the
4606
 * generic form of alternatives separated by '|'.
4607
 */
4608
static std::string NormalizeRequiredCapability(const std::string &osRequiredCap)
4609
0
{
4610
0
    return osRequiredCap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER
4611
0
               ? GDAL_DCAP_RASTER "|" GDAL_DCAP_MULTIDIM_RASTER
4612
0
               : osRequiredCap;
4613
0
}
4614
4615
/************************************************************************/
4616
/*                        DriverHasCapability()                         */
4617
/************************************************************************/
4618
4619
/** Returns whether poDriver meets the osRequiredCap requirement, which may
4620
 * express alternatives separated by '|', among the AND-ed list of
4621
 * requirements requiredCaps.
4622
 */
4623
static bool DriverHasCapability(GDALDriver *poDriver,
4624
                                const std::string &osRequiredCap,
4625
                                const std::vector<std::string> &requiredCaps)
4626
0
{
4627
0
    const CPLStringList aosAlternatives(CSLTokenizeString2(
4628
0
        NormalizeRequiredCapability(osRequiredCap).c_str(), "|", 0));
4629
0
    for (const char *pszCap : cpl::Iterate(aosAlternatives))
4630
0
    {
4631
0
        const char *pszVal = poDriver->GetMetadataItem(pszCap);
4632
0
        if (pszVal && pszVal[0])
4633
0
        {
4634
0
            return true;
4635
0
        }
4636
        // if it supports Create, it supports CreateCopy. GDAL_DCAP_RASTER is
4637
        // matched as a whole entry, i.e. not as one of the '|' alternatives of
4638
        // an entry, which is how all requirement lists spell it.
4639
0
        else if (EQUAL(pszCap, GDAL_DCAP_CREATECOPY) &&
4640
0
                 std::find(requiredCaps.begin(), requiredCaps.end(),
4641
0
                           GDAL_DCAP_RASTER) != requiredCaps.end() &&
4642
0
                 poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4643
0
                 poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4644
0
        {
4645
0
            return true;
4646
0
        }
4647
0
    }
4648
0
    return false;
4649
0
}
4650
4651
/************************************************************************/
4652
/*                           ValidateFormat()                           */
4653
/************************************************************************/
4654
4655
bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4656
                                   bool bStreamAllowed,
4657
                                   bool bGDALGAllowed) const
4658
0
{
4659
0
    if (arg.GetChoices().empty())
4660
0
    {
4661
0
        const auto Validate =
4662
0
            [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4663
0
        {
4664
0
            if (const auto extraFormats =
4665
0
                    arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4666
0
            {
4667
0
                for (const auto &extraFormat : *extraFormats)
4668
0
                {
4669
0
                    if (EQUAL(val.c_str(), extraFormat.c_str()))
4670
0
                        return true;
4671
0
                }
4672
0
            }
4673
4674
0
            if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4675
0
                return true;
4676
4677
0
            if (EQUAL(val.c_str(), "GDALG") &&
4678
0
                arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4679
0
            {
4680
0
                if (bGDALGAllowed)
4681
0
                {
4682
0
                    return true;
4683
0
                }
4684
0
                else
4685
0
                {
4686
0
                    ReportError(CE_Failure, CPLE_NotSupported,
4687
0
                                "GDALG output is not supported.");
4688
0
                    return false;
4689
0
                }
4690
0
            }
4691
4692
0
            const auto vrtCompatible =
4693
0
                arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4694
0
            if (vrtCompatible && !vrtCompatible->empty() &&
4695
0
                vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4696
0
            {
4697
0
                ReportError(CE_Failure, CPLE_NotSupported,
4698
0
                            "VRT output is not supported.%s",
4699
0
                            bGDALGAllowed
4700
0
                                ? " Consider using the GDALG driver instead "
4701
0
                                  "(files with .gdalg.json extension)."
4702
0
                                : "");
4703
0
                return false;
4704
0
            }
4705
4706
0
            const auto allowedFormats =
4707
0
                arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4708
0
            if (allowedFormats && !allowedFormats->empty() &&
4709
0
                std::find(allowedFormats->begin(), allowedFormats->end(),
4710
0
                          val) != allowedFormats->end())
4711
0
            {
4712
0
                return true;
4713
0
            }
4714
4715
0
            const auto excludedFormats =
4716
0
                arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4717
0
            if (excludedFormats && !excludedFormats->empty() &&
4718
0
                std::find(excludedFormats->begin(), excludedFormats->end(),
4719
0
                          val) != excludedFormats->end())
4720
0
            {
4721
0
                ReportError(CE_Failure, CPLE_NotSupported,
4722
0
                            "%s output is not supported.", val.c_str());
4723
0
                return false;
4724
0
            }
4725
4726
0
            auto hDriver = GDALGetDriverByName(val.c_str());
4727
0
            if (!hDriver)
4728
0
            {
4729
0
                auto poMissingDriver =
4730
0
                    GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4731
0
                if (poMissingDriver)
4732
0
                {
4733
0
                    const std::string msg =
4734
0
                        GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4735
0
                    ReportError(CE_Failure, CPLE_AppDefined,
4736
0
                                "Invalid value for argument '%s'. Driver '%s' "
4737
0
                                "not found but is known. However plugin %s",
4738
0
                                arg.GetName().c_str(), val.c_str(),
4739
0
                                msg.c_str());
4740
0
                }
4741
0
                else
4742
0
                {
4743
0
                    ReportError(CE_Failure, CPLE_AppDefined,
4744
0
                                "Invalid value for argument '%s'. Driver '%s' "
4745
0
                                "does not exist.",
4746
0
                                arg.GetName().c_str(), val.c_str());
4747
0
                }
4748
0
                return false;
4749
0
            }
4750
4751
0
            const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4752
0
            if (caps)
4753
0
            {
4754
0
                auto poDriver = GDALDriver::FromHandle(hDriver);
4755
0
                for (const std::string &cap : *caps)
4756
0
                {
4757
0
                    if (DriverHasCapability(poDriver, cap, *caps))
4758
0
                        continue;
4759
4760
0
                    if (cap == GDAL_DMD_EXTENSIONS)
4761
0
                    {
4762
0
                        ReportError(CE_Failure, CPLE_AppDefined,
4763
0
                                    "Invalid value for argument '%s'. Driver "
4764
0
                                    "'%s' does not advertise any file format "
4765
0
                                    "extension.",
4766
0
                                    arg.GetName().c_str(), val.c_str());
4767
0
                        return false;
4768
0
                    }
4769
0
                    else if (cap == GDAL_DCAP_CREATE)
4770
0
                    {
4771
0
                        auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4772
0
                        if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
4773
0
                            updateArg->IsExplicitlySet())
4774
0
                        {
4775
0
                            continue;
4776
0
                        }
4777
4778
0
                        ReportError(CE_Failure, CPLE_AppDefined,
4779
0
                                    "Invalid value for argument '%s'. "
4780
0
                                    "Driver '%s' does not have write support.",
4781
0
                                    arg.GetName().c_str(), val.c_str());
4782
0
                        return false;
4783
0
                    }
4784
0
                    else
4785
0
                    {
4786
0
                        CPLString osCap(NormalizeRequiredCapability(cap));
4787
0
                        osCap.replaceAll("|", " or ");
4788
0
                        ReportError(CE_Failure, CPLE_AppDefined,
4789
0
                                    "Invalid value for argument '%s'. Driver "
4790
0
                                    "'%s' does not expose the required '%s' "
4791
0
                                    "capability.",
4792
0
                                    arg.GetName().c_str(), val.c_str(),
4793
0
                                    osCap.c_str());
4794
0
                        return false;
4795
0
                    }
4796
0
                }
4797
0
            }
4798
0
            return true;
4799
0
        };
4800
4801
0
        if (arg.GetType() == GAAT_STRING)
4802
0
        {
4803
0
            return Validate(arg.Get<std::string>());
4804
0
        }
4805
0
        else if (arg.GetType() == GAAT_STRING_LIST)
4806
0
        {
4807
0
            for (const auto &val : arg.Get<std::vector<std::string>>())
4808
0
            {
4809
0
                if (!Validate(val))
4810
0
                    return false;
4811
0
            }
4812
0
        }
4813
0
    }
4814
4815
0
    return true;
4816
0
}
4817
4818
/************************************************************************/
4819
/*                     FormatAutoCompleteFunction()                     */
4820
/************************************************************************/
4821
4822
/* static */
4823
std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4824
    const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4825
0
{
4826
0
    std::vector<std::string> res;
4827
0
    auto poDM = GetGDALDriverManager();
4828
0
    const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4829
0
    const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4830
0
    const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4831
0
    const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4832
0
    if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4833
0
        res = std::move(*extraFormats);
4834
0
    for (int i = 0; i < poDM->GetDriverCount(); ++i)
4835
0
    {
4836
0
        auto poDriver = poDM->GetDriver(i);
4837
4838
0
        if (vrtCompatible && !vrtCompatible->empty() &&
4839
0
            vrtCompatible->front() == "false" &&
4840
0
            EQUAL(poDriver->GetDescription(), "VRT"))
4841
0
        {
4842
            // do nothing
4843
0
        }
4844
0
        else if (allowedFormats && !allowedFormats->empty() &&
4845
0
                 std::find(allowedFormats->begin(), allowedFormats->end(),
4846
0
                           poDriver->GetDescription()) != allowedFormats->end())
4847
0
        {
4848
0
            res.push_back(poDriver->GetDescription());
4849
0
        }
4850
0
        else if (excludedFormats && !excludedFormats->empty() &&
4851
0
                 std::find(excludedFormats->begin(), excludedFormats->end(),
4852
0
                           poDriver->GetDescription()) !=
4853
0
                     excludedFormats->end())
4854
0
        {
4855
0
            continue;
4856
0
        }
4857
0
        else if (caps)
4858
0
        {
4859
0
            bool ok = true;
4860
0
            for (const std::string &cap : *caps)
4861
0
            {
4862
0
                if (!DriverHasCapability(poDriver, cap, *caps))
4863
0
                {
4864
0
                    ok = false;
4865
0
                    break;
4866
0
                }
4867
0
            }
4868
0
            if (ok)
4869
0
            {
4870
0
                res.push_back(poDriver->GetDescription());
4871
0
            }
4872
0
        }
4873
0
    }
4874
0
    if (bGDALGAllowed)
4875
0
        res.push_back("GDALG");
4876
0
    return res;
4877
0
}
4878
4879
/************************************************************************/
4880
/*                 GDALAlgorithm::AddInputFormatsArg()                  */
4881
/************************************************************************/
4882
4883
GDALInConstructionAlgorithmArg &
4884
GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4885
                                  const char *helpMessage)
4886
0
{
4887
0
    auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4888
0
                       MsgOrDefault(helpMessage, _("Input formats")), pValue)
4889
0
                    .AddAlias("if")
4890
0
                    .SetCategory(GAAC_ADVANCED);
4891
0
    arg.AddValidationAction([this, &arg]()
4892
0
                            { return ValidateFormat(arg, false, false); });
4893
0
    arg.SetAutoCompleteFunction(
4894
0
        [&arg](const std::string &)
4895
0
        { return FormatAutoCompleteFunction(arg, false, false); });
4896
0
    return arg;
4897
0
}
4898
4899
/************************************************************************/
4900
/*                 GDALAlgorithm::AddOutputFormatArg()                  */
4901
/************************************************************************/
4902
4903
GDALInConstructionAlgorithmArg &
4904
GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4905
                                  bool bGDALGAllowed, const char *helpMessage)
4906
0
{
4907
0
    auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4908
0
                       MsgOrDefault(helpMessage,
4909
0
                                    bGDALGAllowed
4910
0
                                        ? _("Output format (\"GDALG\" allowed)")
4911
0
                                        : _("Output format")),
4912
0
                       pValue)
4913
0
                    .AddAlias("of")
4914
0
                    .AddAlias("format");
4915
0
    arg.AddValidationAction(
4916
0
        [this, &arg, bStreamAllowed, bGDALGAllowed]()
4917
0
        { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4918
0
    arg.SetAutoCompleteFunction(
4919
0
        [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4920
0
        {
4921
0
            return FormatAutoCompleteFunction(arg, bStreamAllowed,
4922
0
                                              bGDALGAllowed);
4923
0
        });
4924
0
    return arg;
4925
0
}
4926
4927
/************************************************************************/
4928
/*                GDALAlgorithm::AddOutputDataTypeArg()                 */
4929
/************************************************************************/
4930
GDALInConstructionAlgorithmArg &
4931
GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4932
                                    const char *helpMessage)
4933
0
{
4934
0
    auto &arg =
4935
0
        AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4936
0
               MsgOrDefault(helpMessage, _("Output data type")), pValue)
4937
0
            .AddAlias("ot")
4938
0
            .AddAlias("datatype")
4939
0
            .AddMetadataItem("type", {"GDALDataType"})
4940
0
            .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4941
0
                        "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4942
0
                        "Float32", "Float64", "CFloat32", "CFloat64")
4943
0
            .SetHiddenChoices("Byte");
4944
0
    return arg;
4945
0
}
4946
4947
/************************************************************************/
4948
/*                    GDALAlgorithm::AddNodataArg()                     */
4949
/************************************************************************/
4950
4951
GDALInConstructionAlgorithmArg &
4952
GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4953
                            const std::string &optionName,
4954
                            const char *helpMessage)
4955
0
{
4956
0
    auto &arg = AddArg(
4957
0
        optionName, 0,
4958
0
        MsgOrDefault(helpMessage,
4959
0
                     noneAllowed
4960
0
                         ? _("Assign a specified nodata value to output bands "
4961
0
                             "('none', numeric value, 'nan', 'inf', '-inf')")
4962
0
                         : _("Assign a specified nodata value to output bands "
4963
0
                             "(numeric value, 'nan', 'inf', '-inf')")),
4964
0
        pValue);
4965
0
    arg.AddValidationAction(
4966
0
        [this, pValue, noneAllowed, optionName]()
4967
0
        {
4968
0
            if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4969
0
            {
4970
0
                char *endptr = nullptr;
4971
0
                CPLStrtod(pValue->c_str(), &endptr);
4972
0
                if (endptr != pValue->c_str() + pValue->size())
4973
0
                {
4974
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
4975
0
                                "Value of '%s' should be %sa "
4976
0
                                "numeric value, 'nan', 'inf' or '-inf'",
4977
0
                                optionName.c_str(),
4978
0
                                noneAllowed ? "'none', " : "");
4979
0
                    return false;
4980
0
                }
4981
0
            }
4982
0
            return true;
4983
0
        });
4984
0
    return arg;
4985
0
}
4986
4987
/************************************************************************/
4988
/*                 GDALAlgorithm::AddOutputStringArg()                  */
4989
/************************************************************************/
4990
4991
GDALInConstructionAlgorithmArg &
4992
GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4993
0
{
4994
0
    return AddArg(
4995
0
               GDAL_ARG_NAME_OUTPUT_STRING, 0,
4996
0
               MsgOrDefault(helpMessage,
4997
0
                            _("Output string, in which the result is placed")),
4998
0
               pValue)
4999
0
        .SetHiddenForCLI()
5000
0
        .SetIsInput(false)
5001
0
        .SetIsOutput(true);
5002
0
}
5003
5004
/************************************************************************/
5005
/*                    GDALAlgorithm::AddStdoutArg()                     */
5006
/************************************************************************/
5007
5008
GDALInConstructionAlgorithmArg &
5009
GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
5010
0
{
5011
0
    return AddArg(GDAL_ARG_NAME_STDOUT, 0,
5012
0
                  MsgOrDefault(helpMessage,
5013
0
                               _("Directly output on stdout. If enabled, "
5014
0
                                 "output-string will be empty")),
5015
0
                  pValue)
5016
0
        .SetHidden();
5017
0
}
5018
5019
/************************************************************************/
5020
/*                   GDALAlgorithm::AddLayerNameArg()                   */
5021
/************************************************************************/
5022
5023
GDALInConstructionAlgorithmArg &
5024
GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
5025
0
{
5026
0
    return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5027
0
                  MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5028
0
}
5029
5030
/************************************************************************/
5031
/*                   GDALAlgorithm::AddArrayNameArg()                   */
5032
/************************************************************************/
5033
5034
GDALInConstructionAlgorithmArg &
5035
GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
5036
0
{
5037
0
    return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
5038
0
                  pValue)
5039
0
        .SetAutoCompleteFunction([this](const std::string &)
5040
0
                                 { return AutoCompleteArrayName(); });
5041
0
}
5042
5043
/************************************************************************/
5044
/*                   GDALAlgorithm::AddArrayNameArg()                   */
5045
/************************************************************************/
5046
5047
GDALInConstructionAlgorithmArg &
5048
GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
5049
                               const char *helpMessage)
5050
0
{
5051
0
    return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
5052
0
                  pValue)
5053
0
        .SetAutoCompleteFunction([this](const std::string &)
5054
0
                                 { return AutoCompleteArrayName(); });
5055
0
}
5056
5057
/************************************************************************/
5058
/*                GDALAlgorithm::AutoCompleteArrayName()                */
5059
/************************************************************************/
5060
5061
std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
5062
0
{
5063
0
    std::vector<std::string> ret;
5064
0
    std::string osDSName;
5065
0
    auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
5066
0
    if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
5067
0
    {
5068
0
        auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
5069
0
        if (!inputDatasets.empty())
5070
0
        {
5071
0
            osDSName = inputDatasets[0].GetName();
5072
0
        }
5073
0
    }
5074
0
    else if (inputArg && inputArg->GetType() == GAAT_DATASET)
5075
0
    {
5076
0
        auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
5077
0
        osDSName = inputDataset.GetName();
5078
0
    }
5079
5080
0
    if (!osDSName.empty())
5081
0
    {
5082
0
        CPLStringList aosAllowedDrivers;
5083
0
        const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
5084
0
        if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
5085
0
            aosAllowedDrivers =
5086
0
                CPLStringList(ifArg->Get<std::vector<std::string>>());
5087
5088
0
        CPLStringList aosOpenOptions;
5089
0
        const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
5090
0
        if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
5091
0
            aosOpenOptions =
5092
0
                CPLStringList(ooArg->Get<std::vector<std::string>>());
5093
5094
0
        if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5095
0
                osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
5096
0
                aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
5097
0
        {
5098
0
            if (auto poRG = poDS->GetRootGroup())
5099
0
            {
5100
0
                ret = poRG->GetMDArrayFullNamesRecursive();
5101
0
            }
5102
0
        }
5103
0
    }
5104
5105
0
    return ret;
5106
0
}
5107
5108
/************************************************************************/
5109
/*                  GDALAlgorithm::AddMemorySizeArg()                   */
5110
/************************************************************************/
5111
5112
GDALInConstructionAlgorithmArg &
5113
GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
5114
                                const std::string &optionName,
5115
                                const char *helpMessage)
5116
0
{
5117
0
    return AddArg(optionName, 0, helpMessage, pStrValue)
5118
0
        .SetDefault(*pStrValue)
5119
0
        .AddValidationAction(
5120
0
            [this, pValue, pStrValue]()
5121
0
            {
5122
0
                CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
5123
0
                GIntBig nBytes;
5124
0
                bool bUnitSpecified;
5125
0
                if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
5126
0
                                       &bUnitSpecified) != CE_None)
5127
0
                {
5128
0
                    return false;
5129
0
                }
5130
0
                if (!bUnitSpecified)
5131
0
                {
5132
0
                    ReportError(CE_Failure, CPLE_AppDefined,
5133
0
                                "Memory size must have a unit or be a "
5134
0
                                "percentage of usable RAM (2GB, 5%%, etc.)");
5135
0
                    return false;
5136
0
                }
5137
                if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5138
                {
5139
                    // -1 to please CoverityScan
5140
                    if (static_cast<std::uint64_t>(nBytes) >
5141
                        std::numeric_limits<size_t>::max() - 1U)
5142
                    {
5143
                        ReportError(CE_Failure, CPLE_AppDefined,
5144
                                    "Memory size %s is too large.",
5145
                                    pStrValue->c_str());
5146
                        return false;
5147
                    }
5148
                }
5149
5150
0
                *pValue = static_cast<size_t>(nBytes);
5151
0
                return true;
5152
0
            });
5153
0
}
5154
5155
/************************************************************************/
5156
/*                GDALAlgorithm::AddOutputLayerNameArg()                */
5157
/************************************************************************/
5158
5159
GDALInConstructionAlgorithmArg &
5160
GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5161
                                     const char *helpMessage)
5162
0
{
5163
0
    return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5164
0
                  MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5165
0
}
5166
5167
/************************************************************************/
5168
/*                   GDALAlgorithm::AddLayerNameArg()                   */
5169
/************************************************************************/
5170
5171
GDALInConstructionAlgorithmArg &
5172
GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5173
                               const char *helpMessage)
5174
0
{
5175
0
    return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5176
0
                  MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5177
0
}
5178
5179
/************************************************************************/
5180
/*                 GDALAlgorithm::AddGeometryTypeArg()                  */
5181
/************************************************************************/
5182
5183
GDALInConstructionAlgorithmArg &
5184
GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5185
0
{
5186
0
    return AddArg("geometry-type", 0,
5187
0
                  MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5188
0
        .SetAutoCompleteFunction(
5189
0
            [](const std::string &currentValue)
5190
0
            {
5191
0
                std::vector<std::string> oRet;
5192
0
                for (const char *type :
5193
0
                     {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5194
0
                      "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5195
0
                      "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5196
0
                      "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5197
0
                      "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5198
0
                {
5199
0
                    if (currentValue.empty() ||
5200
0
                        STARTS_WITH(type, currentValue.c_str()))
5201
0
                    {
5202
0
                        oRet.push_back(type);
5203
0
                        oRet.push_back(std::string(type).append("Z"));
5204
0
                        oRet.push_back(std::string(type).append("M"));
5205
0
                        oRet.push_back(std::string(type).append("ZM"));
5206
0
                    }
5207
0
                }
5208
0
                return oRet;
5209
0
            })
5210
0
        .AddValidationAction(
5211
0
            [this, pValue]()
5212
0
            {
5213
0
                if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5214
0
                        wkbUnknown &&
5215
0
                    !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5216
0
                {
5217
0
                    ReportError(CE_Failure, CPLE_AppDefined,
5218
0
                                "Invalid geometry type '%s'", pValue->c_str());
5219
0
                    return false;
5220
0
                }
5221
0
                return true;
5222
0
            });
5223
0
}
5224
5225
/************************************************************************/
5226
/*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
5227
/************************************************************************/
5228
5229
/* static */
5230
void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5231
    GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5232
0
{
5233
0
    CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5234
0
              datasetArg.GetType() == GAAT_DATASET_LIST);
5235
5236
0
    layerArg.SetAutoCompleteFunction(
5237
0
        [&datasetArg](const std::string &currentValue)
5238
0
        {
5239
0
            std::vector<std::string> ret;
5240
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5241
0
            GDALArgDatasetValue *dsVal = nullptr;
5242
0
            if (datasetArg.GetType() == GAAT_DATASET)
5243
0
            {
5244
0
                dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5245
0
            }
5246
0
            else
5247
0
            {
5248
0
                auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5249
0
                if (val.size() == 1)
5250
0
                {
5251
0
                    dsVal = &val[0];
5252
0
                }
5253
0
            }
5254
0
            if (dsVal && !dsVal->GetName().empty())
5255
0
            {
5256
0
                auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5257
0
                    dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5258
0
                if (poDS)
5259
0
                {
5260
0
                    for (auto &&poLayer : poDS->GetLayers())
5261
0
                    {
5262
0
                        if (currentValue == poLayer->GetDescription())
5263
0
                        {
5264
0
                            ret.clear();
5265
0
                            ret.push_back(poLayer->GetDescription());
5266
0
                            break;
5267
0
                        }
5268
0
                        ret.push_back(poLayer->GetDescription());
5269
0
                    }
5270
0
                }
5271
0
            }
5272
0
            return ret;
5273
0
        });
5274
0
}
5275
5276
/************************************************************************/
5277
/*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
5278
/************************************************************************/
5279
5280
void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5281
    GDALInConstructionAlgorithmArg &fieldArg,
5282
    const GDALAlgorithmArg *layerNameArg, bool attributeFields,
5283
    bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg,
5284
    const std::vector<std::string> &extraValues,
5285
    std::function<bool(const OGRFieldDefn *)> filterFn)
5286
0
{
5287
5288
0
    fieldArg.SetAutoCompleteFunction(
5289
0
        [&datasetArg, layerNameArg, attributeFields, geometryFields,
5290
0
         extraValues,
5291
0
         filterFn = std::move(filterFn)](const std::string &currentValue)
5292
0
        {
5293
0
            std::set<std::string> ret{};
5294
0
            if (!datasetArg.empty())
5295
0
            {
5296
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5297
5298
0
                const auto getLayerFields =
5299
0
                    [&ret, &currentValue, attributeFields, geometryFields,
5300
0
                     &extraValues, &filterFn](const OGRLayer *poLayer)
5301
0
                {
5302
0
                    const auto poDefn = poLayer->GetLayerDefn();
5303
0
                    if (attributeFields)
5304
0
                    {
5305
0
                        for (const auto poFieldDefn : poDefn->GetFields())
5306
0
                        {
5307
0
                            if (filterFn && !filterFn(poFieldDefn))
5308
0
                            {
5309
0
                                continue;
5310
0
                            }
5311
5312
0
                            const char *fieldName = poFieldDefn->GetNameRef();
5313
5314
0
                            if (currentValue == fieldName)
5315
0
                            {
5316
0
                                ret.clear();
5317
0
                                ret.insert(fieldName);
5318
0
                                break;
5319
0
                            }
5320
0
                            ret.insert(fieldName);
5321
0
                        }
5322
0
                    }
5323
0
                    if (geometryFields)
5324
0
                    {
5325
0
                        for (const auto poFieldDefn : poDefn->GetGeomFields())
5326
0
                        {
5327
0
                            const char *fieldName = poFieldDefn->GetNameRef();
5328
0
                            if (fieldName[0] == 0)
5329
0
                                fieldName = OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME;
5330
0
                            if (currentValue == fieldName)
5331
0
                            {
5332
0
                                ret.clear();
5333
0
                                ret.insert(fieldName);
5334
0
                                break;
5335
0
                            }
5336
0
                            ret.insert(fieldName);
5337
0
                        }
5338
0
                    }
5339
0
                    for (const auto &value : extraValues)
5340
0
                    {
5341
0
                        if (currentValue == value)
5342
0
                        {
5343
0
                            ret.clear();
5344
0
                            ret.insert(value);
5345
0
                            break;
5346
0
                        }
5347
0
                        ret.insert(value);
5348
0
                    }
5349
0
                };
5350
5351
0
                const GDALArgDatasetValue &dsVal = datasetArg[0];
5352
5353
0
                if (!dsVal.GetName().empty())
5354
0
                {
5355
0
                    auto poDS = std::unique_ptr<GDALDataset>(
5356
0
                        GDALDataset::Open(dsVal.GetName().c_str(),
5357
0
                                          GDAL_OF_VECTOR | GDAL_OF_READONLY));
5358
0
                    if (poDS)
5359
0
                    {
5360
0
                        std::vector<std::string> layerNames;
5361
0
                        if (layerNameArg && layerNameArg->IsExplicitlySet())
5362
0
                        {
5363
0
                            if (layerNameArg->GetType() == GAAT_STRING_LIST)
5364
0
                            {
5365
0
                                layerNames =
5366
0
                                    layerNameArg
5367
0
                                        ->Get<std::vector<std::string>>();
5368
0
                            }
5369
0
                            else if (layerNameArg->GetType() == GAAT_STRING)
5370
0
                            {
5371
0
                                layerNames.push_back(
5372
0
                                    layerNameArg->Get<std::string>());
5373
0
                            }
5374
0
                        }
5375
0
                        if (layerNames.empty())
5376
0
                        {
5377
                            // Loop through all layers
5378
0
                            for (const auto *poLayer : poDS->GetLayers())
5379
0
                            {
5380
0
                                getLayerFields(poLayer);
5381
0
                            }
5382
0
                        }
5383
0
                        else
5384
0
                        {
5385
0
                            for (const std::string &layerName : layerNames)
5386
0
                            {
5387
0
                                const auto poLayer =
5388
0
                                    poDS->GetLayerByName(layerName.c_str());
5389
0
                                if (poLayer)
5390
0
                                {
5391
0
                                    getLayerFields(poLayer);
5392
0
                                }
5393
0
                            }
5394
0
                        }
5395
0
                    }
5396
0
                }
5397
0
            }
5398
0
            std::vector<std::string> retVector(ret.begin(), ret.end());
5399
0
            return retVector;
5400
0
        });
5401
0
}
5402
5403
/************************************************************************/
5404
/*                   GDALAlgorithm::AddFieldNameArg()                   */
5405
/************************************************************************/
5406
5407
GDALInConstructionAlgorithmArg &
5408
GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5409
0
{
5410
0
    return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5411
0
                  pValue);
5412
0
}
5413
5414
/************************************************************************/
5415
/*                GDALAlgorithm::ParseFieldDefinition()                 */
5416
/************************************************************************/
5417
bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5418
                                         OGRFieldDefn *poFieldDefn,
5419
                                         std::string *posError)
5420
0
{
5421
0
    static const std::regex re(
5422
0
        R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5423
0
    std::smatch match;
5424
0
    if (std::regex_match(posStrDef, match, re))
5425
0
    {
5426
0
        const std::string name = match[1];
5427
0
        const std::string type = match[2];
5428
0
        const int width = match[3].matched ? std::stoi(match[3]) : 0;
5429
0
        const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5430
0
        poFieldDefn->SetName(name.c_str());
5431
5432
0
        const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5433
0
        if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5434
0
        {
5435
0
            if (posError)
5436
0
                *posError = "Unsupported field type: " + type;
5437
5438
0
            return false;
5439
0
        }
5440
0
        poFieldDefn->SetType(typeEnum);
5441
0
        poFieldDefn->SetWidth(width);
5442
0
        poFieldDefn->SetPrecision(precision);
5443
0
        return true;
5444
0
    }
5445
5446
0
    if (posError)
5447
0
        *posError = "Invalid field definition format. Expected "
5448
0
                    "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5449
5450
0
    return false;
5451
0
}
5452
5453
/************************************************************************/
5454
/*                GDALAlgorithm::AddFieldDefinitionArg()                */
5455
/************************************************************************/
5456
5457
GDALInConstructionAlgorithmArg &
5458
GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5459
                                     std::vector<OGRFieldDefn> *pFieldDefns,
5460
                                     const char *helpMessage)
5461
0
{
5462
0
    auto &arg =
5463
0
        AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5464
0
               pValues)
5465
0
            .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5466
0
            .SetPackedValuesAllowed(true)
5467
0
            .SetRepeatedArgAllowed(true);
5468
5469
0
    auto validationFunction = [this, pFieldDefns, pValues]()
5470
0
    {
5471
0
        pFieldDefns->clear();
5472
0
        for (const auto &strValue : *pValues)
5473
0
        {
5474
0
            OGRFieldDefn fieldDefn("", OFTString);
5475
0
            std::string error;
5476
0
            if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5477
0
                                                     &error))
5478
0
            {
5479
0
                ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5480
0
                return false;
5481
0
            }
5482
            // Check uniqueness of field names
5483
0
            for (const auto &existingFieldDefn : *pFieldDefns)
5484
0
            {
5485
0
                if (EQUAL(existingFieldDefn.GetNameRef(),
5486
0
                          fieldDefn.GetNameRef()))
5487
0
                {
5488
0
                    ReportError(CE_Failure, CPLE_AppDefined,
5489
0
                                "Duplicate field name: '%s'",
5490
0
                                fieldDefn.GetNameRef());
5491
0
                    return false;
5492
0
                }
5493
0
            }
5494
0
            pFieldDefns->push_back(fieldDefn);
5495
0
        }
5496
0
        return true;
5497
0
    };
5498
5499
0
    arg.AddValidationAction(std::move(validationFunction));
5500
5501
0
    return arg;
5502
0
}
5503
5504
/************************************************************************/
5505
/*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
5506
/************************************************************************/
5507
5508
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5509
    OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5510
    std::string *pStrValue, const std::string &argName, const char *helpMessage)
5511
0
{
5512
0
    auto &arg =
5513
0
        AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5514
0
               MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5515
0
            .SetAutoCompleteFunction(
5516
0
                [](const std::string &currentValue)
5517
0
                {
5518
0
                    std::vector<std::string> oRet;
5519
0
                    for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5520
0
                    {
5521
0
                        const char *pszSubType =
5522
0
                            OGRFieldDefn::GetFieldSubTypeName(
5523
0
                                static_cast<OGRFieldSubType>(i));
5524
0
                        if (pszSubType != nullptr)
5525
0
                        {
5526
0
                            if (currentValue.empty() ||
5527
0
                                STARTS_WITH(pszSubType, currentValue.c_str()))
5528
0
                            {
5529
0
                                oRet.push_back(pszSubType);
5530
0
                            }
5531
0
                        }
5532
0
                    }
5533
5534
0
                    for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5535
0
                    {
5536
                        // Skip deprecated
5537
0
                        if (static_cast<OGRFieldType>(i) ==
5538
0
                                OGRFieldType::OFTWideString ||
5539
0
                            static_cast<OGRFieldType>(i) ==
5540
0
                                OGRFieldType::OFTWideStringList)
5541
0
                            continue;
5542
0
                        const char *pszType = OGRFieldDefn::GetFieldTypeName(
5543
0
                            static_cast<OGRFieldType>(i));
5544
0
                        if (pszType != nullptr)
5545
0
                        {
5546
0
                            if (currentValue.empty() ||
5547
0
                                STARTS_WITH(pszType, currentValue.c_str()))
5548
0
                            {
5549
0
                                oRet.push_back(pszType);
5550
0
                            }
5551
0
                        }
5552
0
                    }
5553
0
                    return oRet;
5554
0
                });
5555
5556
0
    auto validationFunction =
5557
0
        [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5558
0
    {
5559
0
        bool isValid{true};
5560
0
        *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5561
5562
        // String is returned for unknown types
5563
0
        if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5564
0
        {
5565
0
            isValid = false;
5566
0
        }
5567
5568
0
        *pSubtypeValue =
5569
0
            OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5570
5571
0
        if (*pSubtypeValue != OFSTNone)
5572
0
        {
5573
0
            isValid = true;
5574
0
            switch (*pSubtypeValue)
5575
0
            {
5576
0
                case OFSTBoolean:
5577
0
                case OFSTInt16:
5578
0
                {
5579
0
                    *pTypeValue = OFTInteger;
5580
0
                    break;
5581
0
                }
5582
0
                case OFSTFloat32:
5583
0
                {
5584
0
                    *pTypeValue = OFTReal;
5585
0
                    break;
5586
0
                }
5587
0
                default:
5588
0
                {
5589
0
                    *pTypeValue = OFTString;
5590
0
                    break;
5591
0
                }
5592
0
            }
5593
0
        }
5594
5595
0
        if (!isValid)
5596
0
        {
5597
0
            ReportError(CE_Failure, CPLE_AppDefined,
5598
0
                        "Invalid value for argument '%s': '%s'",
5599
0
                        arg.GetName().c_str(), pStrValue->c_str());
5600
0
        }
5601
5602
0
        return isValid;
5603
0
    };
5604
5605
0
    if (!pStrValue->empty())
5606
0
    {
5607
0
        arg.SetDefault(*pStrValue);
5608
0
        validationFunction();
5609
0
    }
5610
5611
0
    arg.AddValidationAction(std::move(validationFunction));
5612
5613
0
    return arg;
5614
0
}
5615
5616
/************************************************************************/
5617
/*                   GDALAlgorithm::ValidateBandArg()                   */
5618
/************************************************************************/
5619
5620
bool GDALAlgorithm::ValidateBandArg() const
5621
0
{
5622
0
    bool ret = true;
5623
0
    const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5624
0
    const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5625
0
    if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5626
0
        (inputDatasetArg->GetType() == GAAT_DATASET ||
5627
0
         inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5628
0
        (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5629
0
    {
5630
0
        const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5631
0
        {
5632
0
            if (nBand > poDS->GetRasterCount())
5633
0
            {
5634
0
                ReportError(CE_Failure, CPLE_AppDefined,
5635
0
                            "Value of 'band' should be greater or equal than "
5636
0
                            "1 and less or equal than %d.",
5637
0
                            poDS->GetRasterCount());
5638
0
                return false;
5639
0
            }
5640
0
            return true;
5641
0
        };
5642
5643
0
        const auto ValidateForOneDataset =
5644
0
            [&bandArg, &CheckBand](const GDALDataset *poDS)
5645
0
        {
5646
0
            bool l_ret = true;
5647
0
            if (bandArg->GetType() == GAAT_INTEGER)
5648
0
            {
5649
0
                l_ret = CheckBand(poDS, bandArg->Get<int>());
5650
0
            }
5651
0
            else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5652
0
            {
5653
0
                for (int nBand : bandArg->Get<std::vector<int>>())
5654
0
                {
5655
0
                    l_ret = l_ret && CheckBand(poDS, nBand);
5656
0
                }
5657
0
            }
5658
0
            return l_ret;
5659
0
        };
5660
5661
0
        if (inputDatasetArg->GetType() == GAAT_DATASET)
5662
0
        {
5663
0
            auto poDS =
5664
0
                inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5665
0
            if (poDS && !ValidateForOneDataset(poDS))
5666
0
                ret = false;
5667
0
        }
5668
0
        else
5669
0
        {
5670
0
            CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5671
0
            for (auto &datasetValue :
5672
0
                 inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5673
0
            {
5674
0
                auto poDS = datasetValue.GetDatasetRef();
5675
0
                if (poDS && !ValidateForOneDataset(poDS))
5676
0
                    ret = false;
5677
0
            }
5678
0
        }
5679
0
    }
5680
0
    return ret;
5681
0
}
5682
5683
/************************************************************************/
5684
/*            GDALAlgorithm::RunPreStepPipelineValidations()            */
5685
/************************************************************************/
5686
5687
bool GDALAlgorithm::RunPreStepPipelineValidations() const
5688
0
{
5689
0
    return ValidateBandArg();
5690
0
}
5691
5692
/************************************************************************/
5693
/*                     GDALAlgorithm::AddBandArg()                      */
5694
/************************************************************************/
5695
5696
GDALInConstructionAlgorithmArg &
5697
GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5698
0
{
5699
0
    AddValidationAction([this]() { return ValidateBandArg(); });
5700
5701
0
    return AddArg(GDAL_ARG_NAME_BAND, 'b',
5702
0
                  MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5703
0
                  pValue)
5704
0
        .AddValidationAction(
5705
0
            [pValue]()
5706
0
            {
5707
0
                if (*pValue <= 0)
5708
0
                {
5709
0
                    CPLError(CE_Failure, CPLE_AppDefined,
5710
0
                             "Value of 'band' should greater or equal to 1.");
5711
0
                    return false;
5712
0
                }
5713
0
                return true;
5714
0
            });
5715
0
}
5716
5717
/************************************************************************/
5718
/*                     GDALAlgorithm::AddBandArg()                      */
5719
/************************************************************************/
5720
5721
GDALInConstructionAlgorithmArg &
5722
GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5723
0
{
5724
0
    AddValidationAction([this]() { return ValidateBandArg(); });
5725
5726
0
    return AddArg(GDAL_ARG_NAME_BAND, 'b',
5727
0
                  MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5728
0
                  pValue)
5729
0
        .AddValidationAction(
5730
0
            [pValue]()
5731
0
            {
5732
0
                for (int val : *pValue)
5733
0
                {
5734
0
                    if (val <= 0)
5735
0
                    {
5736
0
                        CPLError(CE_Failure, CPLE_AppDefined,
5737
0
                                 "Value of 'band' should greater or equal "
5738
0
                                 "to 1.");
5739
0
                        return false;
5740
0
                    }
5741
0
                }
5742
0
                return true;
5743
0
            });
5744
0
}
5745
5746
/************************************************************************/
5747
/*                      ParseAndValidateKeyValue()                      */
5748
/************************************************************************/
5749
5750
bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5751
0
{
5752
0
    const auto Validate = [this, &arg](const std::string &val)
5753
0
    {
5754
0
        if (val.find('=') == std::string::npos)
5755
0
        {
5756
0
            ReportError(
5757
0
                CE_Failure, CPLE_AppDefined,
5758
0
                "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5759
0
                arg.GetName().c_str());
5760
0
            return false;
5761
0
        }
5762
5763
0
        return true;
5764
0
    };
5765
5766
0
    if (arg.GetType() == GAAT_STRING)
5767
0
    {
5768
0
        return Validate(arg.Get<std::string>());
5769
0
    }
5770
0
    else if (arg.GetType() == GAAT_STRING_LIST)
5771
0
    {
5772
0
        std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5773
0
        if (vals.size() == 1)
5774
0
        {
5775
            // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5776
0
            std::vector<std::string> newVals;
5777
0
            std::string curToken;
5778
0
            bool canSplitOnComma = true;
5779
0
            char lastSep = 0;
5780
0
            bool inString = false;
5781
0
            bool equalFoundInLastToken = false;
5782
0
            for (char c : vals[0])
5783
0
            {
5784
0
                if (!inString && c == ',')
5785
0
                {
5786
0
                    if (lastSep != '=' || !equalFoundInLastToken)
5787
0
                    {
5788
0
                        canSplitOnComma = false;
5789
0
                        break;
5790
0
                    }
5791
0
                    lastSep = c;
5792
0
                    newVals.push_back(curToken);
5793
0
                    curToken.clear();
5794
0
                    equalFoundInLastToken = false;
5795
0
                }
5796
0
                else if (!inString && c == '=')
5797
0
                {
5798
0
                    if (lastSep == '=')
5799
0
                    {
5800
0
                        canSplitOnComma = false;
5801
0
                        break;
5802
0
                    }
5803
0
                    equalFoundInLastToken = true;
5804
0
                    lastSep = c;
5805
0
                    curToken += c;
5806
0
                }
5807
0
                else if (c == '"')
5808
0
                {
5809
0
                    inString = !inString;
5810
0
                    curToken += c;
5811
0
                }
5812
0
                else
5813
0
                {
5814
0
                    curToken += c;
5815
0
                }
5816
0
            }
5817
0
            if (canSplitOnComma && !inString && equalFoundInLastToken)
5818
0
            {
5819
0
                if (!curToken.empty())
5820
0
                    newVals.emplace_back(std::move(curToken));
5821
0
                vals = std::move(newVals);
5822
0
            }
5823
0
        }
5824
5825
0
        for (const auto &val : vals)
5826
0
        {
5827
0
            if (!Validate(val))
5828
0
                return false;
5829
0
        }
5830
0
    }
5831
5832
0
    return true;
5833
0
}
5834
5835
/************************************************************************/
5836
/*                           IsGDALGOutput()                            */
5837
/************************************************************************/
5838
5839
bool GDALAlgorithm::IsGDALGOutput() const
5840
0
{
5841
0
    bool isGDALGOutput = false;
5842
0
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5843
0
    const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5844
0
    if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5845
0
        outputArg->IsExplicitlySet())
5846
0
    {
5847
0
        if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5848
0
            outputFormatArg->IsExplicitlySet())
5849
0
        {
5850
0
            const auto &val =
5851
0
                outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5852
0
            isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5853
0
        }
5854
0
        else
5855
0
        {
5856
0
            const auto &filename =
5857
0
                outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5858
0
            isGDALGOutput =
5859
0
                filename.GetName().size() > strlen(".gdalg.json") &&
5860
0
                EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5861
0
                          strlen(".gdalg.json"),
5862
0
                      ".gdalg.json");
5863
0
        }
5864
0
    }
5865
0
    return isGDALGOutput;
5866
0
}
5867
5868
/************************************************************************/
5869
/*                         ProcessGDALGOutput()                         */
5870
/************************************************************************/
5871
5872
GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5873
0
{
5874
0
    if (!SupportsStreamedOutput())
5875
0
        return ProcessGDALGOutputRet::NOT_GDALG;
5876
5877
0
    if (IsGDALGOutput())
5878
0
    {
5879
0
        const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5880
0
        const auto &filename =
5881
0
            outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5882
0
        VSIStatBufL sStat;
5883
0
        if (VSIStatL(filename.c_str(), &sStat) == 0)
5884
0
        {
5885
0
            const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5886
0
            if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5887
0
            {
5888
0
                if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5889
0
                {
5890
0
                    CPLError(CE_Failure, CPLE_AppDefined,
5891
0
                             "File '%s' already exists. Specify the "
5892
0
                             "--overwrite option to overwrite it.",
5893
0
                             filename.c_str());
5894
0
                    return ProcessGDALGOutputRet::GDALG_ERROR;
5895
0
                }
5896
0
            }
5897
0
        }
5898
5899
0
        std::string osCommandLine;
5900
5901
0
        for (const auto &path : GDALAlgorithm::m_callPath)
5902
0
        {
5903
0
            if (!osCommandLine.empty())
5904
0
                osCommandLine += ' ';
5905
0
            osCommandLine += path;
5906
0
        }
5907
5908
0
        for (const auto &arg : GetArgs())
5909
0
        {
5910
0
            if (arg->IsExplicitlySet() &&
5911
0
                arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5912
0
                arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5913
0
                arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5914
0
                arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5915
0
            {
5916
0
                osCommandLine += ' ';
5917
0
                std::string strArg;
5918
0
                if (!arg->Serialize(strArg))
5919
0
                {
5920
0
                    CPLError(CE_Failure, CPLE_AppDefined,
5921
0
                             "Cannot serialize argument %s",
5922
0
                             arg->GetName().c_str());
5923
0
                    return ProcessGDALGOutputRet::GDALG_ERROR;
5924
0
                }
5925
0
                osCommandLine += strArg;
5926
0
            }
5927
0
        }
5928
5929
0
        osCommandLine += " --output-format stream --output streamed_dataset";
5930
5931
0
        std::string outStringUnused;
5932
0
        return SaveGDALG(filename, outStringUnused, osCommandLine)
5933
0
                   ? ProcessGDALGOutputRet::GDALG_OK
5934
0
                   : ProcessGDALGOutputRet::GDALG_ERROR;
5935
0
    }
5936
5937
0
    return ProcessGDALGOutputRet::NOT_GDALG;
5938
0
}
5939
5940
/************************************************************************/
5941
/*                      GDALAlgorithm::SaveGDALG()                      */
5942
/************************************************************************/
5943
5944
/* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5945
                                           std::string &outString,
5946
                                           const std::string &commandLine)
5947
0
{
5948
0
    CPLJSONDocument oDoc;
5949
0
    oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5950
0
    oDoc.GetRoot().Add("command_line", commandLine);
5951
0
    oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5952
5953
0
    if (!filename.empty())
5954
0
        return oDoc.Save(filename);
5955
5956
0
    outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5957
0
    return true;
5958
0
}
5959
5960
/************************************************************************/
5961
/*                GDALAlgorithm::AddCreationOptionsArg()                */
5962
/************************************************************************/
5963
5964
GDALInConstructionAlgorithmArg &
5965
GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5966
                                     const char *helpMessage)
5967
0
{
5968
0
    auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5969
0
                       MsgOrDefault(helpMessage, _("Creation option")), pValue)
5970
0
                    .AddAlias("co")
5971
0
                    .SetMetaVar("<KEY>=<VALUE>")
5972
0
                    .SetPackedValuesAllowed(false);
5973
0
    arg.AddValidationAction([this, &arg]()
5974
0
                            { return ParseAndValidateKeyValue(arg); });
5975
5976
0
    arg.SetAutoCompleteFunction(
5977
0
        [this](const std::string &currentValue)
5978
0
        {
5979
0
            std::vector<std::string> oRet;
5980
5981
0
            int datasetType =
5982
0
                GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5983
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5984
0
            if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5985
0
                              outputArg->GetType() == GAAT_DATASET_LIST))
5986
0
            {
5987
0
                datasetType = outputArg->GetDatasetType();
5988
0
            }
5989
5990
0
            const char *pszMDCreationOptionList =
5991
0
                (datasetType == GDAL_OF_MULTIDIM_RASTER)
5992
0
                    ? GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST
5993
0
                    : GDAL_DMD_CREATIONOPTIONLIST;
5994
5995
0
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5996
0
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5997
0
                outputFormat->IsExplicitlySet())
5998
0
            {
5999
0
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
6000
0
                    outputFormat->Get<std::string>().c_str());
6001
0
                if (poDriver)
6002
0
                {
6003
0
                    AddOptionsSuggestions(
6004
0
                        poDriver->GetMetadataItem(pszMDCreationOptionList),
6005
0
                        datasetType, currentValue, oRet);
6006
0
                }
6007
0
                return oRet;
6008
0
            }
6009
6010
0
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
6011
0
            {
6012
0
                auto poDM = GetGDALDriverManager();
6013
0
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6014
0
                const auto &osDSName = datasetValue.GetName();
6015
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6016
0
                if (!osExt.empty())
6017
0
                {
6018
0
                    std::set<std::string> oVisitedExtensions;
6019
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
6020
0
                    {
6021
0
                        auto poDriver = poDM->GetDriver(i);
6022
0
                        if (((datasetType & GDAL_OF_RASTER) != 0 &&
6023
0
                             poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
6024
0
                            ((datasetType & GDAL_OF_VECTOR) != 0 &&
6025
0
                             poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
6026
0
                            ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
6027
0
                             poDriver->GetMetadataItem(
6028
0
                                 GDAL_DCAP_MULTIDIM_RASTER)))
6029
0
                        {
6030
0
                            const char *pszExtensions =
6031
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6032
0
                            if (pszExtensions)
6033
0
                            {
6034
0
                                const CPLStringList aosExts(
6035
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
6036
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
6037
0
                                {
6038
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
6039
0
                                        !cpl::contains(oVisitedExtensions,
6040
0
                                                       pszExt))
6041
0
                                    {
6042
0
                                        oVisitedExtensions.insert(pszExt);
6043
0
                                        if (AddOptionsSuggestions(
6044
0
                                                poDriver->GetMetadataItem(
6045
0
                                                    pszMDCreationOptionList),
6046
0
                                                datasetType, currentValue,
6047
0
                                                oRet))
6048
0
                                        {
6049
0
                                            return oRet;
6050
0
                                        }
6051
0
                                        break;
6052
0
                                    }
6053
0
                                }
6054
0
                            }
6055
0
                        }
6056
0
                    }
6057
0
                }
6058
0
            }
6059
6060
0
            return oRet;
6061
0
        });
6062
6063
0
    return arg;
6064
0
}
6065
6066
/************************************************************************/
6067
/*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
6068
/************************************************************************/
6069
6070
GDALInConstructionAlgorithmArg &
6071
GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
6072
                                          const char *helpMessage)
6073
0
{
6074
0
    auto &arg =
6075
0
        AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
6076
0
               MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
6077
0
            .AddAlias("lco")
6078
0
            .SetMetaVar("<KEY>=<VALUE>")
6079
0
            .SetPackedValuesAllowed(false);
6080
0
    arg.AddValidationAction([this, &arg]()
6081
0
                            { return ParseAndValidateKeyValue(arg); });
6082
6083
0
    arg.SetAutoCompleteFunction(
6084
0
        [this](const std::string &currentValue)
6085
0
        {
6086
0
            std::vector<std::string> oRet;
6087
6088
0
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6089
0
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
6090
0
                outputFormat->IsExplicitlySet())
6091
0
            {
6092
0
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
6093
0
                    outputFormat->Get<std::string>().c_str());
6094
0
                if (poDriver)
6095
0
                {
6096
0
                    AddOptionsSuggestions(poDriver->GetMetadataItem(
6097
0
                                              GDAL_DS_LAYER_CREATIONOPTIONLIST),
6098
0
                                          GDAL_OF_VECTOR, currentValue, oRet);
6099
0
                }
6100
0
                return oRet;
6101
0
            }
6102
6103
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
6104
0
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
6105
0
            {
6106
0
                auto poDM = GetGDALDriverManager();
6107
0
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6108
0
                const auto &osDSName = datasetValue.GetName();
6109
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6110
0
                if (!osExt.empty())
6111
0
                {
6112
0
                    std::set<std::string> oVisitedExtensions;
6113
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
6114
0
                    {
6115
0
                        auto poDriver = poDM->GetDriver(i);
6116
0
                        if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
6117
0
                        {
6118
0
                            const char *pszExtensions =
6119
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6120
0
                            if (pszExtensions)
6121
0
                            {
6122
0
                                const CPLStringList aosExts(
6123
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
6124
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
6125
0
                                {
6126
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
6127
0
                                        !cpl::contains(oVisitedExtensions,
6128
0
                                                       pszExt))
6129
0
                                    {
6130
0
                                        oVisitedExtensions.insert(pszExt);
6131
0
                                        if (AddOptionsSuggestions(
6132
0
                                                poDriver->GetMetadataItem(
6133
0
                                                    GDAL_DS_LAYER_CREATIONOPTIONLIST),
6134
0
                                                GDAL_OF_VECTOR, currentValue,
6135
0
                                                oRet))
6136
0
                                        {
6137
0
                                            return oRet;
6138
0
                                        }
6139
0
                                        break;
6140
0
                                    }
6141
0
                                }
6142
0
                            }
6143
0
                        }
6144
0
                    }
6145
0
                }
6146
0
            }
6147
6148
0
            return oRet;
6149
0
        });
6150
6151
0
    return arg;
6152
0
}
6153
6154
/************************************************************************/
6155
/*                     GDALAlgorithm::AddBBOXArg()                      */
6156
/************************************************************************/
6157
6158
/** Add bbox=xmin,ymin,xmax,ymax argument. */
6159
GDALInConstructionAlgorithmArg &
6160
GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
6161
0
{
6162
0
    auto &arg = AddArg("bbox", 0,
6163
0
                       MsgOrDefault(helpMessage,
6164
0
                                    _("Bounding box as xmin,ymin,xmax,ymax")),
6165
0
                       pValue)
6166
0
                    .SetRepeatedArgAllowed(false)
6167
0
                    .SetMinCount(4)
6168
0
                    .SetMaxCount(4)
6169
0
                    .SetDisplayHintAboutRepetition(false);
6170
0
    arg.AddValidationAction(
6171
0
        [&arg]()
6172
0
        {
6173
0
            const auto &val = arg.Get<std::vector<double>>();
6174
0
            CPLAssert(val.size() == 4);
6175
0
            if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
6176
0
            {
6177
0
                CPLError(CE_Failure, CPLE_AppDefined,
6178
0
                         "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
6179
0
                         "xmin <= xmax and ymin <= ymax");
6180
0
                return false;
6181
0
            }
6182
0
            return true;
6183
0
        });
6184
0
    return arg;
6185
0
}
6186
6187
/************************************************************************/
6188
/*                  GDALAlgorithm::AddActiveLayerArg()                  */
6189
/************************************************************************/
6190
6191
GDALInConstructionAlgorithmArg &
6192
GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6193
0
{
6194
0
    return AddArg("active-layer", 0,
6195
0
                  MsgOrDefault(helpMessage,
6196
0
                               _("Set active layer (if not specified, all)")),
6197
0
                  pValue);
6198
0
}
6199
6200
/************************************************************************/
6201
/*                  GDALAlgorithm::AddNumThreadsArg()                   */
6202
/************************************************************************/
6203
6204
GDALInConstructionAlgorithmArg &
6205
GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6206
                                const char *helpMessage)
6207
0
{
6208
0
    auto &arg =
6209
0
        AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6210
0
               MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6211
0
               pStrValue);
6212
6213
0
    AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6214
0
           _("Number of jobs (read-only, hidden argument)"), pValue)
6215
0
        .SetHidden();
6216
6217
0
    auto lambda = [this, &arg, pValue, pStrValue]
6218
0
    {
6219
0
        bool bOK = false;
6220
0
        const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6221
0
        const int nLimit = std::clamp(
6222
0
            pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6223
0
            CPLGetNumCPUs());
6224
0
        const int nNumThreads =
6225
0
            GDALGetNumThreads(pStrValue->c_str(), nLimit,
6226
0
                              /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6227
0
        if (bOK)
6228
0
        {
6229
0
            *pValue = nNumThreads;
6230
0
        }
6231
0
        else
6232
0
        {
6233
0
            ReportError(CE_Failure, CPLE_IllegalArg,
6234
0
                        "Invalid value for '%s' argument",
6235
0
                        arg.GetName().c_str());
6236
0
        }
6237
0
        return bOK;
6238
0
    };
6239
0
    if (!pStrValue->empty())
6240
0
    {
6241
0
        arg.SetDefault(*pStrValue);
6242
0
        lambda();
6243
0
    }
6244
0
    arg.AddValidationAction(std::move(lambda));
6245
0
    return arg;
6246
0
}
6247
6248
/************************************************************************/
6249
/*                 GDALAlgorithm::AddAbsolutePathArg()                  */
6250
/************************************************************************/
6251
6252
GDALInConstructionAlgorithmArg &
6253
GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6254
0
{
6255
0
    return AddArg(
6256
0
        "absolute-path", 0,
6257
0
        MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6258
0
                                    "should be stored as an absolute path")),
6259
0
        pValue);
6260
0
}
6261
6262
/************************************************************************/
6263
/*               GDALAlgorithm::AddPixelFunctionNameArg()               */
6264
/************************************************************************/
6265
6266
GDALInConstructionAlgorithmArg &
6267
GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6268
                                       const char *helpMessage)
6269
0
{
6270
6271
0
    const auto pixelFunctionNames =
6272
0
        VRTDerivedRasterBand::GetPixelFunctionNames();
6273
0
    return AddArg(
6274
0
               "pixel-function", 0,
6275
0
               MsgOrDefault(
6276
0
                   helpMessage,
6277
0
                   _("Specify a pixel function to calculate output value from "
6278
0
                     "overlapping inputs")),
6279
0
               pValue)
6280
0
        .SetChoices(pixelFunctionNames);
6281
0
}
6282
6283
/************************************************************************/
6284
/*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
6285
/************************************************************************/
6286
6287
GDALInConstructionAlgorithmArg &
6288
GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6289
                                       const char *helpMessage)
6290
0
{
6291
0
    auto &pixelFunctionArgArg =
6292
0
        AddArg("pixel-function-arg", 0,
6293
0
               MsgOrDefault(
6294
0
                   helpMessage,
6295
0
                   _("Specify argument(s) to pass to the pixel function")),
6296
0
               pValue)
6297
0
            .SetMetaVar("<NAME>=<VALUE>")
6298
0
            .SetRepeatedArgAllowed(true);
6299
0
    pixelFunctionArgArg.AddValidationAction(
6300
0
        [this, &pixelFunctionArgArg]()
6301
0
        { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6302
6303
0
    pixelFunctionArgArg.SetAutoCompleteFunction(
6304
0
        [this](const std::string &currentValue)
6305
0
        {
6306
0
            std::string pixelFunction;
6307
0
            const auto pixelFunctionArg = GetArg("pixel-function");
6308
0
            if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6309
0
            {
6310
0
                pixelFunction = pixelFunctionArg->Get<std::string>();
6311
0
            }
6312
6313
0
            std::vector<std::string> ret;
6314
6315
0
            if (!pixelFunction.empty())
6316
0
            {
6317
0
                const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6318
0
                    pixelFunction.c_str());
6319
0
                if (!pair)
6320
0
                {
6321
0
                    ret.push_back("**");
6322
                    // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6323
0
                    ret.push_back(std::string("\xC2\xA0"
6324
0
                                              "Invalid pixel function name"));
6325
0
                }
6326
0
                else if (pair->second.find("Argument name=") ==
6327
0
                         std::string::npos)
6328
0
                {
6329
0
                    ret.push_back("**");
6330
                    // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6331
0
                    ret.push_back(
6332
0
                        std::string(
6333
0
                            "\xC2\xA0"
6334
0
                            "No pixel function arguments for pixel function '")
6335
0
                            .append(pixelFunction)
6336
0
                            .append("'"));
6337
0
                }
6338
0
                else
6339
0
                {
6340
0
                    AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6341
0
                                          ret);
6342
0
                }
6343
0
            }
6344
6345
0
            return ret;
6346
0
        });
6347
6348
0
    return pixelFunctionArgArg;
6349
0
}
6350
6351
/************************************************************************/
6352
/*                   GDALAlgorithm::AddProgressArg()                    */
6353
/************************************************************************/
6354
6355
void GDALAlgorithm::AddProgressArg(bool hidden)
6356
0
{
6357
0
    auto &arg =
6358
0
        AddArg(GDAL_ARG_NAME_QUIET, 'q',
6359
0
               _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6360
0
            .SetAvailableInPipelineStep(false)
6361
0
            .SetCategory(GAAC_COMMON)
6362
0
            .AddAction([this]() { m_progressBarRequested = false; });
6363
0
    if (hidden)
6364
0
        arg.SetHidden();
6365
6366
0
    AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6367
0
        .SetAvailableInPipelineStep(false)
6368
0
        .SetHidden();
6369
0
}
6370
6371
/************************************************************************/
6372
/*                         GDALAlgorithm::Run()                         */
6373
/************************************************************************/
6374
6375
bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6376
0
{
6377
0
    WarnIfDeprecated();
6378
6379
0
    if (m_selectedSubAlg)
6380
0
    {
6381
0
        if (m_calledFromCommandLine)
6382
0
            m_selectedSubAlg->m_calledFromCommandLine = true;
6383
0
        return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6384
0
    }
6385
6386
0
    if (m_helpRequested || m_helpDocRequested)
6387
0
    {
6388
0
        if (m_calledFromCommandLine)
6389
0
            printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6390
0
        return true;
6391
0
    }
6392
6393
0
    if (m_JSONUsageRequested)
6394
0
    {
6395
0
        if (m_calledFromCommandLine)
6396
0
            printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6397
0
        return true;
6398
0
    }
6399
6400
0
    if (!ValidateArguments())
6401
0
        return false;
6402
6403
0
    if (m_alreadyRun)
6404
0
    {
6405
0
        ReportError(CE_Failure, CPLE_AppDefined,
6406
0
                    "Run() can be called only once per algorithm instance");
6407
0
        return false;
6408
0
    }
6409
0
    m_alreadyRun = true;
6410
6411
0
    switch (ProcessGDALGOutput())
6412
0
    {
6413
0
        case ProcessGDALGOutputRet::GDALG_ERROR:
6414
0
            return false;
6415
6416
0
        case ProcessGDALGOutputRet::GDALG_OK:
6417
0
            return true;
6418
6419
0
        case ProcessGDALGOutputRet::NOT_GDALG:
6420
0
            break;
6421
0
    }
6422
6423
0
    if (m_executionForStreamOutput)
6424
0
    {
6425
0
        if (!CheckSafeForStreamOutput())
6426
0
        {
6427
0
            return false;
6428
0
        }
6429
0
    }
6430
6431
0
    return RunImpl(pfnProgress, pProgressData);
6432
0
}
6433
6434
/************************************************************************/
6435
/*              GDALAlgorithm::CheckSafeForStreamOutput()               */
6436
/************************************************************************/
6437
6438
bool GDALAlgorithm::CheckSafeForStreamOutput()
6439
0
{
6440
0
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6441
0
    if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6442
0
    {
6443
0
        const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6444
0
        if (!EQUAL(val.c_str(), "stream"))
6445
0
        {
6446
            // For security reasons, to avoid that reading a .gdalg.json file
6447
            // writes a file on the file system.
6448
0
            ReportError(
6449
0
                CE_Failure, CPLE_NotSupported,
6450
0
                "in streamed execution, --format stream should be used");
6451
0
            return false;
6452
0
        }
6453
0
    }
6454
0
    return true;
6455
0
}
6456
6457
/************************************************************************/
6458
/*                      GDALAlgorithm::Finalize()                       */
6459
/************************************************************************/
6460
6461
bool GDALAlgorithm::Finalize()
6462
0
{
6463
0
    bool ret = true;
6464
0
    if (m_selectedSubAlg)
6465
0
        ret = m_selectedSubAlg->Finalize();
6466
6467
0
    for (auto &arg : m_args)
6468
0
    {
6469
0
        if (arg->GetType() == GAAT_DATASET)
6470
0
        {
6471
0
            ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6472
0
        }
6473
0
        else if (arg->GetType() == GAAT_DATASET_LIST)
6474
0
        {
6475
0
            for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6476
0
            {
6477
0
                ret = ds.Close() && ret;
6478
0
            }
6479
0
        }
6480
0
    }
6481
0
    return ret;
6482
0
}
6483
6484
/************************************************************************/
6485
/*                  GDALAlgorithm::GetArgNamesForCLI()                  */
6486
/************************************************************************/
6487
6488
std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6489
GDALAlgorithm::GetArgNamesForCLI() const
6490
0
{
6491
0
    std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6492
6493
0
    size_t maxOptLen = 0;
6494
0
    for (const auto &arg : m_args)
6495
0
    {
6496
0
        if (arg->IsHidden() || arg->IsHiddenForCLI())
6497
0
            continue;
6498
0
        std::string opt;
6499
0
        bool addComma = false;
6500
0
        if (!arg->GetShortName().empty())
6501
0
        {
6502
0
            opt += '-';
6503
0
            opt += arg->GetShortName();
6504
0
            addComma = true;
6505
0
        }
6506
0
        for (char alias : arg->GetShortNameAliases())
6507
0
        {
6508
0
            if (addComma)
6509
0
                opt += ", ";
6510
0
            opt += "-";
6511
0
            opt += alias;
6512
0
            addComma = true;
6513
0
        }
6514
0
        for (const std::string &alias : arg->GetAliases())
6515
0
        {
6516
0
            if (addComma)
6517
0
                opt += ", ";
6518
0
            opt += "--";
6519
0
            opt += alias;
6520
0
            addComma = true;
6521
0
        }
6522
0
        if (!arg->GetName().empty())
6523
0
        {
6524
0
            if (addComma)
6525
0
                opt += ", ";
6526
0
            opt += "--";
6527
0
            opt += arg->GetName();
6528
0
        }
6529
0
        const auto &metaVar = arg->GetMetaVar();
6530
0
        if (!metaVar.empty())
6531
0
        {
6532
0
            opt += ' ';
6533
0
            if (metaVar.front() != '<')
6534
0
                opt += '<';
6535
0
            opt += metaVar;
6536
0
            if (metaVar.back() != '>')
6537
0
                opt += '>';
6538
0
        }
6539
0
        maxOptLen = std::max(maxOptLen, opt.size());
6540
0
        options.emplace_back(arg.get(), opt);
6541
0
    }
6542
6543
0
    return std::make_pair(std::move(options), maxOptLen);
6544
0
}
6545
6546
/************************************************************************/
6547
/*                   GDALAlgorithm::GetUsageForCLI()                    */
6548
/************************************************************************/
6549
6550
std::string
6551
GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6552
                              const UsageOptions &usageOptions) const
6553
0
{
6554
0
    if (m_selectedSubAlg)
6555
0
        return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6556
6557
0
    std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6558
0
    std::string osPath;
6559
0
    for (const std::string &s : m_callPath)
6560
0
    {
6561
0
        if (!osPath.empty())
6562
0
            osPath += ' ';
6563
0
        osPath += s;
6564
0
    }
6565
0
    osRet += ' ';
6566
0
    osRet += osPath;
6567
6568
0
    bool hasNonPositionals = false;
6569
0
    for (const auto &arg : m_args)
6570
0
    {
6571
0
        if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6572
0
            hasNonPositionals = true;
6573
0
    }
6574
6575
0
    if (HasSubAlgorithms())
6576
0
    {
6577
0
        if (m_callPath.size() == 1)
6578
0
        {
6579
0
            osRet += " <COMMAND>";
6580
0
            if (hasNonPositionals)
6581
0
                osRet += " [OPTIONS]";
6582
0
            if (usageOptions.isPipelineStep)
6583
0
            {
6584
0
                const size_t nLenFirstLine = osRet.size();
6585
0
                osRet += '\n';
6586
0
                osRet.append(nLenFirstLine, '-');
6587
0
                osRet += '\n';
6588
0
            }
6589
0
            osRet += "\nwhere <COMMAND> is one of:\n";
6590
0
        }
6591
0
        else
6592
0
        {
6593
0
            osRet += " <SUBCOMMAND>";
6594
0
            if (hasNonPositionals)
6595
0
                osRet += " [OPTIONS]";
6596
0
            if (usageOptions.isPipelineStep)
6597
0
            {
6598
0
                const size_t nLenFirstLine = osRet.size();
6599
0
                osRet += '\n';
6600
0
                osRet.append(nLenFirstLine, '-');
6601
0
                osRet += '\n';
6602
0
            }
6603
0
            osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6604
0
        }
6605
0
        size_t maxNameLen = 0;
6606
0
        for (const auto &subAlgName : GetSubAlgorithmNames())
6607
0
        {
6608
0
            maxNameLen = std::max(maxNameLen, subAlgName.size());
6609
0
        }
6610
0
        for (const auto &subAlgName : GetSubAlgorithmNames())
6611
0
        {
6612
0
            auto subAlg = InstantiateSubAlgorithm(subAlgName);
6613
0
            if (subAlg && !subAlg->IsHidden())
6614
0
            {
6615
0
                const std::string &name(subAlg->GetName());
6616
0
                osRet += "  - ";
6617
0
                osRet += name;
6618
0
                osRet += ": ";
6619
0
                osRet.append(maxNameLen - name.size(), ' ');
6620
0
                osRet += subAlg->GetDescription();
6621
0
                if (!subAlg->m_aliases.empty())
6622
0
                {
6623
0
                    bool first = true;
6624
0
                    for (const auto &alias : subAlg->GetAliases())
6625
0
                    {
6626
0
                        if (alias ==
6627
0
                            GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6628
0
                            break;
6629
0
                        if (first)
6630
0
                            osRet += " (alias: ";
6631
0
                        else
6632
0
                            osRet += ", ";
6633
0
                        osRet += alias;
6634
0
                        first = false;
6635
0
                    }
6636
0
                    if (!first)
6637
0
                    {
6638
0
                        osRet += ')';
6639
0
                    }
6640
0
                }
6641
0
                osRet += '\n';
6642
0
            }
6643
0
        }
6644
6645
0
        if (shortUsage && hasNonPositionals)
6646
0
        {
6647
0
            osRet += "\nTry '";
6648
0
            osRet += osPath;
6649
0
            osRet += " --help' for help.\n";
6650
0
        }
6651
0
    }
6652
0
    else
6653
0
    {
6654
0
        if (!m_args.empty())
6655
0
        {
6656
0
            if (hasNonPositionals)
6657
0
                osRet += " [OPTIONS]";
6658
0
            for (const auto *arg : m_positionalArgs)
6659
0
            {
6660
0
                if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6661
0
                    (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6662
0
                {
6663
0
                    const bool optional =
6664
0
                        (!arg->IsRequired() && !(GetName() == "pipeline" &&
6665
0
                                                 arg->GetName() == "pipeline"));
6666
0
                    osRet += ' ';
6667
0
                    if (optional)
6668
0
                        osRet += '[';
6669
0
                    const std::string &metavar = arg->GetMetaVar();
6670
0
                    if (!metavar.empty() && metavar[0] == '<')
6671
0
                    {
6672
0
                        osRet += metavar;
6673
0
                    }
6674
0
                    else
6675
0
                    {
6676
0
                        osRet += '<';
6677
0
                        osRet += metavar;
6678
0
                        osRet += '>';
6679
0
                    }
6680
0
                    if (arg->GetType() == GAAT_DATASET_LIST &&
6681
0
                        arg->GetMaxCount() > 1)
6682
0
                    {
6683
0
                        osRet += "...";
6684
0
                    }
6685
0
                    if (optional)
6686
0
                        osRet += ']';
6687
0
                }
6688
0
            }
6689
0
        }
6690
6691
0
        const size_t nLenFirstLine = osRet.size();
6692
0
        osRet += '\n';
6693
0
        if (usageOptions.isPipelineStep)
6694
0
        {
6695
0
            osRet.append(nLenFirstLine, '-');
6696
0
            osRet += '\n';
6697
0
        }
6698
6699
0
        if (shortUsage)
6700
0
        {
6701
0
            osRet += "Try '";
6702
0
            osRet += osPath;
6703
0
            osRet += " --help' for help.\n";
6704
0
            return osRet;
6705
0
        }
6706
6707
0
        osRet += '\n';
6708
0
        osRet += m_description;
6709
0
        osRet += '\n';
6710
0
    }
6711
6712
0
    if (!m_args.empty() && !shortUsage)
6713
0
    {
6714
0
        std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6715
0
        size_t maxOptLen;
6716
0
        std::tie(options, maxOptLen) = GetArgNamesForCLI();
6717
0
        if (usageOptions.maxOptLen)
6718
0
            maxOptLen = usageOptions.maxOptLen;
6719
6720
0
        const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6721
0
        if (m_arbitraryLongNameArgsAllowed)
6722
0
            maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6723
6724
0
        const auto OutputArg =
6725
0
            [this, maxOptLen, &osRet,
6726
0
             &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6727
0
        {
6728
0
            osRet += "  ";
6729
0
            osRet += opt;
6730
0
            osRet += "  ";
6731
0
            osRet.append(maxOptLen - opt.size(), ' ');
6732
0
            osRet += arg->GetDescription();
6733
6734
0
            const auto &choices = arg->GetChoices();
6735
0
            if (!choices.empty())
6736
0
            {
6737
0
                osRet += ". ";
6738
0
                osRet += arg->GetMetaVar();
6739
0
                osRet += '=';
6740
0
                bool firstChoice = true;
6741
0
                for (const auto &choice : choices)
6742
0
                {
6743
0
                    if (!firstChoice)
6744
0
                        osRet += '|';
6745
0
                    osRet += choice;
6746
0
                    firstChoice = false;
6747
0
                }
6748
0
            }
6749
6750
0
            if (arg->GetType() == GAAT_DATASET ||
6751
0
                arg->GetType() == GAAT_DATASET_LIST)
6752
0
            {
6753
0
                if (arg->IsOutput() &&
6754
0
                    arg->GetDatasetInputFlags() == GADV_NAME &&
6755
0
                    arg->GetDatasetOutputFlags() == GADV_OBJECT)
6756
0
                {
6757
0
                    osRet += " (created by algorithm)";
6758
0
                }
6759
0
            }
6760
6761
0
            if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6762
0
            {
6763
0
                osRet += " (default: ";
6764
0
                osRet += arg->GetDefault<std::string>();
6765
0
                osRet += ')';
6766
0
            }
6767
0
            else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6768
0
            {
6769
0
                if (arg->GetDefault<bool>())
6770
0
                    osRet += " (default: true)";
6771
0
            }
6772
0
            else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6773
0
            {
6774
0
                osRet += " (default: ";
6775
0
                osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6776
0
                osRet += ')';
6777
0
            }
6778
0
            else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6779
0
            {
6780
0
                osRet += " (default: ";
6781
0
                osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6782
0
                osRet += ')';
6783
0
            }
6784
0
            else if (arg->GetType() == GAAT_STRING_LIST &&
6785
0
                     arg->HasDefaultValue())
6786
0
            {
6787
0
                const auto &defaultVal =
6788
0
                    arg->GetDefault<std::vector<std::string>>();
6789
0
                if (defaultVal.size() == 1)
6790
0
                {
6791
0
                    osRet += " (default: ";
6792
0
                    osRet += defaultVal[0];
6793
0
                    osRet += ')';
6794
0
                }
6795
0
            }
6796
0
            else if (arg->GetType() == GAAT_INTEGER_LIST &&
6797
0
                     arg->HasDefaultValue())
6798
0
            {
6799
0
                const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6800
0
                if (defaultVal.size() == 1)
6801
0
                {
6802
0
                    osRet += " (default: ";
6803
0
                    osRet += CPLSPrintf("%d", defaultVal[0]);
6804
0
                    osRet += ')';
6805
0
                }
6806
0
            }
6807
0
            else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6808
0
            {
6809
0
                const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6810
0
                if (defaultVal.size() == 1)
6811
0
                {
6812
0
                    osRet += " (default: ";
6813
0
                    osRet += CPLSPrintf("%g", defaultVal[0]);
6814
0
                    osRet += ')';
6815
0
                }
6816
0
            }
6817
6818
0
            if (arg->GetDisplayHintAboutRepetition())
6819
0
            {
6820
0
                if (arg->GetMinCount() > 0 &&
6821
0
                    arg->GetMinCount() == arg->GetMaxCount())
6822
0
                {
6823
0
                    if (arg->GetMinCount() != 1)
6824
0
                        osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6825
0
                }
6826
0
                else if (arg->GetMinCount() > 0 &&
6827
0
                         arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6828
0
                {
6829
0
                    osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6830
0
                                        arg->GetMaxCount());
6831
0
                }
6832
0
                else if (arg->GetMinCount() > 0)
6833
0
                {
6834
0
                    osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6835
0
                }
6836
0
                else if (arg->GetMaxCount() > 1)
6837
0
                {
6838
0
                    osRet += " [may be repeated]";
6839
0
                }
6840
0
            }
6841
6842
0
            if (arg->IsRequired())
6843
0
            {
6844
0
                osRet += " [required]";
6845
0
            }
6846
6847
0
            if (!arg->IsAvailableInPipelineStep() &&
6848
0
                !usageOptions.isPipelineStep)
6849
0
            {
6850
0
                osRet += " [not available in pipelines]";
6851
0
            }
6852
6853
0
            osRet += '\n';
6854
6855
0
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6856
0
            if (!mutualExclusionGroup.empty())
6857
0
            {
6858
0
                std::string otherArgs;
6859
0
                for (const auto &otherArg : m_args)
6860
0
                {
6861
0
                    if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6862
0
                        otherArg.get() == arg)
6863
0
                        continue;
6864
0
                    if (otherArg->GetMutualExclusionGroup() ==
6865
0
                        mutualExclusionGroup)
6866
0
                    {
6867
0
                        if (!otherArgs.empty())
6868
0
                            otherArgs += ", ";
6869
0
                        otherArgs += "--";
6870
0
                        otherArgs += otherArg->GetName();
6871
0
                    }
6872
0
                }
6873
0
                if (!otherArgs.empty())
6874
0
                {
6875
0
                    osRet += "  ";
6876
0
                    osRet += "  ";
6877
0
                    osRet.append(maxOptLen, ' ');
6878
0
                    osRet += "Mutually exclusive with ";
6879
0
                    osRet += otherArgs;
6880
0
                    osRet += '\n';
6881
0
                }
6882
0
            }
6883
6884
            // Check dependency
6885
0
            std::string dependencyArgs;
6886
6887
0
            for (const auto &dependencyArgumentName :
6888
0
                 GetArgDependencies(arg->GetName()))
6889
0
            {
6890
0
                const auto otherArg{GetArg(dependencyArgumentName)};
6891
0
                if (otherArg != nullptr)
6892
0
                {
6893
0
                    if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6894
0
                        otherArg == arg)
6895
0
                    {
6896
0
                        continue;
6897
0
                    }
6898
6899
0
                    if (!dependencyArgs.empty())
6900
0
                    {
6901
0
                        dependencyArgs += ", ";
6902
0
                    }
6903
6904
0
                    dependencyArgs += "--";
6905
0
                    dependencyArgs += otherArg->GetName();
6906
0
                }
6907
0
                else
6908
0
                {
6909
0
                    CPLError(CE_Warning, CPLE_AppDefined,
6910
0
                             "Argument '%s' depends on unknown argument '%s'",
6911
0
                             arg->GetName().c_str(),
6912
0
                             dependencyArgumentName.c_str());
6913
0
                }
6914
0
            }
6915
6916
0
            if (!dependencyArgs.empty())
6917
0
            {
6918
0
                osRet += "  ";
6919
0
                osRet += "  ";
6920
0
                osRet.append(maxOptLen, ' ');
6921
0
                osRet += "Depends on ";
6922
0
                osRet += dependencyArgs;
6923
0
                osRet += '\n';
6924
0
            }
6925
0
        };
6926
6927
0
        if (!m_positionalArgs.empty())
6928
0
        {
6929
0
            osRet += "\nPositional arguments:\n";
6930
0
            for (const auto &[arg, opt] : options)
6931
0
            {
6932
0
                if (arg->IsPositional())
6933
0
                    OutputArg(arg, opt);
6934
0
            }
6935
0
        }
6936
6937
0
        if (hasNonPositionals)
6938
0
        {
6939
0
            bool hasCommon = false;
6940
0
            bool hasBase = false;
6941
0
            bool hasAdvanced = false;
6942
0
            bool hasEsoteric = false;
6943
0
            std::vector<std::string> categories;
6944
0
            for (const auto &iter : options)
6945
0
            {
6946
0
                const auto &arg = iter.first;
6947
0
                if (!arg->IsPositional())
6948
0
                {
6949
0
                    const auto &category = arg->GetCategory();
6950
0
                    if (category == GAAC_COMMON)
6951
0
                    {
6952
0
                        hasCommon = true;
6953
0
                    }
6954
0
                    else if (category == GAAC_BASE)
6955
0
                    {
6956
0
                        hasBase = true;
6957
0
                    }
6958
0
                    else if (category == GAAC_ADVANCED)
6959
0
                    {
6960
0
                        hasAdvanced = true;
6961
0
                    }
6962
0
                    else if (category == GAAC_ESOTERIC)
6963
0
                    {
6964
0
                        hasEsoteric = true;
6965
0
                    }
6966
0
                    else if (std::find(categories.begin(), categories.end(),
6967
0
                                       category) == categories.end())
6968
0
                    {
6969
0
                        categories.push_back(category);
6970
0
                    }
6971
0
                }
6972
0
            }
6973
0
            if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6974
0
                categories.insert(categories.begin(), GAAC_ADVANCED);
6975
0
            if (hasBase)
6976
0
                categories.insert(categories.begin(), GAAC_BASE);
6977
0
            if (hasCommon && !usageOptions.isPipelineStep)
6978
0
                categories.insert(categories.begin(), GAAC_COMMON);
6979
0
            if (hasEsoteric)
6980
0
                categories.push_back(GAAC_ESOTERIC);
6981
6982
0
            for (const auto &category : categories)
6983
0
            {
6984
0
                osRet += "\n";
6985
0
                if (category != GAAC_BASE)
6986
0
                {
6987
0
                    osRet += category;
6988
0
                    osRet += ' ';
6989
0
                }
6990
0
                osRet += "Options:\n";
6991
0
                for (const auto &[arg, opt] : options)
6992
0
                {
6993
0
                    if (!arg->IsPositional() && arg->GetCategory() == category)
6994
0
                        OutputArg(arg, opt);
6995
0
                }
6996
0
                if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6997
0
                {
6998
0
                    osRet += "  ";
6999
0
                    osRet += userProvidedOpt;
7000
0
                    osRet += "  ";
7001
0
                    if (userProvidedOpt.size() < maxOptLen)
7002
0
                        osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
7003
0
                    osRet += "Argument provided by user";
7004
0
                    osRet += '\n';
7005
0
                }
7006
0
            }
7007
0
        }
7008
0
    }
7009
7010
0
    if (!m_longDescription.empty())
7011
0
    {
7012
0
        osRet += '\n';
7013
0
        osRet += m_longDescription;
7014
0
        osRet += '\n';
7015
0
    }
7016
7017
0
    if (!m_helpDocRequested && !usageOptions.isPipelineMain)
7018
0
    {
7019
0
        if (!m_helpURL.empty())
7020
0
        {
7021
0
            osRet += "\nFor more details, consult ";
7022
0
            osRet += GetHelpFullURL();
7023
0
            osRet += '\n';
7024
0
        }
7025
0
        osRet += GetUsageForCLIEnd();
7026
0
    }
7027
7028
0
    return osRet;
7029
0
}
7030
7031
/************************************************************************/
7032
/*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
7033
/************************************************************************/
7034
7035
//! @cond Doxygen_Suppress
7036
std::string GDALAlgorithm::GetUsageForCLIEnd() const
7037
0
{
7038
0
    std::string osRet;
7039
7040
0
    if (!m_callPath.empty() && m_callPath[0] == "gdal")
7041
0
    {
7042
0
        osRet += "\nWARNING: the gdal command is provisionally provided as an "
7043
0
                 "alternative interface to GDAL and OGR command line "
7044
0
                 "utilities.\nThe project reserves the right to modify, "
7045
0
                 "rename, reorganize, and change the behavior of the utility\n"
7046
0
                 "until it is officially frozen in a future feature release of "
7047
0
                 "GDAL.\n";
7048
0
    }
7049
0
    return osRet;
7050
0
}
7051
7052
//! @endcond
7053
7054
/************************************************************************/
7055
/*                   GDALAlgorithm::GetUsageAsJSON()                    */
7056
/************************************************************************/
7057
7058
std::string GDALAlgorithm::GetUsageAsJSON() const
7059
0
{
7060
0
    CPLJSONDocument oDoc;
7061
0
    auto oRoot = oDoc.GetRoot();
7062
7063
0
    if (m_displayInJSONUsage)
7064
0
    {
7065
0
        oRoot.Add("name", m_name);
7066
0
        CPLJSONArray jFullPath;
7067
0
        for (const std::string &s : m_callPath)
7068
0
        {
7069
0
            jFullPath.Add(s);
7070
0
        }
7071
0
        oRoot.Add("full_path", jFullPath);
7072
0
    }
7073
7074
0
    oRoot.Add("description", m_description);
7075
0
    if (!m_helpURL.empty())
7076
0
    {
7077
0
        oRoot.Add("short_url", m_helpURL);
7078
0
        oRoot.Add("url", GetHelpFullURL());
7079
0
    }
7080
7081
0
    CPLJSONArray jSubAlgorithms;
7082
0
    for (const auto &subAlgName : GetSubAlgorithmNames())
7083
0
    {
7084
0
        auto subAlg = InstantiateSubAlgorithm(subAlgName);
7085
0
        if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
7086
0
        {
7087
0
            CPLJSONDocument oSubDoc;
7088
0
            CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
7089
0
            jSubAlgorithms.Add(oSubDoc.GetRoot());
7090
0
        }
7091
0
    }
7092
0
    oRoot.Add("sub_algorithms", jSubAlgorithms);
7093
7094
0
    if (m_arbitraryLongNameArgsAllowed)
7095
0
    {
7096
0
        oRoot.Add("user_provided_arguments_allowed", true);
7097
0
    }
7098
7099
0
    const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
7100
0
    {
7101
0
        CPLJSONObject jArg;
7102
0
        jArg.Add("name", arg->GetName());
7103
0
        jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
7104
0
        jArg.Add("description", arg->GetDescription());
7105
7106
0
        const auto &metaVar = arg->GetMetaVar();
7107
0
        if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
7108
0
        {
7109
0
            if (metaVar.front() == '<' && metaVar.back() == '>' &&
7110
0
                metaVar.substr(1, metaVar.size() - 2).find('>') ==
7111
0
                    std::string::npos)
7112
0
                jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
7113
0
            else
7114
0
                jArg.Add("metavar", metaVar);
7115
0
        }
7116
7117
0
        if (!arg->IsAvailableInPipelineStep())
7118
0
        {
7119
0
            jArg.Add("available_in_pipeline_step", false);
7120
0
        }
7121
7122
0
        const auto &choices = arg->GetChoices();
7123
0
        if (!choices.empty())
7124
0
        {
7125
0
            CPLJSONArray jChoices;
7126
0
            for (const auto &choice : choices)
7127
0
                jChoices.Add(choice);
7128
0
            jArg.Add("choices", jChoices);
7129
0
        }
7130
0
        if (arg->HasDefaultValue())
7131
0
        {
7132
0
            switch (arg->GetType())
7133
0
            {
7134
0
                case GAAT_BOOLEAN:
7135
0
                    jArg.Add("default", arg->GetDefault<bool>());
7136
0
                    break;
7137
0
                case GAAT_STRING:
7138
0
                    jArg.Add("default", arg->GetDefault<std::string>());
7139
0
                    break;
7140
0
                case GAAT_INTEGER:
7141
0
                    jArg.Add("default", arg->GetDefault<int>());
7142
0
                    break;
7143
0
                case GAAT_REAL:
7144
0
                    jArg.Add("default", arg->GetDefault<double>());
7145
0
                    break;
7146
0
                case GAAT_STRING_LIST:
7147
0
                {
7148
0
                    const auto &val =
7149
0
                        arg->GetDefault<std::vector<std::string>>();
7150
0
                    if (val.size() == 1)
7151
0
                    {
7152
0
                        jArg.Add("default", val[0]);
7153
0
                    }
7154
0
                    else
7155
0
                    {
7156
0
                        CPLJSONArray jArr;
7157
0
                        for (const auto &s : val)
7158
0
                        {
7159
0
                            jArr.Add(s);
7160
0
                        }
7161
0
                        jArg.Add("default", jArr);
7162
0
                    }
7163
0
                    break;
7164
0
                }
7165
0
                case GAAT_INTEGER_LIST:
7166
0
                {
7167
0
                    const auto &val = arg->GetDefault<std::vector<int>>();
7168
0
                    if (val.size() == 1)
7169
0
                    {
7170
0
                        jArg.Add("default", val[0]);
7171
0
                    }
7172
0
                    else
7173
0
                    {
7174
0
                        CPLJSONArray jArr;
7175
0
                        for (int i : val)
7176
0
                        {
7177
0
                            jArr.Add(i);
7178
0
                        }
7179
0
                        jArg.Add("default", jArr);
7180
0
                    }
7181
0
                    break;
7182
0
                }
7183
0
                case GAAT_REAL_LIST:
7184
0
                {
7185
0
                    const auto &val = arg->GetDefault<std::vector<double>>();
7186
0
                    if (val.size() == 1)
7187
0
                    {
7188
0
                        jArg.Add("default", val[0]);
7189
0
                    }
7190
0
                    else
7191
0
                    {
7192
0
                        CPLJSONArray jArr;
7193
0
                        for (double d : val)
7194
0
                        {
7195
0
                            jArr.Add(d);
7196
0
                        }
7197
0
                        jArg.Add("default", jArr);
7198
0
                    }
7199
0
                    break;
7200
0
                }
7201
0
                case GAAT_DATASET:
7202
0
                case GAAT_DATASET_LIST:
7203
0
                    CPLError(CE_Warning, CPLE_AppDefined,
7204
0
                             "Unhandled default value for arg %s",
7205
0
                             arg->GetName().c_str());
7206
0
                    break;
7207
0
            }
7208
0
        }
7209
7210
0
        const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7211
0
        if (!std::isnan(minVal))
7212
0
        {
7213
0
            if (arg->GetType() == GAAT_INTEGER ||
7214
0
                arg->GetType() == GAAT_INTEGER_LIST)
7215
0
                jArg.Add("min_value", static_cast<int>(minVal));
7216
0
            else
7217
0
                jArg.Add("min_value", minVal);
7218
0
            jArg.Add("min_value_is_included", minValIsIncluded);
7219
0
        }
7220
7221
0
        const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7222
0
        if (!std::isnan(maxVal))
7223
0
        {
7224
0
            if (arg->GetType() == GAAT_INTEGER ||
7225
0
                arg->GetType() == GAAT_INTEGER_LIST)
7226
0
                jArg.Add("max_value", static_cast<int>(maxVal));
7227
0
            else
7228
0
                jArg.Add("max_value", maxVal);
7229
0
            jArg.Add("max_value_is_included", maxValIsIncluded);
7230
0
        }
7231
7232
0
        jArg.Add("required", arg->IsRequired());
7233
0
        if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7234
0
        {
7235
0
            jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7236
0
            jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7237
0
            jArg.Add("min_count", arg->GetMinCount());
7238
0
            jArg.Add("max_count", arg->GetMaxCount());
7239
0
        }
7240
7241
        // Process dependencies
7242
0
        const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7243
0
        if (!mutualDependencyGroup.empty())
7244
0
        {
7245
0
            jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7246
0
        }
7247
7248
0
        CPLJSONArray jDependencies;
7249
0
        for (const auto &dependencyArgumentName :
7250
0
             GetArgDependencies(arg->GetName()))
7251
0
        {
7252
0
            jDependencies.Add(dependencyArgumentName);
7253
0
        }
7254
7255
0
        if (jDependencies.Size() > 0)
7256
0
        {
7257
0
            jArg.Add("depends_on", jDependencies);
7258
0
        }
7259
7260
0
        jArg.Add("category", arg->GetCategory());
7261
7262
0
        if (arg->GetType() == GAAT_DATASET ||
7263
0
            arg->GetType() == GAAT_DATASET_LIST)
7264
0
        {
7265
0
            {
7266
0
                CPLJSONArray jAr;
7267
0
                if (arg->GetDatasetType() & GDAL_OF_RASTER)
7268
0
                    jAr.Add("raster");
7269
0
                if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7270
0
                    jAr.Add("vector");
7271
0
                if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7272
0
                    jAr.Add("multidim_raster");
7273
0
                jArg.Add("dataset_type", jAr);
7274
0
            }
7275
7276
0
            const auto GetFlags = [](int flags)
7277
0
            {
7278
0
                CPLJSONArray jAr;
7279
0
                if (flags & GADV_NAME)
7280
0
                    jAr.Add("name");
7281
0
                if (flags & GADV_OBJECT)
7282
0
                    jAr.Add("dataset");
7283
0
                return jAr;
7284
0
            };
7285
7286
0
            if (arg->IsInput())
7287
0
            {
7288
0
                jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7289
0
            }
7290
0
            if (arg->IsOutput())
7291
0
            {
7292
0
                jArg.Add("output_flags",
7293
0
                         GetFlags(arg->GetDatasetOutputFlags()));
7294
0
            }
7295
0
        }
7296
7297
0
        const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7298
0
        if (!mutualExclusionGroup.empty())
7299
0
        {
7300
0
            jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7301
0
        }
7302
7303
0
        const auto &metadata = arg->GetMetadata();
7304
0
        if (!metadata.empty())
7305
0
        {
7306
0
            CPLJSONObject jMetadata;
7307
0
            for (const auto &[key, values] : metadata)
7308
0
            {
7309
0
                CPLJSONArray jValue;
7310
0
                for (const auto &value : values)
7311
0
                    jValue.Add(value);
7312
0
                jMetadata.Add(key, jValue);
7313
0
            }
7314
0
            jArg.Add("metadata", jMetadata);
7315
0
        }
7316
7317
0
        return jArg;
7318
0
    };
7319
7320
0
    {
7321
0
        CPLJSONArray jArgs;
7322
0
        for (const auto &arg : m_args)
7323
0
        {
7324
0
            if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7325
0
                jArgs.Add(ProcessArg(arg.get()));
7326
0
        }
7327
0
        oRoot.Add("input_arguments", jArgs);
7328
0
    }
7329
7330
0
    {
7331
0
        CPLJSONArray jArgs;
7332
0
        for (const auto &arg : m_args)
7333
0
        {
7334
0
            if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7335
0
                jArgs.Add(ProcessArg(arg.get()));
7336
0
        }
7337
0
        oRoot.Add("output_arguments", jArgs);
7338
0
    }
7339
7340
0
    {
7341
0
        CPLJSONArray jArgs;
7342
0
        for (const auto &arg : m_args)
7343
0
        {
7344
0
            if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7345
0
                jArgs.Add(ProcessArg(arg.get()));
7346
0
        }
7347
0
        oRoot.Add("input_output_arguments", jArgs);
7348
0
    }
7349
7350
0
    if (m_supportsStreamedOutput)
7351
0
    {
7352
0
        oRoot.Add("supports_streamed_output", true);
7353
0
    }
7354
7355
0
    return oDoc.SaveAsString();
7356
0
}
7357
7358
/************************************************************************/
7359
/*                   GDALAlgorithm::GetAutoComplete()                   */
7360
/************************************************************************/
7361
7362
std::vector<std::string>
7363
GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7364
                               bool lastWordIsComplete, bool showAllOptions)
7365
0
{
7366
0
    std::vector<std::string> ret;
7367
7368
    // Get inner-most algorithm
7369
0
    std::unique_ptr<GDALAlgorithm> curAlgHolder;
7370
0
    GDALAlgorithm *curAlg = this;
7371
0
    while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7372
0
    {
7373
0
        auto subAlg = curAlg->InstantiateSubAlgorithm(
7374
0
            args.front(), /* suggestionAllowed = */ false);
7375
0
        if (!subAlg)
7376
0
            break;
7377
0
        if (args.size() == 1 && !lastWordIsComplete)
7378
0
        {
7379
0
            int nCount = 0;
7380
0
            for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7381
0
            {
7382
0
                if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7383
0
                    nCount++;
7384
0
            }
7385
0
            if (nCount >= 2)
7386
0
            {
7387
0
                for (const std::string &subAlgName :
7388
0
                     curAlg->GetSubAlgorithmNames())
7389
0
                {
7390
0
                    subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7391
0
                    if (subAlg && !subAlg->IsHidden())
7392
0
                        ret.push_back(subAlg->GetName());
7393
0
                }
7394
0
                return ret;
7395
0
            }
7396
0
        }
7397
0
        showAllOptions = false;
7398
0
        args.erase(args.begin());
7399
0
        curAlgHolder = std::move(subAlg);
7400
0
        curAlg = curAlgHolder.get();
7401
0
    }
7402
0
    if (curAlg != this)
7403
0
    {
7404
0
        curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7405
0
        return curAlg->GetAutoComplete(args, lastWordIsComplete,
7406
0
                                       /* showAllOptions = */ false);
7407
0
    }
7408
7409
0
    std::string option;
7410
0
    std::string value;
7411
0
    ExtractLastOptionAndValue(args, option, value);
7412
7413
0
    if (option.empty() && !args.empty() && !args.back().empty() &&
7414
0
        args.back()[0] == '-')
7415
0
    {
7416
0
        const auto &lastArg = args.back();
7417
        // List available options
7418
0
        for (const auto &arg : GetArgs())
7419
0
        {
7420
0
            if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7421
0
                (!showAllOptions &&
7422
0
                 (arg->GetName() == "help" || arg->GetName() == "config" ||
7423
0
                  arg->GetName() == "version" ||
7424
0
                  arg->GetName() == "json-usage")))
7425
0
            {
7426
0
                continue;
7427
0
            }
7428
0
            if (!arg->GetShortName().empty())
7429
0
            {
7430
0
                std::string str = std::string("-").append(arg->GetShortName());
7431
0
                if (lastArg == str)
7432
0
                    ret.push_back(std::move(str));
7433
0
            }
7434
0
            if (lastArg != "-" && lastArg != "--")
7435
0
            {
7436
0
                for (const std::string &alias : arg->GetAliases())
7437
0
                {
7438
0
                    std::string str = std::string("--").append(alias);
7439
0
                    if (cpl::starts_with(str, lastArg))
7440
0
                        ret.push_back(std::move(str));
7441
0
                }
7442
0
            }
7443
0
            if (!arg->GetName().empty())
7444
0
            {
7445
0
                std::string str = std::string("--").append(arg->GetName());
7446
0
                if (cpl::starts_with(str, lastArg))
7447
0
                    ret.push_back(std::move(str));
7448
0
            }
7449
0
        }
7450
0
        std::sort(ret.begin(), ret.end());
7451
0
    }
7452
0
    else if (!option.empty())
7453
0
    {
7454
        // List possible choices for current option
7455
0
        auto arg = GetArg(option);
7456
0
        if (arg && arg->GetType() != GAAT_BOOLEAN)
7457
0
        {
7458
0
            ret = arg->GetChoices();
7459
0
            if (ret.empty())
7460
0
            {
7461
0
                {
7462
0
                    CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7463
0
                    SetParseForAutoCompletion();
7464
0
                    CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7465
0
                }
7466
0
                ret = arg->GetAutoCompleteChoices(value);
7467
0
            }
7468
0
            else
7469
0
            {
7470
0
                std::sort(ret.begin(), ret.end());
7471
0
            }
7472
0
            if (!ret.empty() && ret.back() == value)
7473
0
            {
7474
0
                ret.clear();
7475
0
            }
7476
0
            else if (ret.empty())
7477
0
            {
7478
0
                ret.push_back("**");
7479
                // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7480
0
                ret.push_back(std::string("\xC2\xA0"
7481
0
                                          "description: ")
7482
0
                                  .append(arg->GetDescription()));
7483
0
            }
7484
0
        }
7485
0
    }
7486
0
    else
7487
0
    {
7488
        // List possible sub-algorithms
7489
0
        for (const std::string &subAlgName : GetSubAlgorithmNames())
7490
0
        {
7491
0
            auto subAlg = InstantiateSubAlgorithm(subAlgName);
7492
0
            if (subAlg && !subAlg->IsHidden())
7493
0
                ret.push_back(subAlg->GetName());
7494
0
        }
7495
0
        if (!ret.empty())
7496
0
        {
7497
0
            std::sort(ret.begin(), ret.end());
7498
0
        }
7499
7500
        // Try filenames
7501
0
        if (ret.empty() && !args.empty())
7502
0
        {
7503
0
            {
7504
0
                CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7505
0
                SetParseForAutoCompletion();
7506
0
                CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7507
0
            }
7508
7509
0
            const std::string &lastArg = args.back();
7510
0
            GDALAlgorithmArg *arg = nullptr;
7511
0
            for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7512
0
                                     "like", "source", "destination"})
7513
0
            {
7514
0
                if (!arg)
7515
0
                {
7516
0
                    auto newArg = GetArg(name);
7517
0
                    if (newArg)
7518
0
                    {
7519
0
                        if (!newArg->IsExplicitlySet())
7520
0
                        {
7521
0
                            arg = newArg;
7522
0
                        }
7523
0
                        else if (newArg->GetType() == GAAT_STRING ||
7524
0
                                 newArg->GetType() == GAAT_STRING_LIST ||
7525
0
                                 newArg->GetType() == GAAT_DATASET ||
7526
0
                                 newArg->GetType() == GAAT_DATASET_LIST)
7527
0
                        {
7528
0
                            VSIStatBufL sStat;
7529
0
                            if ((!lastArg.empty() && lastArg.back() == '/') ||
7530
0
                                VSIStatL(lastArg.c_str(), &sStat) != 0)
7531
0
                            {
7532
0
                                arg = newArg;
7533
0
                            }
7534
0
                        }
7535
0
                    }
7536
0
                }
7537
0
            }
7538
0
            if (arg)
7539
0
            {
7540
0
                ret = arg->GetAutoCompleteChoices(lastArg);
7541
0
            }
7542
0
        }
7543
0
    }
7544
7545
0
    return ret;
7546
0
}
7547
7548
/************************************************************************/
7549
/*                   GDALAlgorithm::GetFieldIndices()                   */
7550
/************************************************************************/
7551
7552
bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7553
                                    OGRLayerH hLayer, std::vector<int> &indices)
7554
0
{
7555
0
    VALIDATE_POINTER1(hLayer, __func__, false);
7556
7557
0
    const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7558
7559
0
    if (names.size() == 1 && names[0] == "ALL")
7560
0
    {
7561
0
        const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7562
0
        for (int i = 0; i < nSrcFieldCount; ++i)
7563
0
        {
7564
0
            indices.push_back(i);
7565
0
        }
7566
0
    }
7567
0
    else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7568
0
    {
7569
0
        std::set<int> fieldsAdded;
7570
0
        for (const std::string &osFieldName : names)
7571
0
        {
7572
7573
0
            const int nIdx =
7574
0
                layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7575
7576
0
            if (nIdx < 0)
7577
0
            {
7578
0
                CPLError(CE_Failure, CPLE_AppDefined,
7579
0
                         "Field '%s' does not exist in layer '%s'",
7580
0
                         osFieldName.c_str(), layer.GetName());
7581
0
                return false;
7582
0
            }
7583
7584
0
            if (fieldsAdded.insert(nIdx).second)
7585
0
            {
7586
0
                indices.push_back(nIdx);
7587
0
            }
7588
0
        }
7589
0
    }
7590
7591
0
    return true;
7592
0
}
7593
7594
/************************************************************************/
7595
/*              GDALAlgorithm::ExtractLastOptionAndValue()              */
7596
/************************************************************************/
7597
7598
void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7599
                                              std::string &option,
7600
                                              std::string &value) const
7601
0
{
7602
0
    if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7603
0
    {
7604
0
        const auto nPosEqual = args.back().find('=');
7605
0
        if (nPosEqual == std::string::npos)
7606
0
        {
7607
            // Deal with "gdal ... --option"
7608
0
            if (GetArg(args.back()))
7609
0
            {
7610
0
                option = args.back();
7611
0
                args.pop_back();
7612
0
            }
7613
0
        }
7614
0
        else
7615
0
        {
7616
            // Deal with "gdal ... --option=<value>"
7617
0
            if (GetArg(args.back().substr(0, nPosEqual)))
7618
0
            {
7619
0
                option = args.back().substr(0, nPosEqual);
7620
0
                value = args.back().substr(nPosEqual + 1);
7621
0
                args.pop_back();
7622
0
            }
7623
0
        }
7624
0
    }
7625
0
    else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7626
0
             args[args.size() - 2][0] == '-')
7627
0
    {
7628
        // Deal with "gdal ... --option <value>"
7629
0
        auto arg = GetArg(args[args.size() - 2]);
7630
0
        if (arg && arg->GetType() != GAAT_BOOLEAN)
7631
0
        {
7632
0
            option = args[args.size() - 2];
7633
0
            value = args.back();
7634
0
            args.pop_back();
7635
0
        }
7636
0
    }
7637
7638
0
    const auto IsKeyValueOption = [](const std::string &osStr)
7639
0
    {
7640
0
        return osStr == "--co" || osStr == "--creation-option" ||
7641
0
               osStr == "--lco" || osStr == "--layer-creation-option" ||
7642
0
               osStr == "--oo" || osStr == "--open-option";
7643
0
    };
7644
7645
0
    if (IsKeyValueOption(option))
7646
0
    {
7647
0
        const auto nPosEqual = value.find('=');
7648
0
        if (nPosEqual != std::string::npos)
7649
0
        {
7650
0
            value.resize(nPosEqual);
7651
0
        }
7652
0
    }
7653
0
}
7654
7655
/************************************************************************/
7656
/*                 GDALAlgorithm::GetArgDependencies()                  */
7657
/************************************************************************/
7658
7659
std::vector<std::string>
7660
GDALAlgorithm::GetArgDependencies(const std::string &osName) const
7661
0
{
7662
0
    const auto arg = GetArg(osName, false);
7663
0
    if (!arg)
7664
0
    {
7665
0
        ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
7666
0
                    osName.c_str());
7667
0
        return {};
7668
0
    }
7669
0
    std::vector<std::string> dependencies = arg->GetDirectDependencies();
7670
0
    if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7671
0
        !mutualDependencyGroup.empty())
7672
0
    {
7673
0
        for (const auto &otherArg : m_args)
7674
0
        {
7675
0
            if (otherArg.get() == arg ||
7676
0
                mutualDependencyGroup.compare(
7677
0
                    otherArg->GetMutualDependencyGroup()) != 0)
7678
0
                continue;
7679
0
            dependencies.push_back(otherArg->GetName());
7680
0
        }
7681
0
    }
7682
0
    return dependencies;
7683
0
}
7684
7685
//! @cond Doxygen_Suppress
7686
7687
/************************************************************************/
7688
/*                  GDALContainerAlgorithm::RunImpl()                   */
7689
/************************************************************************/
7690
7691
bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7692
0
{
7693
0
    return false;
7694
0
}
7695
7696
//! @endcond
7697
7698
/************************************************************************/
7699
/*                        GDALAlgorithmRelease()                        */
7700
/************************************************************************/
7701
7702
/** Release a handle to an algorithm.
7703
 *
7704
 * @since 3.11
7705
 */
7706
void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7707
0
{
7708
0
    delete hAlg;
7709
0
}
7710
7711
/************************************************************************/
7712
/*                        GDALAlgorithmGetName()                        */
7713
/************************************************************************/
7714
7715
/** Return the algorithm name.
7716
 *
7717
 * @param hAlg Handle to an algorithm. Must NOT be null.
7718
 * @return algorithm name whose lifetime is bound to hAlg and which must not
7719
 * be freed.
7720
 * @since 3.11
7721
 */
7722
const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7723
0
{
7724
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7725
0
    return hAlg->ptr->GetName().c_str();
7726
0
}
7727
7728
/************************************************************************/
7729
/*                    GDALAlgorithmGetDescription()                     */
7730
/************************************************************************/
7731
7732
/** Return the algorithm (short) description.
7733
 *
7734
 * @param hAlg Handle to an algorithm. Must NOT be null.
7735
 * @return algorithm description whose lifetime is bound to hAlg and which must
7736
 * not be freed.
7737
 * @since 3.11
7738
 */
7739
const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7740
0
{
7741
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7742
0
    return hAlg->ptr->GetDescription().c_str();
7743
0
}
7744
7745
/************************************************************************/
7746
/*                  GDALAlgorithmGetLongDescription()                   */
7747
/************************************************************************/
7748
7749
/** Return the algorithm (longer) description.
7750
 *
7751
 * @param hAlg Handle to an algorithm. Must NOT be null.
7752
 * @return algorithm description whose lifetime is bound to hAlg and which must
7753
 * not be freed.
7754
 * @since 3.11
7755
 */
7756
const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7757
0
{
7758
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7759
0
    return hAlg->ptr->GetLongDescription().c_str();
7760
0
}
7761
7762
/************************************************************************/
7763
/*                    GDALAlgorithmGetHelpFullURL()                     */
7764
/************************************************************************/
7765
7766
/** Return the algorithm full URL.
7767
 *
7768
 * @param hAlg Handle to an algorithm. Must NOT be null.
7769
 * @return algorithm URL whose lifetime is bound to hAlg and which must
7770
 * not be freed.
7771
 * @since 3.11
7772
 */
7773
const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7774
0
{
7775
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7776
0
    return hAlg->ptr->GetHelpFullURL().c_str();
7777
0
}
7778
7779
/************************************************************************/
7780
/*                   GDALAlgorithmHasSubAlgorithms()                    */
7781
/************************************************************************/
7782
7783
/** Return whether the algorithm has sub-algorithms.
7784
 *
7785
 * @param hAlg Handle to an algorithm. Must NOT be null.
7786
 * @since 3.11
7787
 */
7788
bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7789
0
{
7790
0
    VALIDATE_POINTER1(hAlg, __func__, false);
7791
0
    return hAlg->ptr->HasSubAlgorithms();
7792
0
}
7793
7794
/************************************************************************/
7795
/*                 GDALAlgorithmGetSubAlgorithmNames()                  */
7796
/************************************************************************/
7797
7798
/** Get the names of registered algorithms.
7799
 *
7800
 * @param hAlg Handle to an algorithm. Must NOT be null.
7801
 * @return a NULL terminated list of names, which must be destroyed with
7802
 * CSLDestroy()
7803
 * @since 3.11
7804
 */
7805
char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7806
0
{
7807
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7808
0
    return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7809
0
}
7810
7811
/************************************************************************/
7812
/*                GDALAlgorithmInstantiateSubAlgorithm()                */
7813
/************************************************************************/
7814
7815
/** Instantiate an algorithm by its name (or its alias).
7816
 *
7817
 * @param hAlg Handle to an algorithm. Must NOT be null.
7818
 * @param pszSubAlgName Algorithm name. Must NOT be null.
7819
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7820
 * or NULL if the algorithm does not exist or another error occurred.
7821
 * @since 3.11
7822
 */
7823
GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7824
                                                    const char *pszSubAlgName)
7825
0
{
7826
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7827
0
    VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7828
0
    auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7829
0
    return subAlg
7830
0
               ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7831
0
               : nullptr;
7832
0
}
7833
7834
/************************************************************************/
7835
/*               GDALAlgorithmParseCommandLineArguments()               */
7836
/************************************************************************/
7837
7838
/** Parse a command line argument, which does not include the algorithm
7839
 * name, to set the value of corresponding arguments.
7840
 *
7841
 * @param hAlg Handle to an algorithm. Must NOT be null.
7842
 * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7843
 * @return true if successful, false otherwise
7844
 * @since 3.11
7845
 */
7846
7847
bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7848
                                            CSLConstList papszArgs)
7849
0
{
7850
0
    VALIDATE_POINTER1(hAlg, __func__, false);
7851
0
    return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7852
0
}
7853
7854
/************************************************************************/
7855
/*                  GDALAlgorithmGetActualAlgorithm()                   */
7856
/************************************************************************/
7857
7858
/** Return the actual algorithm that is going to be invoked, when the
7859
 * current algorithm has sub-algorithms.
7860
 *
7861
 * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7862
 *
7863
 * Note that the lifetime of the returned algorithm does not exceed the one of
7864
 * the hAlg instance that owns it.
7865
 *
7866
 * @param hAlg Handle to an algorithm. Must NOT be null.
7867
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7868
 * @since 3.11
7869
 */
7870
GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7871
0
{
7872
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7873
0
    return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7874
0
}
7875
7876
/************************************************************************/
7877
/*                          GDALAlgorithmRun()                          */
7878
/************************************************************************/
7879
7880
/** Execute the algorithm, starting with ValidateArguments() and then
7881
 * calling RunImpl().
7882
 *
7883
 * This function must be called at most once per instance.
7884
 *
7885
 * @param hAlg Handle to an algorithm. Must NOT be null.
7886
 * @param pfnProgress Progress callback. May be null.
7887
 * @param pProgressData Progress callback user data. May be null.
7888
 * @return true if successful, false otherwise
7889
 * @since 3.11
7890
 */
7891
7892
bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7893
                      void *pProgressData)
7894
0
{
7895
0
    VALIDATE_POINTER1(hAlg, __func__, false);
7896
0
    return hAlg->ptr->Run(pfnProgress, pProgressData);
7897
0
}
7898
7899
/************************************************************************/
7900
/*                       GDALAlgorithmFinalize()                        */
7901
/************************************************************************/
7902
7903
/** Complete any pending actions, and return the final status.
7904
 * This is typically useful for algorithm that generate an output dataset.
7905
 *
7906
 * Note that this function does *NOT* release memory associated with the
7907
 * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7908
 *
7909
 * @param hAlg Handle to an algorithm. Must NOT be null.
7910
 * @return true if successful, false otherwise
7911
 * @since 3.11
7912
 */
7913
7914
bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7915
0
{
7916
0
    VALIDATE_POINTER1(hAlg, __func__, false);
7917
0
    return hAlg->ptr->Finalize();
7918
0
}
7919
7920
/************************************************************************/
7921
/*                    GDALAlgorithmGetUsageAsJSON()                     */
7922
/************************************************************************/
7923
7924
/** Return the usage of the algorithm as a JSON-serialized string.
7925
 *
7926
 * This can be used to dynamically generate interfaces to algorithms.
7927
 *
7928
 * @param hAlg Handle to an algorithm. Must NOT be null.
7929
 * @return a string that must be freed with CPLFree()
7930
 * @since 3.11
7931
 */
7932
char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7933
0
{
7934
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7935
0
    return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7936
0
}
7937
7938
/************************************************************************/
7939
/*                      GDALAlgorithmGetArgNames()                      */
7940
/************************************************************************/
7941
7942
/** Return the list of available argument names.
7943
 *
7944
 * @param hAlg Handle to an algorithm. Must NOT be null.
7945
 * @return a NULL terminated list of names, which must be destroyed with
7946
 * CSLDestroy()
7947
 * @since 3.11
7948
 */
7949
char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7950
0
{
7951
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7952
0
    CPLStringList list;
7953
0
    for (const auto &arg : hAlg->ptr->GetArgs())
7954
0
        list.AddString(arg->GetName().c_str());
7955
0
    return list.StealList();
7956
0
}
7957
7958
/************************************************************************/
7959
/*                        GDALAlgorithmGetArg()                         */
7960
/************************************************************************/
7961
7962
/** Return an argument from its name.
7963
 *
7964
 * The lifetime of the returned object does not exceed the one of hAlg.
7965
 *
7966
 * @param hAlg Handle to an algorithm. Must NOT be null.
7967
 * @param pszArgName Argument name. Must NOT be null.
7968
 * @return an argument that must be released with GDALAlgorithmArgRelease(),
7969
 * or nullptr in case of error
7970
 * @since 3.11
7971
 */
7972
GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7973
                                      const char *pszArgName)
7974
0
{
7975
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
7976
0
    VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7977
0
    auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7978
0
                                 /* isConst = */ true);
7979
0
    if (!arg)
7980
0
        return nullptr;
7981
0
    return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7982
0
}
7983
7984
/************************************************************************/
7985
/*                    GDALAlgorithmGetArgNonConst()                     */
7986
/************************************************************************/
7987
7988
/** Return an argument from its name, possibly allowing creation of user-provided
7989
 * argument if the algorithm allow it.
7990
 *
7991
 * The lifetime of the returned object does not exceed the one of hAlg.
7992
 *
7993
 * @param hAlg Handle to an algorithm. Must NOT be null.
7994
 * @param pszArgName Argument name. Must NOT be null.
7995
 * @return an argument that must be released with GDALAlgorithmArgRelease(),
7996
 * or nullptr in case of error
7997
 * @since 3.12
7998
 */
7999
GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
8000
                                              const char *pszArgName)
8001
0
{
8002
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
8003
0
    VALIDATE_POINTER1(pszArgName, __func__, nullptr);
8004
0
    auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
8005
0
                                 /* isConst = */ false);
8006
0
    if (!arg)
8007
0
        return nullptr;
8008
0
    return std::make_unique<GDALAlgorithmArgHS>(arg).release();
8009
0
}
8010
8011
/************************************************************************/
8012
/*                  GDALAlgorithmGetArgDependencies()                   */
8013
/************************************************************************/
8014
8015
/** Return the list of argument names the specified argument depends on.
8016
 *
8017
 *  This includes both regular dependencies and mutual dependencies.
8018
 *
8019
 * @param hAlg Handle to an algorithm. Must NOT be null.
8020
 * @param pszArgName Argument name. Must NOT be null.
8021
 * @return a NULL terminated list of names, which must be destroyed with
8022
 * CSLDestroy()
8023
 * @since 3.11
8024
 */
8025
char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
8026
                                       const char *pszArgName)
8027
0
{
8028
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
8029
0
    VALIDATE_POINTER1(pszArgName, __func__, nullptr);
8030
0
    return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
8031
0
}
8032
8033
/************************************************************************/
8034
/*                      GDALAlgorithmArgRelease()                       */
8035
/************************************************************************/
8036
8037
/** Release a handle to an argument.
8038
 *
8039
 * @since 3.11
8040
 */
8041
void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
8042
0
{
8043
0
    delete hArg;
8044
0
}
8045
8046
/************************************************************************/
8047
/*                      GDALAlgorithmArgGetName()                       */
8048
/************************************************************************/
8049
8050
/** Return the name of an argument.
8051
 *
8052
 * @param hArg Handle to an argument. Must NOT be null.
8053
 * @return argument name whose lifetime is bound to hArg and which must not
8054
 * be freed.
8055
 * @since 3.11
8056
 */
8057
const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
8058
0
{
8059
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8060
0
    return hArg->ptr->GetName().c_str();
8061
0
}
8062
8063
/************************************************************************/
8064
/*                      GDALAlgorithmArgGetType()                       */
8065
/************************************************************************/
8066
8067
/** Get the type of an argument
8068
 *
8069
 * @param hArg Handle to an argument. Must NOT be null.
8070
 * @since 3.11
8071
 */
8072
GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
8073
0
{
8074
0
    VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
8075
0
    return hArg->ptr->GetType();
8076
0
}
8077
8078
/************************************************************************/
8079
/*                   GDALAlgorithmArgGetDescription()                   */
8080
/************************************************************************/
8081
8082
/** Return the description of an argument.
8083
 *
8084
 * @param hArg Handle to an argument. Must NOT be null.
8085
 * @return argument description whose lifetime is bound to hArg and which must not
8086
 * be freed.
8087
 * @since 3.11
8088
 */
8089
const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
8090
0
{
8091
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8092
0
    return hArg->ptr->GetDescription().c_str();
8093
0
}
8094
8095
/************************************************************************/
8096
/*                    GDALAlgorithmArgGetShortName()                    */
8097
/************************************************************************/
8098
8099
/** Return the short name, or empty string if there is none
8100
 *
8101
 * @param hArg Handle to an argument. Must NOT be null.
8102
 * @return short name whose lifetime is bound to hArg and which must not
8103
 * be freed.
8104
 * @since 3.11
8105
 */
8106
const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
8107
0
{
8108
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8109
0
    return hArg->ptr->GetShortName().c_str();
8110
0
}
8111
8112
/************************************************************************/
8113
/*                     GDALAlgorithmArgGetAliases()                     */
8114
/************************************************************************/
8115
8116
/** Return the aliases (potentially none)
8117
 *
8118
 * @param hArg Handle to an argument. Must NOT be null.
8119
 * @return a NULL terminated list of names, which must be destroyed with
8120
 * CSLDestroy()
8121
8122
 * @since 3.11
8123
 */
8124
char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
8125
0
{
8126
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8127
0
    return CPLStringList(hArg->ptr->GetAliases()).StealList();
8128
0
}
8129
8130
/************************************************************************/
8131
/*                     GDALAlgorithmArgGetMetaVar()                     */
8132
/************************************************************************/
8133
8134
/** Return the "meta-var" hint.
8135
 *
8136
 * By default, the meta-var value is the long name of the argument in
8137
 * upper case.
8138
 *
8139
 * @param hArg Handle to an argument. Must NOT be null.
8140
 * @return meta-var hint whose lifetime is bound to hArg and which must not
8141
 * be freed.
8142
 * @since 3.11
8143
 */
8144
const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
8145
0
{
8146
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8147
0
    return hArg->ptr->GetMetaVar().c_str();
8148
0
}
8149
8150
/************************************************************************/
8151
/*                    GDALAlgorithmArgGetCategory()                     */
8152
/************************************************************************/
8153
8154
/** Return the argument category
8155
 *
8156
 * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
8157
 *
8158
 * @param hArg Handle to an argument. Must NOT be null.
8159
 * @return category whose lifetime is bound to hArg and which must not
8160
 * be freed.
8161
 * @since 3.11
8162
 */
8163
const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
8164
0
{
8165
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8166
0
    return hArg->ptr->GetCategory().c_str();
8167
0
}
8168
8169
/************************************************************************/
8170
/*                    GDALAlgorithmArgIsPositional()                    */
8171
/************************************************************************/
8172
8173
/** Return if the argument is a positional one.
8174
 *
8175
 * @param hArg Handle to an argument. Must NOT be null.
8176
 * @since 3.11
8177
 */
8178
bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
8179
0
{
8180
0
    VALIDATE_POINTER1(hArg, __func__, false);
8181
0
    return hArg->ptr->IsPositional();
8182
0
}
8183
8184
/************************************************************************/
8185
/*                     GDALAlgorithmArgIsRequired()                     */
8186
/************************************************************************/
8187
8188
/** Return whether the argument is required. Defaults to false.
8189
 *
8190
 * @param hArg Handle to an argument. Must NOT be null.
8191
 * @since 3.11
8192
 */
8193
bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
8194
0
{
8195
0
    VALIDATE_POINTER1(hArg, __func__, false);
8196
0
    return hArg->ptr->IsRequired();
8197
0
}
8198
8199
/************************************************************************/
8200
/*                    GDALAlgorithmArgGetMinCount()                     */
8201
/************************************************************************/
8202
8203
/** Return the minimum number of values for the argument.
8204
 *
8205
 * Defaults to 0.
8206
 * Only applies to list type of arguments.
8207
 *
8208
 * @param hArg Handle to an argument. Must NOT be null.
8209
 * @since 3.11
8210
 */
8211
int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
8212
0
{
8213
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8214
0
    return hArg->ptr->GetMinCount();
8215
0
}
8216
8217
/************************************************************************/
8218
/*                    GDALAlgorithmArgGetMaxCount()                     */
8219
/************************************************************************/
8220
8221
/** Return the maximum number of values for the argument.
8222
 *
8223
 * Defaults to 1 for scalar types, and INT_MAX for list types.
8224
 * Only applies to list type of arguments.
8225
 *
8226
 * @param hArg Handle to an argument. Must NOT be null.
8227
 * @since 3.11
8228
 */
8229
int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8230
0
{
8231
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8232
0
    return hArg->ptr->GetMaxCount();
8233
0
}
8234
8235
/************************************************************************/
8236
/*               GDALAlgorithmArgGetPackedValuesAllowed()               */
8237
/************************************************************************/
8238
8239
/** Return whether, for list type of arguments, several values, space
8240
 * separated, may be specified. That is "--foo=bar,baz".
8241
 * The default is true.
8242
 *
8243
 * @param hArg Handle to an argument. Must NOT be null.
8244
 * @since 3.11
8245
 */
8246
bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8247
0
{
8248
0
    VALIDATE_POINTER1(hArg, __func__, false);
8249
0
    return hArg->ptr->GetPackedValuesAllowed();
8250
0
}
8251
8252
/************************************************************************/
8253
/*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
8254
/************************************************************************/
8255
8256
/** Return whether, for list type of arguments, the argument may be
8257
 * repeated. That is "--foo=bar --foo=baz".
8258
 * The default is true.
8259
 *
8260
 * @param hArg Handle to an argument. Must NOT be null.
8261
 * @since 3.11
8262
 */
8263
bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8264
0
{
8265
0
    VALIDATE_POINTER1(hArg, __func__, false);
8266
0
    return hArg->ptr->GetRepeatedArgAllowed();
8267
0
}
8268
8269
/************************************************************************/
8270
/*                     GDALAlgorithmArgGetChoices()                     */
8271
/************************************************************************/
8272
8273
/** Return the allowed values (as strings) for the argument.
8274
 *
8275
 * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8276
 *
8277
 * @param hArg Handle to an argument. Must NOT be null.
8278
 * @return a NULL terminated list of names, which must be destroyed with
8279
 * CSLDestroy()
8280
8281
 * @since 3.11
8282
 */
8283
char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8284
0
{
8285
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8286
0
    return CPLStringList(hArg->ptr->GetChoices()).StealList();
8287
0
}
8288
8289
/************************************************************************/
8290
/*                  GDALAlgorithmArgGetMetadataItem()                   */
8291
/************************************************************************/
8292
8293
/** Return the values of the metadata item of an argument.
8294
 *
8295
 * @param hArg Handle to an argument. Must NOT be null.
8296
 * @param pszItem Name of the item. Must NOT be null.
8297
 * @return a NULL terminated list of values, which must be destroyed with
8298
 * CSLDestroy()
8299
8300
 * @since 3.11
8301
 */
8302
char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8303
                                       const char *pszItem)
8304
0
{
8305
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8306
0
    VALIDATE_POINTER1(pszItem, __func__, nullptr);
8307
0
    const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8308
0
    return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8309
0
}
8310
8311
/************************************************************************/
8312
/*                  GDALAlgorithmArgIsExplicitlySet()                   */
8313
/************************************************************************/
8314
8315
/** Return whether the argument value has been explicitly set with Set()
8316
 *
8317
 * @param hArg Handle to an argument. Must NOT be null.
8318
 * @since 3.11
8319
 */
8320
bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8321
0
{
8322
0
    VALIDATE_POINTER1(hArg, __func__, false);
8323
0
    return hArg->ptr->IsExplicitlySet();
8324
0
}
8325
8326
/************************************************************************/
8327
/*                  GDALAlgorithmArgHasDefaultValue()                   */
8328
/************************************************************************/
8329
8330
/** Return if the argument has a declared default value.
8331
 *
8332
 * @param hArg Handle to an argument. Must NOT be null.
8333
 * @since 3.11
8334
 */
8335
bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8336
0
{
8337
0
    VALIDATE_POINTER1(hArg, __func__, false);
8338
0
    return hArg->ptr->HasDefaultValue();
8339
0
}
8340
8341
/************************************************************************/
8342
/*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
8343
/************************************************************************/
8344
8345
/** Return the argument default value as a integer.
8346
 *
8347
 * Must only be called on arguments whose type is GAAT_BOOLEAN
8348
 *
8349
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8350
 * argument has a default value.
8351
 *
8352
 * @param hArg Handle to an argument. Must NOT be null.
8353
 * @since 3.12
8354
 */
8355
bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8356
0
{
8357
0
    VALIDATE_POINTER1(hArg, __func__, false);
8358
0
    if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8359
0
    {
8360
0
        CPLError(CE_Failure, CPLE_AppDefined,
8361
0
                 "%s must only be called on arguments of type GAAT_BOOLEAN",
8362
0
                 __func__);
8363
0
        return false;
8364
0
    }
8365
0
    return hArg->ptr->GetDefault<bool>();
8366
0
}
8367
8368
/************************************************************************/
8369
/*                 GDALAlgorithmArgGetDefaultAsString()                 */
8370
/************************************************************************/
8371
8372
/** Return the argument default value as a string.
8373
 *
8374
 * Must only be called on arguments whose type is GAAT_STRING.
8375
 *
8376
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8377
 * argument has a default value.
8378
 *
8379
 * @param hArg Handle to an argument. Must NOT be null.
8380
 * @return string whose lifetime is bound to hArg and which must not
8381
 * be freed.
8382
 * @since 3.11
8383
 */
8384
const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8385
0
{
8386
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8387
0
    if (hArg->ptr->GetType() != GAAT_STRING)
8388
0
    {
8389
0
        CPLError(CE_Failure, CPLE_AppDefined,
8390
0
                 "%s must only be called on arguments of type GAAT_STRING",
8391
0
                 __func__);
8392
0
        return nullptr;
8393
0
    }
8394
0
    return hArg->ptr->GetDefault<std::string>().c_str();
8395
0
}
8396
8397
/************************************************************************/
8398
/*                GDALAlgorithmArgGetDefaultAsInteger()                 */
8399
/************************************************************************/
8400
8401
/** Return the argument default value as a integer.
8402
 *
8403
 * Must only be called on arguments whose type is GAAT_INTEGER
8404
 *
8405
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8406
 * argument has a default value.
8407
 *
8408
 * @param hArg Handle to an argument. Must NOT be null.
8409
 * @since 3.12
8410
 */
8411
int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8412
0
{
8413
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8414
0
    if (hArg->ptr->GetType() != GAAT_INTEGER)
8415
0
    {
8416
0
        CPLError(CE_Failure, CPLE_AppDefined,
8417
0
                 "%s must only be called on arguments of type GAAT_INTEGER",
8418
0
                 __func__);
8419
0
        return 0;
8420
0
    }
8421
0
    return hArg->ptr->GetDefault<int>();
8422
0
}
8423
8424
/************************************************************************/
8425
/*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
8426
/************************************************************************/
8427
8428
/** Return the argument default value as a double.
8429
 *
8430
 * Must only be called on arguments whose type is GAAT_REAL
8431
 *
8432
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8433
 * argument has a default value.
8434
 *
8435
 * @param hArg Handle to an argument. Must NOT be null.
8436
 * @since 3.12
8437
 */
8438
double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8439
0
{
8440
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8441
0
    if (hArg->ptr->GetType() != GAAT_REAL)
8442
0
    {
8443
0
        CPLError(CE_Failure, CPLE_AppDefined,
8444
0
                 "%s must only be called on arguments of type GAAT_REAL",
8445
0
                 __func__);
8446
0
        return 0;
8447
0
    }
8448
0
    return hArg->ptr->GetDefault<double>();
8449
0
}
8450
8451
/************************************************************************/
8452
/*               GDALAlgorithmArgGetDefaultAsStringList()               */
8453
/************************************************************************/
8454
8455
/** Return the argument default value as a string list.
8456
 *
8457
 * Must only be called on arguments whose type is GAAT_STRING_LIST.
8458
 *
8459
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8460
 * argument has a default value.
8461
 *
8462
 * @param hArg Handle to an argument. Must NOT be null.
8463
 * @return a NULL terminated list of names, which must be destroyed with
8464
 * CSLDestroy()
8465
8466
 * @since 3.12
8467
 */
8468
char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8469
0
{
8470
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8471
0
    if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8472
0
    {
8473
0
        CPLError(CE_Failure, CPLE_AppDefined,
8474
0
                 "%s must only be called on arguments of type GAAT_STRING_LIST",
8475
0
                 __func__);
8476
0
        return nullptr;
8477
0
    }
8478
0
    return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8479
0
        .StealList();
8480
0
}
8481
8482
/************************************************************************/
8483
/*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
8484
/************************************************************************/
8485
8486
/** Return the argument default value as a integer list.
8487
 *
8488
 * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8489
 *
8490
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8491
 * argument has a default value.
8492
 *
8493
 * @param hArg Handle to an argument. Must NOT be null.
8494
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8495
 * @since 3.12
8496
 */
8497
const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8498
                                                   size_t *pnCount)
8499
0
{
8500
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8501
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
8502
0
    if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8503
0
    {
8504
0
        CPLError(
8505
0
            CE_Failure, CPLE_AppDefined,
8506
0
            "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8507
0
            __func__);
8508
0
        *pnCount = 0;
8509
0
        return nullptr;
8510
0
    }
8511
0
    const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8512
0
    *pnCount = val.size();
8513
0
    return val.data();
8514
0
}
8515
8516
/************************************************************************/
8517
/*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
8518
/************************************************************************/
8519
8520
/** Return the argument default value as a real list.
8521
 *
8522
 * Must only be called on arguments whose type is GAAT_REAL_LIST.
8523
 *
8524
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8525
 * argument has a default value.
8526
 *
8527
 * @param hArg Handle to an argument. Must NOT be null.
8528
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8529
 * @since 3.12
8530
 */
8531
const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8532
                                                     size_t *pnCount)
8533
0
{
8534
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8535
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
8536
0
    if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8537
0
    {
8538
0
        CPLError(CE_Failure, CPLE_AppDefined,
8539
0
                 "%s must only be called on arguments of type GAAT_REAL_LIST",
8540
0
                 __func__);
8541
0
        *pnCount = 0;
8542
0
        return nullptr;
8543
0
    }
8544
0
    const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8545
0
    *pnCount = val.size();
8546
0
    return val.data();
8547
0
}
8548
8549
/************************************************************************/
8550
/*                      GDALAlgorithmArgIsHidden()                      */
8551
/************************************************************************/
8552
8553
/** Return whether the argument is hidden (for GDAL internal use)
8554
 *
8555
 * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8556
 * GDALAlgorithmArgIsHiddenForAPI().
8557
 *
8558
 * @param hArg Handle to an argument. Must NOT be null.
8559
 * @since 3.12
8560
 */
8561
bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8562
0
{
8563
0
    VALIDATE_POINTER1(hArg, __func__, false);
8564
0
    return hArg->ptr->IsHidden();
8565
0
}
8566
8567
/************************************************************************/
8568
/*                   GDALAlgorithmArgIsHiddenForCLI()                   */
8569
/************************************************************************/
8570
8571
/** Return whether the argument must not be mentioned in CLI usage.
8572
 *
8573
 * For example, "output-value" for "gdal raster info", which is only
8574
 * meant when the algorithm is used from a non-CLI context.
8575
 *
8576
 * @param hArg Handle to an argument. Must NOT be null.
8577
 * @since 3.11
8578
 */
8579
bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8580
0
{
8581
0
    VALIDATE_POINTER1(hArg, __func__, false);
8582
0
    return hArg->ptr->IsHiddenForCLI();
8583
0
}
8584
8585
/************************************************************************/
8586
/*                   GDALAlgorithmArgIsHiddenForAPI()                   */
8587
/************************************************************************/
8588
8589
/** Return whether the argument must not be mentioned in the context of an
8590
 * API use.
8591
 * Said otherwise, if it is only for CLI usage.
8592
 *
8593
 * For example "--help"
8594
 *
8595
 * @param hArg Handle to an argument. Must NOT be null.
8596
 * @since 3.12
8597
 */
8598
bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8599
0
{
8600
0
    VALIDATE_POINTER1(hArg, __func__, false);
8601
0
    return hArg->ptr->IsHiddenForAPI();
8602
0
}
8603
8604
/************************************************************************/
8605
/*                    GDALAlgorithmArgIsOnlyForCLI()                    */
8606
/************************************************************************/
8607
8608
/** Return whether the argument must not be mentioned in the context of an
8609
 * API use.
8610
 * Said otherwise, if it is only for CLI usage.
8611
 *
8612
 * For example "--help"
8613
 *
8614
 * @param hArg Handle to an argument. Must NOT be null.
8615
 * @since 3.11
8616
 * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8617
 */
8618
bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8619
0
{
8620
0
    VALIDATE_POINTER1(hArg, __func__, false);
8621
0
    return hArg->ptr->IsHiddenForAPI();
8622
0
}
8623
8624
/************************************************************************/
8625
/*             GDALAlgorithmArgIsAvailableInPipelineStep()              */
8626
/************************************************************************/
8627
8628
/** Return whether the argument is available in a pipeline step.
8629
 *
8630
 * If false, it is only available in standalone mode.
8631
 *
8632
 * @param hArg Handle to an argument. Must NOT be null.
8633
 * @since 3.13
8634
 */
8635
bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8636
0
{
8637
0
    VALIDATE_POINTER1(hArg, __func__, false);
8638
0
    return hArg->ptr->IsAvailableInPipelineStep();
8639
0
}
8640
8641
/************************************************************************/
8642
/*                      GDALAlgorithmArgIsInput()                       */
8643
/************************************************************************/
8644
8645
/** Indicate whether the value of the argument is read-only during the
8646
 * execution of the algorithm.
8647
 *
8648
 * Default is true.
8649
 *
8650
 * @param hArg Handle to an argument. Must NOT be null.
8651
 * @since 3.11
8652
 */
8653
bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8654
0
{
8655
0
    VALIDATE_POINTER1(hArg, __func__, false);
8656
0
    return hArg->ptr->IsInput();
8657
0
}
8658
8659
/************************************************************************/
8660
/*                      GDALAlgorithmArgIsOutput()                      */
8661
/************************************************************************/
8662
8663
/** Return whether (at least part of) the value of the argument is set
8664
 * during the execution of the algorithm.
8665
 *
8666
 * For example, "output-value" for "gdal raster info"
8667
 * Default is false.
8668
 * An argument may return both IsInput() and IsOutput() as true.
8669
 * For example the "gdal raster convert" algorithm consumes the dataset
8670
 * name of its "output" argument, and sets the dataset object during its
8671
 * execution.
8672
 *
8673
 * @param hArg Handle to an argument. Must NOT be null.
8674
 * @since 3.11
8675
 */
8676
bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8677
0
{
8678
0
    VALIDATE_POINTER1(hArg, __func__, false);
8679
0
    return hArg->ptr->IsOutput();
8680
0
}
8681
8682
/************************************************************************/
8683
/*                   GDALAlgorithmArgGetDatasetType()                   */
8684
/************************************************************************/
8685
8686
/** Get which type of dataset is allowed / generated.
8687
 *
8688
 * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8689
 * GDAL_OF_MULTIDIM_RASTER.
8690
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8691
 *
8692
 * @param hArg Handle to an argument. Must NOT be null.
8693
 * @since 3.11
8694
 */
8695
GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8696
0
{
8697
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8698
0
    return hArg->ptr->GetDatasetType();
8699
0
}
8700
8701
/************************************************************************/
8702
/*                GDALAlgorithmArgGetDatasetInputFlags()                */
8703
/************************************************************************/
8704
8705
/** Indicates which components among name and dataset are accepted as
8706
 * input, when this argument serves as an input.
8707
 *
8708
 * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8709
 * input.
8710
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
8711
 * accepted as input.
8712
 * If both bits are set, the algorithm can accept either a name or a dataset
8713
 * object.
8714
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8715
 *
8716
 * @param hArg Handle to an argument. Must NOT be null.
8717
 * @return string whose lifetime is bound to hAlg and which must not
8718
 * be freed.
8719
 * @since 3.11
8720
 */
8721
int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8722
0
{
8723
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8724
0
    return hArg->ptr->GetDatasetInputFlags();
8725
0
}
8726
8727
/************************************************************************/
8728
/*               GDALAlgorithmArgGetDatasetOutputFlags()                */
8729
/************************************************************************/
8730
8731
/** Indicates which components among name and dataset are modified,
8732
 * when this argument serves as an output.
8733
 *
8734
 * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8735
 * output (that is the algorithm will generate the name. Rarely used).
8736
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
8737
 * generated as output, and available for use after the algorithm has
8738
 * completed.
8739
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8740
 *
8741
 * @param hArg Handle to an argument. Must NOT be null.
8742
 * @return string whose lifetime is bound to hAlg and which must not
8743
 * be freed.
8744
 * @since 3.11
8745
 */
8746
int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8747
0
{
8748
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8749
0
    return hArg->ptr->GetDatasetOutputFlags();
8750
0
}
8751
8752
/************************************************************************/
8753
/*              GDALAlgorithmArgGetMutualExclusionGroup()               */
8754
/************************************************************************/
8755
8756
/** Return the name of the mutual exclusion group to which this argument
8757
 * belongs to.
8758
 *
8759
 * Or empty string if it does not belong to any exclusion group.
8760
 *
8761
 * @param hArg Handle to an argument. Must NOT be null.
8762
 * @return string whose lifetime is bound to hArg and which must not
8763
 * be freed.
8764
 * @since 3.11
8765
 */
8766
const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8767
0
{
8768
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8769
0
    return hArg->ptr->GetMutualExclusionGroup().c_str();
8770
0
}
8771
8772
/************************************************************************/
8773
/*              GDALAlgorithmArgGetMutualDependencyGroup()              */
8774
/************************************************************************/
8775
8776
/** Return the name of the mutual dependency group to which this argument
8777
 * belongs to.
8778
 *
8779
 * Or empty string if it does not belong to any dependency group.
8780
 *
8781
 * @param hArg Handle to an argument. Must NOT be null.
8782
 * @return string whose lifetime is bound to hArg and which must not
8783
 * be freed.
8784
 * @since 3.13
8785
 */
8786
const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8787
0
{
8788
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8789
0
    return hArg->ptr->GetMutualDependencyGroup().c_str();
8790
0
}
8791
8792
/************************************************************************/
8793
/*               GDALAlgorithmArgGetDirectDependencies()                */
8794
/************************************************************************/
8795
8796
/** Return the list of names of arguments that this argument depends on.
8797
 *
8798
 *  This is not necessarily a symmetric relationship.
8799
 *  If argument A depends on argument B, it doesn't mean that B depends on A.
8800
 *  Mutual dependency groups are a special case of dependencies,
8801
 *  where all arguments of the group depend on each other and are not
8802
 *  returned by this method.
8803
 *
8804
 * @param hArg Handle to an argument. Must NOT be null.
8805
 * @return a NULL terminated list of names, which must be destroyed with
8806
 * CSLDestroy()
8807
 * @since 3.13
8808
 */
8809
char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8810
0
{
8811
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8812
0
    return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8813
0
}
8814
8815
/************************************************************************/
8816
/*                    GDALAlgorithmArgGetAsBoolean()                    */
8817
/************************************************************************/
8818
8819
/** Return the argument value as a boolean.
8820
 *
8821
 * Must only be called on arguments whose type is GAAT_BOOLEAN.
8822
 *
8823
 * @param hArg Handle to an argument. Must NOT be null.
8824
 * @since 3.11
8825
 */
8826
bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8827
0
{
8828
0
    VALIDATE_POINTER1(hArg, __func__, false);
8829
0
    if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8830
0
    {
8831
0
        CPLError(CE_Failure, CPLE_AppDefined,
8832
0
                 "%s must only be called on arguments of type GAAT_BOOLEAN",
8833
0
                 __func__);
8834
0
        return false;
8835
0
    }
8836
0
    return hArg->ptr->Get<bool>();
8837
0
}
8838
8839
/************************************************************************/
8840
/*                    GDALAlgorithmArgGetAsString()                     */
8841
/************************************************************************/
8842
8843
/** Return the argument value as a string.
8844
 *
8845
 * Must only be called on arguments whose type is GAAT_STRING.
8846
 *
8847
 * @param hArg Handle to an argument. Must NOT be null.
8848
 * @return string whose lifetime is bound to hArg and which must not
8849
 * be freed.
8850
 * @since 3.11
8851
 */
8852
const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8853
0
{
8854
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8855
0
    if (hArg->ptr->GetType() != GAAT_STRING)
8856
0
    {
8857
0
        CPLError(CE_Failure, CPLE_AppDefined,
8858
0
                 "%s must only be called on arguments of type GAAT_STRING",
8859
0
                 __func__);
8860
0
        return nullptr;
8861
0
    }
8862
0
    return hArg->ptr->Get<std::string>().c_str();
8863
0
}
8864
8865
/************************************************************************/
8866
/*                 GDALAlgorithmArgGetAsDatasetValue()                  */
8867
/************************************************************************/
8868
8869
/** Return the argument value as a GDALArgDatasetValueH.
8870
 *
8871
 * Must only be called on arguments whose type is GAAT_DATASET
8872
 *
8873
 * @param hArg Handle to an argument. Must NOT be null.
8874
 * @return handle to a GDALArgDatasetValue that must be released with
8875
 * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8876
 * the one of hArg.
8877
 * @since 3.11
8878
 */
8879
GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8880
0
{
8881
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8882
0
    if (hArg->ptr->GetType() != GAAT_DATASET)
8883
0
    {
8884
0
        CPLError(CE_Failure, CPLE_AppDefined,
8885
0
                 "%s must only be called on arguments of type GAAT_DATASET",
8886
0
                 __func__);
8887
0
        return nullptr;
8888
0
    }
8889
0
    return std::make_unique<GDALArgDatasetValueHS>(
8890
0
               &(hArg->ptr->Get<GDALArgDatasetValue>()))
8891
0
        .release();
8892
0
}
8893
8894
/************************************************************************/
8895
/*                    GDALAlgorithmArgGetAsInteger()                    */
8896
/************************************************************************/
8897
8898
/** Return the argument value as a integer.
8899
 *
8900
 * Must only be called on arguments whose type is GAAT_INTEGER
8901
 *
8902
 * @param hArg Handle to an argument. Must NOT be null.
8903
 * @since 3.11
8904
 */
8905
int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8906
0
{
8907
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8908
0
    if (hArg->ptr->GetType() != GAAT_INTEGER)
8909
0
    {
8910
0
        CPLError(CE_Failure, CPLE_AppDefined,
8911
0
                 "%s must only be called on arguments of type GAAT_INTEGER",
8912
0
                 __func__);
8913
0
        return 0;
8914
0
    }
8915
0
    return hArg->ptr->Get<int>();
8916
0
}
8917
8918
/************************************************************************/
8919
/*                    GDALAlgorithmArgGetAsDouble()                     */
8920
/************************************************************************/
8921
8922
/** Return the argument value as a double.
8923
 *
8924
 * Must only be called on arguments whose type is GAAT_REAL
8925
 *
8926
 * @param hArg Handle to an argument. Must NOT be null.
8927
 * @since 3.11
8928
 */
8929
double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8930
0
{
8931
0
    VALIDATE_POINTER1(hArg, __func__, 0);
8932
0
    if (hArg->ptr->GetType() != GAAT_REAL)
8933
0
    {
8934
0
        CPLError(CE_Failure, CPLE_AppDefined,
8935
0
                 "%s must only be called on arguments of type GAAT_REAL",
8936
0
                 __func__);
8937
0
        return 0;
8938
0
    }
8939
0
    return hArg->ptr->Get<double>();
8940
0
}
8941
8942
/************************************************************************/
8943
/*                  GDALAlgorithmArgGetAsStringList()                   */
8944
/************************************************************************/
8945
8946
/** Return the argument value as a string list.
8947
 *
8948
 * Must only be called on arguments whose type is GAAT_STRING_LIST.
8949
 *
8950
 * @param hArg Handle to an argument. Must NOT be null.
8951
 * @return a NULL terminated list of names, which must be destroyed with
8952
 * CSLDestroy()
8953
8954
 * @since 3.11
8955
 */
8956
char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8957
0
{
8958
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8959
0
    if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8960
0
    {
8961
0
        CPLError(CE_Failure, CPLE_AppDefined,
8962
0
                 "%s must only be called on arguments of type GAAT_STRING_LIST",
8963
0
                 __func__);
8964
0
        return nullptr;
8965
0
    }
8966
0
    return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8967
0
        .StealList();
8968
0
}
8969
8970
/************************************************************************/
8971
/*                  GDALAlgorithmArgGetAsIntegerList()                  */
8972
/************************************************************************/
8973
8974
/** Return the argument value as a integer list.
8975
 *
8976
 * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8977
 *
8978
 * @param hArg Handle to an argument. Must NOT be null.
8979
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8980
 * @since 3.11
8981
 */
8982
const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8983
                                            size_t *pnCount)
8984
0
{
8985
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
8986
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
8987
0
    if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8988
0
    {
8989
0
        CPLError(
8990
0
            CE_Failure, CPLE_AppDefined,
8991
0
            "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8992
0
            __func__);
8993
0
        *pnCount = 0;
8994
0
        return nullptr;
8995
0
    }
8996
0
    const auto &val = hArg->ptr->Get<std::vector<int>>();
8997
0
    *pnCount = val.size();
8998
0
    return val.data();
8999
0
}
9000
9001
/************************************************************************/
9002
/*                  GDALAlgorithmArgGetAsDoubleList()                   */
9003
/************************************************************************/
9004
9005
/** Return the argument value as a real list.
9006
 *
9007
 * Must only be called on arguments whose type is GAAT_REAL_LIST.
9008
 *
9009
 * @param hArg Handle to an argument. Must NOT be null.
9010
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
9011
 * @since 3.11
9012
 */
9013
const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
9014
                                              size_t *pnCount)
9015
0
{
9016
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
9017
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
9018
0
    if (hArg->ptr->GetType() != GAAT_REAL_LIST)
9019
0
    {
9020
0
        CPLError(CE_Failure, CPLE_AppDefined,
9021
0
                 "%s must only be called on arguments of type GAAT_REAL_LIST",
9022
0
                 __func__);
9023
0
        *pnCount = 0;
9024
0
        return nullptr;
9025
0
    }
9026
0
    const auto &val = hArg->ptr->Get<std::vector<double>>();
9027
0
    *pnCount = val.size();
9028
0
    return val.data();
9029
0
}
9030
9031
/************************************************************************/
9032
/*                    GDALAlgorithmArgSetAsBoolean()                    */
9033
/************************************************************************/
9034
9035
/** Set the value for a GAAT_BOOLEAN argument.
9036
 *
9037
 * It cannot be called several times for a given argument.
9038
 * Validation checks and other actions are run.
9039
 *
9040
 * @param hArg Handle to an argument. Must NOT be null.
9041
 * @param value value.
9042
 * @return true if success.
9043
 * @since 3.11
9044
 */
9045
9046
bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
9047
0
{
9048
0
    VALIDATE_POINTER1(hArg, __func__, false);
9049
0
    return hArg->ptr->Set(value);
9050
0
}
9051
9052
/************************************************************************/
9053
/*                    GDALAlgorithmArgSetAsString()                     */
9054
/************************************************************************/
9055
9056
/** Set the value for a GAAT_STRING argument.
9057
 *
9058
 * It cannot be called several times for a given argument.
9059
 * Validation checks and other actions are run.
9060
 *
9061
 * @param hArg Handle to an argument. Must NOT be null.
9062
 * @param value value (may be null)
9063
 * @return true if success.
9064
 * @since 3.11
9065
 */
9066
9067
bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
9068
0
{
9069
0
    VALIDATE_POINTER1(hArg, __func__, false);
9070
0
    return hArg->ptr->Set(value ? value : "");
9071
0
}
9072
9073
/************************************************************************/
9074
/*                    GDALAlgorithmArgSetAsInteger()                    */
9075
/************************************************************************/
9076
9077
/** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
9078
 *
9079
 * It cannot be called several times for a given argument.
9080
 * Validation checks and other actions are run.
9081
 *
9082
 * @param hArg Handle to an argument. Must NOT be null.
9083
 * @param value value.
9084
 * @return true if success.
9085
 * @since 3.11
9086
 */
9087
9088
bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
9089
0
{
9090
0
    VALIDATE_POINTER1(hArg, __func__, false);
9091
0
    return hArg->ptr->Set(value);
9092
0
}
9093
9094
/************************************************************************/
9095
/*                    GDALAlgorithmArgSetAsDouble()                     */
9096
/************************************************************************/
9097
9098
/** Set the value for a GAAT_REAL argument.
9099
 *
9100
 * It cannot be called several times for a given argument.
9101
 * Validation checks and other actions are run.
9102
 *
9103
 * @param hArg Handle to an argument. Must NOT be null.
9104
 * @param value value.
9105
 * @return true if success.
9106
 * @since 3.11
9107
 */
9108
9109
bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
9110
0
{
9111
0
    VALIDATE_POINTER1(hArg, __func__, false);
9112
0
    return hArg->ptr->Set(value);
9113
0
}
9114
9115
/************************************************************************/
9116
/*                 GDALAlgorithmArgSetAsDatasetValue()                  */
9117
/************************************************************************/
9118
9119
/** Set the value for a GAAT_DATASET argument.
9120
 *
9121
 * It cannot be called several times for a given argument.
9122
 * Validation checks and other actions are run.
9123
 *
9124
 * @param hArg Handle to an argument. Must NOT be null.
9125
 * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
9126
 * @return true if success.
9127
 * @since 3.11
9128
 */
9129
bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
9130
                                       GDALArgDatasetValueH value)
9131
0
{
9132
0
    VALIDATE_POINTER1(hArg, __func__, false);
9133
0
    VALIDATE_POINTER1(value, __func__, false);
9134
0
    return hArg->ptr->SetFrom(*(value->ptr));
9135
0
}
9136
9137
/************************************************************************/
9138
/*                     GDALAlgorithmArgSetDataset()                     */
9139
/************************************************************************/
9140
9141
/** Set dataset object, increasing its reference counter.
9142
 *
9143
 * @param hArg Handle to an argument. Must NOT be null.
9144
 * @param hDS Dataset object. May be null.
9145
 * @return true if success.
9146
 * @since 3.11
9147
 */
9148
9149
bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
9150
0
{
9151
0
    VALIDATE_POINTER1(hArg, __func__, false);
9152
0
    return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
9153
0
}
9154
9155
/************************************************************************/
9156
/*                  GDALAlgorithmArgSetAsStringList()                   */
9157
/************************************************************************/
9158
9159
/** Set the value for a GAAT_STRING_LIST argument.
9160
 *
9161
 * It cannot be called several times for a given argument.
9162
 * Validation checks and other actions are run.
9163
 *
9164
 * @param hArg Handle to an argument. Must NOT be null.
9165
 * @param value value as a NULL terminated list (may be null)
9166
 * @return true if success.
9167
 * @since 3.11
9168
 */
9169
9170
bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
9171
0
{
9172
0
    VALIDATE_POINTER1(hArg, __func__, false);
9173
0
    return hArg->ptr->Set(
9174
0
        static_cast<std::vector<std::string>>(CPLStringList(value)));
9175
0
}
9176
9177
/************************************************************************/
9178
/*                  GDALAlgorithmArgSetAsIntegerList()                  */
9179
/************************************************************************/
9180
9181
/** Set the value for a GAAT_INTEGER_LIST argument.
9182
 *
9183
 * It cannot be called several times for a given argument.
9184
 * Validation checks and other actions are run.
9185
 *
9186
 * @param hArg Handle to an argument. Must NOT be null.
9187
 * @param nCount Number of values in pnValues.
9188
 * @param pnValues Pointer to an array of integer values of size nCount.
9189
 * @return true if success.
9190
 * @since 3.11
9191
 */
9192
bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
9193
                                      const int *pnValues)
9194
0
{
9195
0
    VALIDATE_POINTER1(hArg, __func__, false);
9196
0
    return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
9197
0
}
9198
9199
/************************************************************************/
9200
/*                  GDALAlgorithmArgSetAsDoubleList()                   */
9201
/************************************************************************/
9202
9203
/** Set the value for a GAAT_REAL_LIST argument.
9204
 *
9205
 * It cannot be called several times for a given argument.
9206
 * Validation checks and other actions are run.
9207
 *
9208
 * @param hArg Handle to an argument. Must NOT be null.
9209
 * @param nCount Number of values in pnValues.
9210
 * @param pnValues Pointer to an array of double values of size nCount.
9211
 * @return true if success.
9212
 * @since 3.11
9213
 */
9214
bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
9215
                                     const double *pnValues)
9216
0
{
9217
0
    VALIDATE_POINTER1(hArg, __func__, false);
9218
0
    return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
9219
0
}
9220
9221
/************************************************************************/
9222
/*                    GDALAlgorithmArgSetDatasets()                     */
9223
/************************************************************************/
9224
9225
/** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9226
 *
9227
 * @param hArg Handle to an argument. Must NOT be null.
9228
 * @param nCount Number of values in pnValues.
9229
 * @param pahDS Pointer to an array of dataset of size nCount.
9230
 * @return true if success.
9231
 * @since 3.11
9232
 */
9233
9234
bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9235
                                 GDALDatasetH *pahDS)
9236
0
{
9237
0
    VALIDATE_POINTER1(hArg, __func__, false);
9238
0
    std::vector<GDALArgDatasetValue> values;
9239
0
    for (size_t i = 0; i < nCount; ++i)
9240
0
    {
9241
0
        values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9242
0
    }
9243
0
    return hArg->ptr->Set(std::move(values));
9244
0
}
9245
9246
/************************************************************************/
9247
/*                  GDALAlgorithmArgSetDatasetNames()                   */
9248
/************************************************************************/
9249
9250
/** Set dataset names to a GAAT_DATASET_LIST argument.
9251
 *
9252
 * @param hArg Handle to an argument. Must NOT be null.
9253
 * @param names Dataset names as a NULL terminated list (may be null)
9254
 * @return true if success.
9255
 * @since 3.11
9256
 */
9257
9258
bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9259
0
{
9260
0
    VALIDATE_POINTER1(hArg, __func__, false);
9261
0
    std::vector<GDALArgDatasetValue> values;
9262
0
    for (size_t i = 0; names[i]; ++i)
9263
0
    {
9264
0
        values.emplace_back(names[i]);
9265
0
    }
9266
0
    return hArg->ptr->Set(std::move(values));
9267
0
}
9268
9269
/************************************************************************/
9270
/*                     GDALArgDatasetValueCreate()                      */
9271
/************************************************************************/
9272
9273
/** Instantiate an empty GDALArgDatasetValue
9274
 *
9275
 * @return new handle to free with GDALArgDatasetValueRelease()
9276
 * @since 3.11
9277
 */
9278
GDALArgDatasetValueH GDALArgDatasetValueCreate()
9279
0
{
9280
0
    return std::make_unique<GDALArgDatasetValueHS>().release();
9281
0
}
9282
9283
/************************************************************************/
9284
/*                     GDALArgDatasetValueRelease()                     */
9285
/************************************************************************/
9286
9287
/** Release a handle to a GDALArgDatasetValue
9288
 *
9289
 * @since 3.11
9290
 */
9291
void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9292
0
{
9293
0
    delete hValue;
9294
0
}
9295
9296
/************************************************************************/
9297
/*                     GDALArgDatasetValueGetName()                     */
9298
/************************************************************************/
9299
9300
/** Return the name component of the GDALArgDatasetValue
9301
 *
9302
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9303
 * @return string whose lifetime is bound to hAlg and which must not
9304
 * be freed.
9305
 * @since 3.11
9306
 */
9307
const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9308
0
{
9309
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
9310
0
    return hValue->ptr->GetName().c_str();
9311
0
}
9312
9313
/************************************************************************/
9314
/*                  GDALArgDatasetValueGetDatasetRef()                  */
9315
/************************************************************************/
9316
9317
/** Return the dataset component of the GDALArgDatasetValue.
9318
 *
9319
 * This does not modify the reference counter, hence the lifetime of the
9320
 * returned object is not guaranteed to exceed the one of hValue.
9321
 *
9322
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9323
 * @since 3.11
9324
 */
9325
GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9326
0
{
9327
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
9328
0
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9329
0
}
9330
9331
/************************************************************************/
9332
/*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
9333
/************************************************************************/
9334
9335
/** Return the dataset component of the GDALArgDatasetValue, and increase its
9336
 * reference count if not null. Once done with the dataset, the caller should
9337
 * call GDALReleaseDataset().
9338
 *
9339
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9340
 * @since 3.11
9341
 */
9342
GDALDatasetH
9343
GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9344
0
{
9345
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
9346
0
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9347
0
}
9348
9349
/************************************************************************/
9350
/*                     GDALArgDatasetValueSetName()                     */
9351
/************************************************************************/
9352
9353
/** Set dataset name
9354
 *
9355
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9356
 * @param pszName Dataset name. May be null.
9357
 * @since 3.11
9358
 */
9359
9360
void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9361
                                const char *pszName)
9362
0
{
9363
0
    VALIDATE_POINTER0(hValue, __func__);
9364
0
    hValue->ptr->Set(pszName ? pszName : "");
9365
0
}
9366
9367
/************************************************************************/
9368
/*                   GDALArgDatasetValueSetDataset()                    */
9369
/************************************************************************/
9370
9371
/** Set dataset object, increasing its reference counter.
9372
 *
9373
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9374
 * @param hDS Dataset object. May be null.
9375
 * @since 3.11
9376
 */
9377
9378
void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9379
                                   GDALDatasetH hDS)
9380
0
{
9381
0
    VALIDATE_POINTER0(hValue, __func__);
9382
0
    hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9383
0
}