Coverage Report

Created: 2025-08-11 09:23

/src/gdal/gcore/gdalalgorithm.cpp
Line
Count
Source (jump to first uncovered line)
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_error.h"
16
#include "cpl_json.h"
17
#include "cpl_levenshtein.h"
18
#include "cpl_minixml.h"
19
#include "cpl_multiproc.h"
20
21
#include "gdalalgorithm.h"
22
#include "gdal_priv.h"
23
#include "ogrsf_frmts.h"
24
#include "ogr_spatialref.h"
25
#include "vrtdataset.h"
26
27
#include <algorithm>
28
#include <cassert>
29
#include <cerrno>
30
#include <cmath>
31
#include <cstdlib>
32
#include <limits>
33
#include <map>
34
#include <string_view>
35
36
#ifndef _
37
93.1k
#define _(x) (x)
38
#endif
39
40
constexpr const char *GDAL_ARG_NAME_INPUT_FORMAT = "input-format";
41
42
constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
43
44
constexpr const char *GDAL_ARG_NAME_OPEN_OPTION = "open-option";
45
46
constexpr const char *GDAL_ARG_NAME_BAND = "band";
47
48
//! @cond Doxygen_Suppress
49
struct GDALAlgorithmArgHS
50
{
51
    GDALAlgorithmArg *ptr = nullptr;
52
53
0
    explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
54
0
    {
55
0
    }
56
};
57
58
//! @endcond
59
60
//! @cond Doxygen_Suppress
61
struct GDALArgDatasetValueHS
62
{
63
    GDALArgDatasetValue val{};
64
    GDALArgDatasetValue *ptr = nullptr;
65
66
0
    GDALArgDatasetValueHS() : ptr(&val)
67
0
    {
68
0
    }
69
70
0
    explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
71
0
    {
72
0
    }
73
74
    GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
75
    GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
76
};
77
78
//! @endcond
79
80
/************************************************************************/
81
/*                     GDALAlgorithmArgTypeIsList()                     */
82
/************************************************************************/
83
84
bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
85
119k
{
86
119k
    switch (type)
87
119k
    {
88
59.9k
        case GAAT_BOOLEAN:
89
93.3k
        case GAAT_STRING:
90
93.3k
        case GAAT_INTEGER:
91
93.3k
        case GAAT_REAL:
92
99.9k
        case GAAT_DATASET:
93
99.9k
            break;
94
95
20.0k
        case GAAT_STRING_LIST:
96
20.0k
        case GAAT_INTEGER_LIST:
97
20.0k
        case GAAT_REAL_LIST:
98
20.0k
        case GAAT_DATASET_LIST:
99
20.0k
            return true;
100
119k
    }
101
102
99.9k
    return false;
103
119k
}
104
105
/************************************************************************/
106
/*                     GDALAlgorithmArgTypeName()                       */
107
/************************************************************************/
108
109
const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
110
0
{
111
0
    switch (type)
112
0
    {
113
0
        case GAAT_BOOLEAN:
114
0
            break;
115
0
        case GAAT_STRING:
116
0
            return "string";
117
0
        case GAAT_INTEGER:
118
0
            return "integer";
119
0
        case GAAT_REAL:
120
0
            return "real";
121
0
        case GAAT_DATASET:
122
0
            return "dataset";
123
0
        case GAAT_STRING_LIST:
124
0
            return "string_list";
125
0
        case GAAT_INTEGER_LIST:
126
0
            return "integer_list";
127
0
        case GAAT_REAL_LIST:
128
0
            return "real_list";
129
0
        case GAAT_DATASET_LIST:
130
0
            return "dataset_list";
131
0
    }
132
133
0
    return "boolean";
134
0
}
135
136
/************************************************************************/
137
/*                     GDALAlgorithmArgDatasetTypeName()                */
138
/************************************************************************/
139
140
std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
141
6.61k
{
142
6.61k
    std::string ret;
143
6.61k
    if ((type & GDAL_OF_RASTER) != 0)
144
6.61k
        ret = "raster";
145
6.61k
    if ((type & GDAL_OF_VECTOR) != 0)
146
6.61k
    {
147
6.61k
        if (!ret.empty())
148
6.61k
        {
149
6.61k
            if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
150
0
                ret += ", ";
151
6.61k
            else
152
6.61k
                ret += " or ";
153
6.61k
        }
154
6.61k
        ret += "vector";
155
6.61k
    }
156
6.61k
    if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
157
0
    {
158
0
        if (!ret.empty())
159
0
        {
160
0
            ret += " or ";
161
0
        }
162
0
        ret += "multidimensional raster";
163
0
    }
164
6.61k
    return ret;
165
6.61k
}
166
167
/************************************************************************/
168
/*                     GDALAlgorithmArgDecl()                           */
169
/************************************************************************/
170
171
// cppcheck-suppress uninitMemberVar
172
GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
173
                                           char chShortName,
174
                                           const std::string &description,
175
                                           GDALAlgorithmArgType type)
176
119k
    : m_longName(longName),
177
119k
      m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
178
119k
      m_description(description), m_type(type),
179
119k
      m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
180
119k
                    .toupper()),
181
119k
      m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
182
119k
{
183
119k
    if (m_type == GAAT_BOOLEAN)
184
59.9k
    {
185
59.9k
        m_defaultValue = false;
186
59.9k
    }
187
119k
}
188
189
/************************************************************************/
190
/*               GDALAlgorithmArgDecl::SetMinCount()                    */
191
/************************************************************************/
192
193
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
194
2
{
195
2
    if (!GDALAlgorithmArgTypeIsList(m_type))
196
0
    {
197
0
        CPLError(CE_Failure, CPLE_NotSupported,
198
0
                 "SetMinCount() illegal on scalar argument '%s'",
199
0
                 GetName().c_str());
200
0
    }
201
2
    else
202
2
    {
203
2
        m_minCount = count;
204
2
    }
205
2
    return *this;
206
2
}
207
208
/************************************************************************/
209
/*               GDALAlgorithmArgDecl::SetMaxCount()                    */
210
/************************************************************************/
211
212
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
213
2
{
214
2
    if (!GDALAlgorithmArgTypeIsList(m_type))
215
0
    {
216
0
        CPLError(CE_Failure, CPLE_NotSupported,
217
0
                 "SetMaxCount() illegal on scalar argument '%s'",
218
0
                 GetName().c_str());
219
0
    }
220
2
    else
221
2
    {
222
2
        m_maxCount = count;
223
2
    }
224
2
    return *this;
225
2
}
226
227
/************************************************************************/
228
/*                 GDALAlgorithmArg::~GDALAlgorithmArg()                */
229
/************************************************************************/
230
231
119k
GDALAlgorithmArg::~GDALAlgorithmArg() = default;
232
233
/************************************************************************/
234
/*                         GDALAlgorithmArg::Set()                      */
235
/************************************************************************/
236
237
bool GDALAlgorithmArg::Set(bool value)
238
2.03k
{
239
2.03k
    if (m_decl.GetType() != GAAT_BOOLEAN)
240
0
    {
241
0
        CPLError(
242
0
            CE_Failure, CPLE_AppDefined,
243
0
            "Calling Set(bool) on argument '%s' of type %s is not supported",
244
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
245
0
        return false;
246
0
    }
247
2.03k
    return SetInternal(value);
248
2.03k
}
249
250
bool GDALAlgorithmArg::ProcessString(std::string &value) const
251
2.45k
{
252
2.45k
    if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
253
2.45k
        value.front() == '@')
254
0
    {
255
0
        GByte *pabyData = nullptr;
256
0
        if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
257
0
                          10 * 1024 * 1024))
258
0
        {
259
            // Remove UTF-8 BOM
260
0
            size_t offset = 0;
261
0
            if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
262
0
                pabyData[2] == 0xBF)
263
0
            {
264
0
                offset = 3;
265
0
            }
266
0
            value = reinterpret_cast<const char *>(pabyData + offset);
267
0
            VSIFree(pabyData);
268
0
        }
269
0
        else
270
0
        {
271
0
            return false;
272
0
        }
273
0
    }
274
275
2.45k
    if (m_decl.IsRemoveSQLCommentsEnabled())
276
0
        value = CPLRemoveSQLComments(value);
277
278
2.45k
    return true;
279
2.45k
}
280
281
bool GDALAlgorithmArg::Set(const std::string &value)
282
12.5k
{
283
12.5k
    switch (m_decl.GetType())
284
12.5k
    {
285
0
        case GAAT_BOOLEAN:
286
0
            if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
287
0
                EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
288
0
            {
289
0
                return Set(true);
290
0
            }
291
0
            else if (EQUAL(value.c_str(), "0") ||
292
0
                     EQUAL(value.c_str(), "FALSE") ||
293
0
                     EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
294
0
            {
295
0
                return Set(false);
296
0
            }
297
0
            break;
298
299
0
        case GAAT_INTEGER:
300
0
        case GAAT_INTEGER_LIST:
301
0
        {
302
0
            errno = 0;
303
0
            char *endptr = nullptr;
304
0
            const auto v = std::strtoll(value.c_str(), &endptr, 10);
305
0
            if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
306
0
                endptr == value.c_str() + value.size())
307
0
            {
308
0
                if (m_decl.GetType() == GAAT_INTEGER)
309
0
                    return Set(static_cast<int>(v));
310
0
                else
311
0
                    return Set(std::vector<int>{static_cast<int>(v)});
312
0
            }
313
0
            break;
314
0
        }
315
316
0
        case GAAT_REAL:
317
0
        case GAAT_REAL_LIST:
318
0
        {
319
0
            char *endptr = nullptr;
320
0
            const double v = CPLStrtod(value.c_str(), &endptr);
321
0
            if (endptr == value.c_str() + value.size())
322
0
            {
323
0
                if (m_decl.GetType() == GAAT_REAL)
324
0
                    return Set(v);
325
0
                else
326
0
                    return Set(std::vector<double>{v});
327
0
            }
328
0
            break;
329
0
        }
330
331
2.45k
        case GAAT_STRING:
332
2.45k
            break;
333
334
890
        case GAAT_STRING_LIST:
335
890
            return Set(std::vector<std::string>{value});
336
337
9.21k
        case GAAT_DATASET:
338
9.21k
            return SetDatasetName(value);
339
340
0
        case GAAT_DATASET_LIST:
341
0
        {
342
0
            std::vector<GDALArgDatasetValue> v;
343
0
            v.resize(1);
344
0
            v[0].Set(value);
345
0
            return Set(std::move(v));
346
0
        }
347
12.5k
    }
348
349
2.45k
    if (m_decl.GetType() != GAAT_STRING)
350
0
    {
351
0
        CPLError(CE_Failure, CPLE_AppDefined,
352
0
                 "Calling Set(std::string) on argument '%s' of type %s is not "
353
0
                 "supported",
354
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
355
0
        return false;
356
0
    }
357
358
2.45k
    std::string newValue(value);
359
2.45k
    return ProcessString(newValue) && SetInternal(newValue);
360
2.45k
}
361
362
bool GDALAlgorithmArg::Set(int value)
363
0
{
364
0
    if (m_decl.GetType() == GAAT_BOOLEAN)
365
0
    {
366
0
        if (value == 1)
367
0
            return Set(true);
368
0
        else if (value == 0)
369
0
            return Set(false);
370
0
    }
371
0
    else if (m_decl.GetType() == GAAT_REAL)
372
0
    {
373
0
        return Set(static_cast<double>(value));
374
0
    }
375
0
    else if (m_decl.GetType() == GAAT_STRING)
376
0
    {
377
0
        return Set(std::to_string(value));
378
0
    }
379
0
    else if (m_decl.GetType() == GAAT_INTEGER_LIST)
380
0
    {
381
0
        return Set(std::vector<int>{value});
382
0
    }
383
0
    else if (m_decl.GetType() == GAAT_REAL_LIST)
384
0
    {
385
0
        return Set(std::vector<double>{static_cast<double>(value)});
386
0
    }
387
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
388
0
    {
389
0
        return Set(std::vector<std::string>{std::to_string(value)});
390
0
    }
391
392
0
    if (m_decl.GetType() != GAAT_INTEGER)
393
0
    {
394
0
        CPLError(
395
0
            CE_Failure, CPLE_AppDefined,
396
0
            "Calling Set(int) on argument '%s' of type %s is not supported",
397
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
398
0
        return false;
399
0
    }
400
0
    return SetInternal(value);
401
0
}
402
403
bool GDALAlgorithmArg::Set(double value)
404
0
{
405
0
    if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
406
0
        value <= INT_MAX && static_cast<int>(value) == value)
407
0
    {
408
0
        return Set(static_cast<int>(value));
409
0
    }
410
0
    else if (m_decl.GetType() == GAAT_STRING)
411
0
    {
412
0
        return Set(std::to_string(value));
413
0
    }
414
0
    else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
415
0
             value <= INT_MAX && static_cast<int>(value) == value)
416
0
    {
417
0
        return Set(std::vector<int>{static_cast<int>(value)});
418
0
    }
419
0
    else if (m_decl.GetType() == GAAT_REAL_LIST)
420
0
    {
421
0
        return Set(std::vector<double>{value});
422
0
    }
423
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
424
0
    {
425
0
        return Set(std::vector<std::string>{std::to_string(value)});
426
0
    }
427
0
    else if (m_decl.GetType() != GAAT_REAL)
428
0
    {
429
0
        CPLError(
430
0
            CE_Failure, CPLE_AppDefined,
431
0
            "Calling Set(double) on argument '%s' of type %s is not supported",
432
0
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
433
0
        return false;
434
0
    }
435
0
    return SetInternal(value);
436
0
}
437
438
static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
439
0
{
440
0
    if (arg->GetDatasetInputFlags() == GADV_NAME &&
441
0
        arg->GetDatasetOutputFlags() == GADV_OBJECT)
442
0
    {
443
0
        CPLError(
444
0
            CE_Failure, CPLE_AppDefined,
445
0
            "Dataset object '%s' is created by algorithm and cannot be set "
446
0
            "as an input.",
447
0
            arg->GetName().c_str());
448
0
        return false;
449
0
    }
450
0
    else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
451
0
    {
452
0
        CPLError(CE_Failure, CPLE_AppDefined,
453
0
                 "A dataset cannot be set as an input argument of '%s'.",
454
0
                 arg->GetName().c_str());
455
0
        return false;
456
0
    }
457
458
0
    return true;
459
0
}
460
461
bool GDALAlgorithmArg::Set(GDALDataset *ds)
462
0
{
463
0
    if (m_decl.GetType() != GAAT_DATASET)
464
0
    {
465
0
        CPLError(CE_Failure, CPLE_AppDefined,
466
0
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
467
0
                 "is not supported",
468
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
469
0
        return false;
470
0
    }
471
0
    if (!CheckCanSetDatasetObject(this))
472
0
        return false;
473
0
    m_explicitlySet = true;
474
0
    auto &val = *std::get<GDALArgDatasetValue *>(m_value);
475
0
    val.Set(ds);
476
0
    return RunAllActions();
477
0
}
478
479
bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
480
0
{
481
0
    if (m_decl.GetType() != GAAT_DATASET)
482
0
    {
483
0
        CPLError(CE_Failure, CPLE_AppDefined,
484
0
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
485
0
                 "is not supported",
486
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
487
0
        return false;
488
0
    }
489
0
    if (!CheckCanSetDatasetObject(this))
490
0
        return false;
491
0
    m_explicitlySet = true;
492
0
    auto &val = *std::get<GDALArgDatasetValue *>(m_value);
493
0
    val.Set(std::move(ds));
494
0
    return RunAllActions();
495
0
}
496
497
bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
498
9.21k
{
499
9.21k
    if (m_decl.GetType() != GAAT_DATASET)
500
0
    {
501
0
        CPLError(CE_Failure, CPLE_AppDefined,
502
0
                 "Calling SetDatasetName() on argument '%s' of type %s is "
503
0
                 "not supported",
504
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
505
0
        return false;
506
0
    }
507
9.21k
    m_explicitlySet = true;
508
9.21k
    std::get<GDALArgDatasetValue *>(m_value)->Set(name);
509
9.21k
    return RunAllActions();
510
9.21k
}
511
512
bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
513
0
{
514
0
    if (m_decl.GetType() != GAAT_DATASET)
515
0
    {
516
0
        CPLError(CE_Failure, CPLE_AppDefined,
517
0
                 "Calling SetFrom() on argument '%s' of type %s is "
518
0
                 "not supported",
519
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
520
0
        return false;
521
0
    }
522
0
    if (!CheckCanSetDatasetObject(this))
523
0
        return false;
524
0
    m_explicitlySet = true;
525
0
    std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
526
0
    return RunAllActions();
527
0
}
528
529
bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
530
890
{
531
890
    if (m_decl.GetType() == GAAT_INTEGER_LIST)
532
0
    {
533
0
        std::vector<int> v_i;
534
0
        for (const std::string &s : value)
535
0
        {
536
0
            errno = 0;
537
0
            char *endptr = nullptr;
538
0
            const auto v = std::strtoll(s.c_str(), &endptr, 10);
539
0
            if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
540
0
                endptr == s.c_str() + s.size())
541
0
            {
542
0
                v_i.push_back(static_cast<int>(v));
543
0
            }
544
0
            else
545
0
            {
546
0
                break;
547
0
            }
548
0
        }
549
0
        if (v_i.size() == value.size())
550
0
            return Set(v_i);
551
0
    }
552
890
    else if (m_decl.GetType() == GAAT_REAL_LIST)
553
0
    {
554
0
        std::vector<double> v_d;
555
0
        for (const std::string &s : value)
556
0
        {
557
0
            char *endptr = nullptr;
558
0
            const double v = CPLStrtod(s.c_str(), &endptr);
559
0
            if (endptr == s.c_str() + s.size())
560
0
            {
561
0
                v_d.push_back(v);
562
0
            }
563
0
            else
564
0
            {
565
0
                break;
566
0
            }
567
0
        }
568
0
        if (v_d.size() == value.size())
569
0
            return Set(v_d);
570
0
    }
571
890
    else if ((m_decl.GetType() == GAAT_INTEGER ||
572
890
              m_decl.GetType() == GAAT_REAL ||
573
890
              m_decl.GetType() == GAAT_STRING) &&
574
890
             value.size() == 1)
575
0
    {
576
0
        return Set(value[0]);
577
0
    }
578
890
    else if (m_decl.GetType() == GAAT_DATASET_LIST)
579
0
    {
580
0
        std::vector<GDALArgDatasetValue> dsVector;
581
0
        for (const std::string &s : value)
582
0
            dsVector.emplace_back(s);
583
0
        return Set(std::move(dsVector));
584
0
    }
585
586
890
    if (m_decl.GetType() != GAAT_STRING_LIST)
587
0
    {
588
0
        CPLError(CE_Failure, CPLE_AppDefined,
589
0
                 "Calling Set(const std::vector<std::string> &) on argument "
590
0
                 "'%s' of type %s is not supported",
591
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
592
0
        return false;
593
0
    }
594
595
890
    if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
596
890
        m_decl.IsRemoveSQLCommentsEnabled())
597
0
    {
598
0
        std::vector<std::string> newValue(value);
599
0
        for (auto &s : newValue)
600
0
        {
601
0
            if (!ProcessString(s))
602
0
                return false;
603
0
        }
604
0
        return SetInternal(newValue);
605
0
    }
606
890
    else
607
890
    {
608
890
        return SetInternal(value);
609
890
    }
610
890
}
611
612
bool GDALAlgorithmArg::Set(const std::vector<int> &value)
613
0
{
614
0
    if (m_decl.GetType() == GAAT_REAL_LIST)
615
0
    {
616
0
        std::vector<double> v_d;
617
0
        for (int i : value)
618
0
            v_d.push_back(i);
619
0
        return Set(v_d);
620
0
    }
621
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
622
0
    {
623
0
        std::vector<std::string> v_s;
624
0
        for (int i : value)
625
0
            v_s.push_back(std::to_string(i));
626
0
        return Set(v_s);
627
0
    }
628
0
    else if ((m_decl.GetType() == GAAT_INTEGER ||
629
0
              m_decl.GetType() == GAAT_REAL ||
630
0
              m_decl.GetType() == GAAT_STRING) &&
631
0
             value.size() == 1)
632
0
    {
633
0
        return Set(value[0]);
634
0
    }
635
636
0
    if (m_decl.GetType() != GAAT_INTEGER_LIST)
637
0
    {
638
0
        CPLError(CE_Failure, CPLE_AppDefined,
639
0
                 "Calling Set(const std::vector<int> &) on argument '%s' of "
640
0
                 "type %s is not supported",
641
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
642
0
        return false;
643
0
    }
644
0
    return SetInternal(value);
645
0
}
646
647
bool GDALAlgorithmArg::Set(const std::vector<double> &value)
648
0
{
649
0
    if (m_decl.GetType() == GAAT_INTEGER_LIST)
650
0
    {
651
0
        std::vector<int> v_i;
652
0
        for (double d : value)
653
0
        {
654
0
            if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
655
0
            {
656
0
                v_i.push_back(static_cast<int>(d));
657
0
            }
658
0
            else
659
0
            {
660
0
                break;
661
0
            }
662
0
        }
663
0
        if (v_i.size() == value.size())
664
0
            return Set(v_i);
665
0
    }
666
0
    else if (m_decl.GetType() == GAAT_STRING_LIST)
667
0
    {
668
0
        std::vector<std::string> v_s;
669
0
        for (double d : value)
670
0
            v_s.push_back(std::to_string(d));
671
0
        return Set(v_s);
672
0
    }
673
0
    else if ((m_decl.GetType() == GAAT_INTEGER ||
674
0
              m_decl.GetType() == GAAT_REAL ||
675
0
              m_decl.GetType() == GAAT_STRING) &&
676
0
             value.size() == 1)
677
0
    {
678
0
        return Set(value[0]);
679
0
    }
680
681
0
    if (m_decl.GetType() != GAAT_REAL_LIST)
682
0
    {
683
0
        CPLError(CE_Failure, CPLE_AppDefined,
684
0
                 "Calling Set(const std::vector<double> &) on argument '%s' of "
685
0
                 "type %s is not supported",
686
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
687
0
        return false;
688
0
    }
689
0
    return SetInternal(value);
690
0
}
691
692
bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
693
0
{
694
0
    if (m_decl.GetType() != GAAT_DATASET_LIST)
695
0
    {
696
0
        CPLError(CE_Failure, CPLE_AppDefined,
697
0
                 "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
698
0
                 "argument '%s' of type %s is not supported",
699
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
700
0
        return false;
701
0
    }
702
0
    m_explicitlySet = true;
703
0
    *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
704
0
    return RunAllActions();
705
0
}
706
707
GDALAlgorithmArg &
708
GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
709
0
{
710
0
    Set(std::move(value));
711
0
    return *this;
712
0
}
713
714
bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
715
0
{
716
0
    const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
717
0
    return Set(value.exportToWkt(apszOptions));
718
0
}
719
720
bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
721
0
{
722
0
    if (m_decl.GetType() != other.GetType())
723
0
    {
724
0
        CPLError(CE_Failure, CPLE_AppDefined,
725
0
                 "Calling SetFrom() on argument '%s' of type %s whereas "
726
0
                 "other argument type is %s is not supported",
727
0
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
728
0
                 GDALAlgorithmArgTypeName(other.GetType()));
729
0
        return false;
730
0
    }
731
732
0
    switch (m_decl.GetType())
733
0
    {
734
0
        case GAAT_BOOLEAN:
735
0
            *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
736
0
            break;
737
0
        case GAAT_STRING:
738
0
            *std::get<std::string *>(m_value) =
739
0
                *std::get<std::string *>(other.m_value);
740
0
            break;
741
0
        case GAAT_INTEGER:
742
0
            *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
743
0
            break;
744
0
        case GAAT_REAL:
745
0
            *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
746
0
            break;
747
0
        case GAAT_DATASET:
748
0
            return SetFrom(other.Get<GDALArgDatasetValue>());
749
0
        case GAAT_STRING_LIST:
750
0
            *std::get<std::vector<std::string> *>(m_value) =
751
0
                *std::get<std::vector<std::string> *>(other.m_value);
752
0
            break;
753
0
        case GAAT_INTEGER_LIST:
754
0
            *std::get<std::vector<int> *>(m_value) =
755
0
                *std::get<std::vector<int> *>(other.m_value);
756
0
            break;
757
0
        case GAAT_REAL_LIST:
758
0
            *std::get<std::vector<double> *>(m_value) =
759
0
                *std::get<std::vector<double> *>(other.m_value);
760
0
            break;
761
0
        case GAAT_DATASET_LIST:
762
0
        {
763
0
            std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
764
0
            for (const auto &val :
765
0
                 *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
766
0
            {
767
0
                GDALArgDatasetValue v;
768
0
                v.SetFrom(val);
769
0
                std::get<std::vector<GDALArgDatasetValue> *>(m_value)
770
0
                    ->push_back(std::move(v));
771
0
            }
772
0
            break;
773
0
        }
774
0
    }
775
0
    m_explicitlySet = true;
776
0
    return RunAllActions();
777
0
}
778
779
/************************************************************************/
780
/*                  GDALAlgorithmArg::RunAllActions()                   */
781
/************************************************************************/
782
783
bool GDALAlgorithmArg::RunAllActions()
784
14.5k
{
785
14.5k
    if (!RunValidationActions())
786
1.51k
        return false;
787
13.0k
    RunActions();
788
13.0k
    return true;
789
14.5k
}
790
791
/************************************************************************/
792
/*                      GDALAlgorithmArg::RunActions()                  */
793
/************************************************************************/
794
795
void GDALAlgorithmArg::RunActions()
796
13.0k
{
797
13.0k
    for (const auto &f : m_actions)
798
2.92k
        f();
799
13.0k
}
800
801
/************************************************************************/
802
/*                    GDALAlgorithmArg::ValidateChoice()                */
803
/************************************************************************/
804
805
// Returns the canonical value if matching a valid choice, or empty string
806
// otherwise.
807
std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
808
2.21k
{
809
2.21k
    for (const std::string &choice : GetChoices())
810
3.72k
    {
811
3.72k
        if (EQUAL(value.c_str(), choice.c_str()))
812
701
        {
813
701
            return choice;
814
701
        }
815
3.72k
    }
816
817
1.51k
    for (const std::string &choice : GetHiddenChoices())
818
0
    {
819
0
        if (EQUAL(value.c_str(), choice.c_str()))
820
0
        {
821
0
            return choice;
822
0
        }
823
0
    }
824
825
1.51k
    std::string expected;
826
1.51k
    for (const auto &choice : GetChoices())
827
3.02k
    {
828
3.02k
        if (!expected.empty())
829
1.51k
            expected += ", ";
830
3.02k
        expected += '\'';
831
3.02k
        expected += choice;
832
3.02k
        expected += '\'';
833
3.02k
    }
834
1.51k
    if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
835
0
    {
836
0
        return "?";
837
0
    }
838
1.51k
    CPLError(CE_Failure, CPLE_IllegalArg,
839
1.51k
             "Invalid value '%s' for string argument '%s'. Should be "
840
1.51k
             "one among %s.",
841
1.51k
             value.c_str(), GetName().c_str(), expected.c_str());
842
1.51k
    return std::string();
843
1.51k
}
844
845
/************************************************************************/
846
/*                   GDALAlgorithmArg::ValidateIntRange()               */
847
/************************************************************************/
848
849
bool GDALAlgorithmArg::ValidateIntRange(int val) const
850
0
{
851
0
    bool ret = true;
852
853
0
    const auto [minVal, minValIsIncluded] = GetMinValue();
854
0
    if (!std::isnan(minVal))
855
0
    {
856
0
        if (minValIsIncluded && val < minVal)
857
0
        {
858
0
            CPLError(CE_Failure, CPLE_IllegalArg,
859
0
                     "Value of argument '%s' is %d, but should be >= %d",
860
0
                     GetName().c_str(), val, static_cast<int>(minVal));
861
0
            ret = false;
862
0
        }
863
0
        else if (!minValIsIncluded && val <= minVal)
864
0
        {
865
0
            CPLError(CE_Failure, CPLE_IllegalArg,
866
0
                     "Value of argument '%s' is %d, but should be > %d",
867
0
                     GetName().c_str(), val, static_cast<int>(minVal));
868
0
            ret = false;
869
0
        }
870
0
    }
871
872
0
    const auto [maxVal, maxValIsIncluded] = GetMaxValue();
873
0
    if (!std::isnan(maxVal))
874
0
    {
875
876
0
        if (maxValIsIncluded && val > maxVal)
877
0
        {
878
0
            CPLError(CE_Failure, CPLE_IllegalArg,
879
0
                     "Value of argument '%s' is %d, but should be <= %d",
880
0
                     GetName().c_str(), val, static_cast<int>(maxVal));
881
0
            ret = false;
882
0
        }
883
0
        else if (!maxValIsIncluded && val >= maxVal)
884
0
        {
885
0
            CPLError(CE_Failure, CPLE_IllegalArg,
886
0
                     "Value of argument '%s' is %d, but should be < %d",
887
0
                     GetName().c_str(), val, static_cast<int>(maxVal));
888
0
            ret = false;
889
0
        }
890
0
    }
891
892
0
    return ret;
893
0
}
894
895
/************************************************************************/
896
/*                   GDALAlgorithmArg::ValidateRealRange()              */
897
/************************************************************************/
898
899
bool GDALAlgorithmArg::ValidateRealRange(double val) const
900
0
{
901
0
    bool ret = true;
902
903
0
    const auto [minVal, minValIsIncluded] = GetMinValue();
904
0
    if (!std::isnan(minVal))
905
0
    {
906
0
        if (minValIsIncluded && !(val >= minVal))
907
0
        {
908
0
            CPLError(CE_Failure, CPLE_IllegalArg,
909
0
                     "Value of argument '%s' is %g, but should be >= %g",
910
0
                     GetName().c_str(), val, minVal);
911
0
            ret = false;
912
0
        }
913
0
        else if (!minValIsIncluded && !(val > minVal))
914
0
        {
915
0
            CPLError(CE_Failure, CPLE_IllegalArg,
916
0
                     "Value of argument '%s' is %g, but should be > %g",
917
0
                     GetName().c_str(), val, minVal);
918
0
            ret = false;
919
0
        }
920
0
    }
921
922
0
    const auto [maxVal, maxValIsIncluded] = GetMaxValue();
923
0
    if (!std::isnan(maxVal))
924
0
    {
925
926
0
        if (maxValIsIncluded && !(val <= maxVal))
927
0
        {
928
0
            CPLError(CE_Failure, CPLE_IllegalArg,
929
0
                     "Value of argument '%s' is %g, but should be <= %g",
930
0
                     GetName().c_str(), val, maxVal);
931
0
            ret = false;
932
0
        }
933
0
        else if (!maxValIsIncluded && !(val < maxVal))
934
0
        {
935
0
            CPLError(CE_Failure, CPLE_IllegalArg,
936
0
                     "Value of argument '%s' is %g, but should be < %g",
937
0
                     GetName().c_str(), val, maxVal);
938
0
            ret = false;
939
0
        }
940
0
    }
941
942
0
    return ret;
943
0
}
944
945
/************************************************************************/
946
/*                    GDALAlgorithmArg::RunValidationActions()          */
947
/************************************************************************/
948
949
bool GDALAlgorithmArg::RunValidationActions()
950
14.5k
{
951
14.5k
    bool ret = true;
952
953
14.5k
    if (GetType() == GAAT_STRING && !GetChoices().empty())
954
2.21k
    {
955
2.21k
        auto &val = Get<std::string>();
956
2.21k
        std::string validVal = ValidateChoice(val);
957
2.21k
        if (validVal.empty())
958
1.51k
            ret = false;
959
701
        else
960
701
            val = std::move(validVal);
961
2.21k
    }
962
12.3k
    else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
963
0
    {
964
0
        auto &values = Get<std::vector<std::string>>();
965
0
        for (std::string &val : values)
966
0
        {
967
0
            std::string validVal = ValidateChoice(val);
968
0
            if (validVal.empty())
969
0
                ret = false;
970
0
            else
971
0
                val = std::move(validVal);
972
0
        }
973
0
    }
974
975
14.5k
    if (GetType() == GAAT_STRING)
976
2.45k
    {
977
2.45k
        const int nMinCharCount = GetMinCharCount();
978
2.45k
        if (nMinCharCount > 0)
979
0
        {
980
0
            const auto &val = Get<std::string>();
981
0
            if (val.size() < static_cast<size_t>(nMinCharCount))
982
0
            {
983
0
                CPLError(
984
0
                    CE_Failure, CPLE_IllegalArg,
985
0
                    "Value of argument '%s' is '%s', but should have at least "
986
0
                    "%d character(s)",
987
0
                    GetName().c_str(), val.c_str(), nMinCharCount);
988
0
                ret = false;
989
0
            }
990
0
        }
991
2.45k
    }
992
12.1k
    else if (GetType() == GAAT_STRING_LIST)
993
890
    {
994
890
        const int nMinCharCount = GetMinCharCount();
995
890
        if (nMinCharCount > 0)
996
0
        {
997
0
            for (const auto &val : Get<std::vector<std::string>>())
998
0
            {
999
0
                if (val.size() < static_cast<size_t>(nMinCharCount))
1000
0
                {
1001
0
                    CPLError(
1002
0
                        CE_Failure, CPLE_IllegalArg,
1003
0
                        "Value of argument '%s' is '%s', but should have at "
1004
0
                        "least %d character(s)",
1005
0
                        GetName().c_str(), val.c_str(), nMinCharCount);
1006
0
                    ret = false;
1007
0
                }
1008
0
            }
1009
0
        }
1010
890
    }
1011
11.2k
    else if (GetType() == GAAT_INTEGER)
1012
0
    {
1013
0
        ret = ValidateIntRange(Get<int>()) && ret;
1014
0
    }
1015
11.2k
    else if (GetType() == GAAT_INTEGER_LIST)
1016
0
    {
1017
0
        for (int v : Get<std::vector<int>>())
1018
0
            ret = ValidateIntRange(v) && ret;
1019
0
    }
1020
11.2k
    else if (GetType() == GAAT_REAL)
1021
0
    {
1022
0
        ret = ValidateRealRange(Get<double>()) && ret;
1023
0
    }
1024
11.2k
    else if (GetType() == GAAT_REAL_LIST)
1025
0
    {
1026
0
        for (double v : Get<std::vector<double>>())
1027
0
            ret = ValidateRealRange(v) && ret;
1028
0
    }
1029
1030
14.5k
    for (const auto &f : m_validationActions)
1031
2.21k
    {
1032
2.21k
        if (!f())
1033
0
            ret = false;
1034
2.21k
    }
1035
1036
14.5k
    return ret;
1037
14.5k
}
1038
1039
/************************************************************************/
1040
/*                    GDALAlgorithmArg::Serialize()                     */
1041
/************************************************************************/
1042
1043
bool GDALAlgorithmArg::Serialize(std::string &serializedArg) const
1044
0
{
1045
0
    serializedArg.clear();
1046
1047
0
    if (!IsExplicitlySet())
1048
0
    {
1049
0
        return false;
1050
0
    }
1051
1052
0
    std::string ret = "--";
1053
0
    ret += GetName();
1054
0
    if (GetType() == GAAT_BOOLEAN)
1055
0
    {
1056
0
        serializedArg = std::move(ret);
1057
0
        return true;
1058
0
    }
1059
1060
0
    const auto AppendString = [&ret](const std::string &str)
1061
0
    {
1062
0
        if (str.find('"') != std::string::npos ||
1063
0
            str.find(' ') != std::string::npos ||
1064
0
            str.find('\\') != std::string::npos ||
1065
0
            str.find(',') != std::string::npos)
1066
0
        {
1067
0
            ret += '"';
1068
0
            ret +=
1069
0
                CPLString(str).replaceAll('\\', "\\\\").replaceAll('"', "\\\"");
1070
0
            ret += '"';
1071
0
        }
1072
0
        else
1073
0
        {
1074
0
            ret += str;
1075
0
        }
1076
0
    };
1077
1078
0
    const auto AddListValueSeparator = [this, &ret]()
1079
0
    {
1080
0
        if (GetPackedValuesAllowed())
1081
0
        {
1082
0
            ret += ',';
1083
0
        }
1084
0
        else
1085
0
        {
1086
0
            ret += " --";
1087
0
            ret += GetName();
1088
0
            ret += ' ';
1089
0
        }
1090
0
    };
1091
1092
0
    ret += ' ';
1093
0
    switch (GetType())
1094
0
    {
1095
0
        case GAAT_BOOLEAN:
1096
0
            break;
1097
0
        case GAAT_STRING:
1098
0
        {
1099
0
            const auto &val = Get<std::string>();
1100
0
            AppendString(val);
1101
0
            break;
1102
0
        }
1103
0
        case GAAT_INTEGER:
1104
0
        {
1105
0
            ret += CPLSPrintf("%d", Get<int>());
1106
0
            break;
1107
0
        }
1108
0
        case GAAT_REAL:
1109
0
        {
1110
0
            ret += CPLSPrintf("%.17g", Get<double>());
1111
0
            break;
1112
0
        }
1113
0
        case GAAT_DATASET:
1114
0
        {
1115
0
            const auto &val = Get<GDALArgDatasetValue>();
1116
0
            const auto &str = val.GetName();
1117
0
            if (str.empty())
1118
0
            {
1119
0
                return false;
1120
0
            }
1121
0
            AppendString(str);
1122
0
            break;
1123
0
        }
1124
0
        case GAAT_STRING_LIST:
1125
0
        {
1126
0
            const auto &vals = Get<std::vector<std::string>>();
1127
0
            for (size_t i = 0; i < vals.size(); ++i)
1128
0
            {
1129
0
                if (i > 0)
1130
0
                    AddListValueSeparator();
1131
0
                AppendString(vals[i]);
1132
0
            }
1133
0
            break;
1134
0
        }
1135
0
        case GAAT_INTEGER_LIST:
1136
0
        {
1137
0
            const auto &vals = Get<std::vector<int>>();
1138
0
            for (size_t i = 0; i < vals.size(); ++i)
1139
0
            {
1140
0
                if (i > 0)
1141
0
                    AddListValueSeparator();
1142
0
                ret += CPLSPrintf("%d", vals[i]);
1143
0
            }
1144
0
            break;
1145
0
        }
1146
0
        case GAAT_REAL_LIST:
1147
0
        {
1148
0
            const auto &vals = Get<std::vector<double>>();
1149
0
            for (size_t i = 0; i < vals.size(); ++i)
1150
0
            {
1151
0
                if (i > 0)
1152
0
                    AddListValueSeparator();
1153
0
                ret += CPLSPrintf("%.17g", vals[i]);
1154
0
            }
1155
0
            break;
1156
0
        }
1157
0
        case GAAT_DATASET_LIST:
1158
0
        {
1159
0
            const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1160
0
            for (size_t i = 0; i < vals.size(); ++i)
1161
0
            {
1162
0
                if (i > 0)
1163
0
                    AddListValueSeparator();
1164
0
                const auto &val = vals[i];
1165
0
                const auto &str = val.GetName();
1166
0
                if (str.empty())
1167
0
                {
1168
0
                    return false;
1169
0
                }
1170
0
                AppendString(str);
1171
0
            }
1172
0
            break;
1173
0
        }
1174
0
    }
1175
1176
0
    serializedArg = std::move(ret);
1177
0
    return true;
1178
0
}
1179
1180
/************************************************************************/
1181
/*                   ~GDALInConstructionAlgorithmArg()                  */
1182
/************************************************************************/
1183
1184
GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1185
1186
/************************************************************************/
1187
/*              GDALInConstructionAlgorithmArg::AddAlias()              */
1188
/************************************************************************/
1189
1190
GDALInConstructionAlgorithmArg &
1191
GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1192
13.2k
{
1193
13.2k
    m_decl.AddAlias(alias);
1194
13.2k
    if (m_owner)
1195
13.2k
        m_owner->AddAliasFor(this, alias);
1196
13.2k
    return *this;
1197
13.2k
}
1198
1199
/************************************************************************/
1200
/*            GDALInConstructionAlgorithmArg::AddHiddenAlias()          */
1201
/************************************************************************/
1202
1203
GDALInConstructionAlgorithmArg &
1204
GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1205
2
{
1206
2
    m_decl.AddHiddenAlias(alias);
1207
2
    if (m_owner)
1208
2
        m_owner->AddAliasFor(this, alias);
1209
2
    return *this;
1210
2
}
1211
1212
/************************************************************************/
1213
/*           GDALInConstructionAlgorithmArg::AddShortNameAlias()        */
1214
/************************************************************************/
1215
1216
GDALInConstructionAlgorithmArg &
1217
GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1218
0
{
1219
0
    m_decl.AddShortNameAlias(shortNameAlias);
1220
0
    if (m_owner)
1221
0
        m_owner->AddShortNameAliasFor(this, shortNameAlias);
1222
0
    return *this;
1223
0
}
1224
1225
/************************************************************************/
1226
/*             GDALInConstructionAlgorithmArg::SetPositional()          */
1227
/************************************************************************/
1228
1229
GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1230
6.61k
{
1231
6.61k
    m_decl.SetPositional();
1232
6.61k
    if (m_owner)
1233
6.61k
        m_owner->SetPositional(this);
1234
6.61k
    return *this;
1235
6.61k
}
1236
1237
/************************************************************************/
1238
/*              GDALArgDatasetValue::GDALArgDatasetValue()              */
1239
/************************************************************************/
1240
1241
GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1242
0
    : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1243
0
      m_nameSet(true)
1244
0
{
1245
0
    if (m_poDS)
1246
0
        m_poDS->Reference();
1247
0
}
1248
1249
/************************************************************************/
1250
/*              GDALArgDatasetValue::Set()                              */
1251
/************************************************************************/
1252
1253
void GDALArgDatasetValue::Set(const std::string &name)
1254
9.21k
{
1255
9.21k
    Close();
1256
9.21k
    m_name = name;
1257
9.21k
    m_nameSet = true;
1258
9.21k
    if (m_ownerArg)
1259
9.21k
        m_ownerArg->NotifyValueSet();
1260
9.21k
}
1261
1262
/************************************************************************/
1263
/*              GDALArgDatasetValue::Set()                              */
1264
/************************************************************************/
1265
1266
void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1267
0
{
1268
0
    Close();
1269
0
    m_poDS = poDS.release();
1270
0
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1271
0
    m_nameSet = true;
1272
0
    if (m_ownerArg)
1273
0
        m_ownerArg->NotifyValueSet();
1274
0
}
1275
1276
/************************************************************************/
1277
/*              GDALArgDatasetValue::Set()                              */
1278
/************************************************************************/
1279
1280
void GDALArgDatasetValue::Set(GDALDataset *poDS)
1281
1.28k
{
1282
1.28k
    Close();
1283
1.28k
    m_poDS = poDS;
1284
1.28k
    if (m_poDS)
1285
1.28k
        m_poDS->Reference();
1286
1.28k
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1287
1.28k
    m_nameSet = true;
1288
1.28k
    if (m_ownerArg)
1289
1.28k
        m_ownerArg->NotifyValueSet();
1290
1.28k
}
1291
1292
/************************************************************************/
1293
/*                   GDALArgDatasetValue::SetFrom()                     */
1294
/************************************************************************/
1295
1296
void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1297
0
{
1298
0
    Close();
1299
0
    m_name = other.m_name;
1300
0
    m_nameSet = other.m_nameSet;
1301
0
    m_poDS = other.m_poDS;
1302
0
    if (m_poDS)
1303
0
        m_poDS->Reference();
1304
0
}
1305
1306
/************************************************************************/
1307
/*              GDALArgDatasetValue::~GDALArgDatasetValue()             */
1308
/************************************************************************/
1309
1310
GDALArgDatasetValue::~GDALArgDatasetValue()
1311
6.61k
{
1312
6.61k
    Close();
1313
6.61k
}
1314
1315
/************************************************************************/
1316
/*                     GDALArgDatasetValue::Close()                     */
1317
/************************************************************************/
1318
1319
bool GDALArgDatasetValue::Close()
1320
23.7k
{
1321
23.7k
    bool ret = true;
1322
23.7k
    if (m_poDS && m_poDS->Dereference() == 0)
1323
1.28k
    {
1324
1.28k
        ret = m_poDS->Close() == CE_None;
1325
1.28k
        delete m_poDS;
1326
1.28k
    }
1327
23.7k
    m_poDS = nullptr;
1328
23.7k
    return ret;
1329
23.7k
}
1330
1331
/************************************************************************/
1332
/*                      GDALArgDatasetValue::operator=()                */
1333
/************************************************************************/
1334
1335
GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1336
0
{
1337
0
    Close();
1338
0
    m_poDS = other.m_poDS;
1339
0
    m_name = other.m_name;
1340
0
    m_nameSet = other.m_nameSet;
1341
0
    other.m_poDS = nullptr;
1342
0
    other.m_name.clear();
1343
0
    other.m_nameSet = false;
1344
0
    return *this;
1345
0
}
1346
1347
/************************************************************************/
1348
/*                   GDALArgDatasetValue::GetDataset()                  */
1349
/************************************************************************/
1350
1351
GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1352
0
{
1353
0
    if (m_poDS)
1354
0
        m_poDS->Reference();
1355
0
    return m_poDS;
1356
0
}
1357
1358
/************************************************************************/
1359
/*               GDALArgDatasetValue(GDALArgDatasetValue &&other)       */
1360
/************************************************************************/
1361
1362
GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1363
0
    : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1364
0
{
1365
0
    other.m_poDS = nullptr;
1366
0
    other.m_name.clear();
1367
0
}
1368
1369
/************************************************************************/
1370
/*              GDALInConstructionAlgorithmArg::SetIsCRSArg()           */
1371
/************************************************************************/
1372
1373
GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1374
    bool noneAllowed, const std::vector<std::string> &specialValues)
1375
1
{
1376
1
    if (GetType() != GAAT_STRING)
1377
0
    {
1378
0
        CPLError(CE_Failure, CPLE_AppDefined,
1379
0
                 "SetIsCRSArg() can only be called on a String argument");
1380
0
        return *this;
1381
0
    }
1382
1
    AddValidationAction(
1383
1
        [this, noneAllowed, specialValues]()
1384
1
        {
1385
0
            const std::string &osVal =
1386
0
                static_cast<const GDALInConstructionAlgorithmArg *>(this)
1387
0
                    ->Get<std::string>();
1388
0
            if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1389
0
                return true;
1390
1391
0
            if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1392
0
                std::find(specialValues.begin(), specialValues.end(), osVal) ==
1393
0
                    specialValues.end())
1394
0
            {
1395
0
                OGRSpatialReference oSRS;
1396
0
                if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1397
0
                {
1398
0
                    m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1399
0
                                         "Invalid value for '%s' argument",
1400
0
                                         GetName().c_str());
1401
0
                    return false;
1402
0
                }
1403
0
            }
1404
0
            return true;
1405
0
        });
1406
1407
1
    SetAutoCompleteFunction(
1408
1
        [this, noneAllowed, specialValues](const std::string &currentValue)
1409
1
        {
1410
0
            bool bIsRaster = false;
1411
0
            OGREnvelope sDatasetLongLatEnv;
1412
0
            OGRSpatialReference oDSCRS;
1413
0
            const char *pszCelestialBodyName = nullptr;
1414
0
            if (GetName() == "dst-crs")
1415
0
            {
1416
0
                auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1417
0
                if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1418
0
                {
1419
0
                    auto &val =
1420
0
                        inputArg->Get<std::vector<GDALArgDatasetValue>>();
1421
0
                    if (val.size() == 1)
1422
0
                    {
1423
0
                        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1424
0
                        auto poDS = std::unique_ptr<GDALDataset>(
1425
0
                            GDALDataset::Open(val[0].GetName().c_str()));
1426
0
                        if (poDS)
1427
0
                        {
1428
0
                            bIsRaster = poDS->GetRasterCount() != 0;
1429
0
                            const OGRSpatialReference *poCRS;
1430
0
                            if ((poCRS = poDS->GetSpatialRef()) != nullptr)
1431
0
                            {
1432
0
                                oDSCRS = *poCRS;
1433
0
                            }
1434
0
                            else if (poDS->GetLayerCount() >= 1)
1435
0
                            {
1436
0
                                if (auto poLayer = poDS->GetLayer(0))
1437
0
                                {
1438
0
                                    if ((poCRS = poLayer->GetSpatialRef()) !=
1439
0
                                        nullptr)
1440
0
                                        oDSCRS = *poCRS;
1441
0
                                }
1442
0
                            }
1443
0
                            if (!oDSCRS.IsEmpty())
1444
0
                            {
1445
0
                                pszCelestialBodyName =
1446
0
                                    oDSCRS.GetCelestialBodyName();
1447
1448
0
                                if (!pszCelestialBodyName ||
1449
0
                                    !EQUAL(pszCelestialBodyName, "Earth"))
1450
0
                                {
1451
0
                                    OGRSpatialReference oLongLat;
1452
0
                                    oLongLat.CopyGeogCSFrom(&oDSCRS);
1453
0
                                    oLongLat.SetAxisMappingStrategy(
1454
0
                                        OAMS_TRADITIONAL_GIS_ORDER);
1455
0
                                    poDS->GetExtent(&sDatasetLongLatEnv,
1456
0
                                                    &oLongLat);
1457
0
                                }
1458
0
                                else
1459
0
                                {
1460
0
                                    poDS->GetExtentWGS84LongLat(
1461
0
                                        &sDatasetLongLatEnv);
1462
0
                                }
1463
0
                            }
1464
0
                        }
1465
0
                    }
1466
0
                }
1467
0
            }
1468
1469
0
            const auto IsCRSCompatible =
1470
0
                [bIsRaster, &sDatasetLongLatEnv,
1471
0
                 pszCelestialBodyName](const OSRCRSInfo *crsInfo)
1472
0
            {
1473
0
                if (!sDatasetLongLatEnv.IsInit())
1474
0
                    return true;
1475
0
                return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1476
0
                       !(bIsRaster &&
1477
0
                         crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1478
0
                       crsInfo->dfWestLongitudeDeg <
1479
0
                           crsInfo->dfEastLongitudeDeg &&
1480
0
                       sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1481
0
                       sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1482
0
                       sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1483
0
                       sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1484
0
                       ((pszCelestialBodyName &&
1485
0
                         crsInfo->pszCelestialBodyName &&
1486
0
                         EQUAL(pszCelestialBodyName,
1487
0
                               crsInfo->pszCelestialBodyName)) ||
1488
0
                        (!pszCelestialBodyName &&
1489
0
                         !crsInfo->pszCelestialBodyName));
1490
0
            };
1491
1492
0
            std::vector<std::string> oRet;
1493
0
            if (noneAllowed)
1494
0
                oRet.push_back("none");
1495
0
            oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1496
0
            if (!currentValue.empty())
1497
0
            {
1498
0
                const CPLStringList aosTokens(
1499
0
                    CSLTokenizeString2(currentValue.c_str(), ":", 0));
1500
0
                int nCount = 0;
1501
0
                std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1502
0
                    pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1503
0
                                                           nullptr, &nCount),
1504
0
                             OSRDestroyCRSInfoList);
1505
0
                std::string osCode;
1506
1507
0
                std::vector<const OSRCRSInfo *> candidates;
1508
0
                for (int i = 0; i < nCount; ++i)
1509
0
                {
1510
0
                    const auto *entry = (pCRSList.get())[i];
1511
0
                    if (!entry->bDeprecated && IsCRSCompatible(entry))
1512
0
                    {
1513
0
                        if (aosTokens.size() == 1 ||
1514
0
                            STARTS_WITH(entry->pszCode, aosTokens[1]))
1515
0
                        {
1516
0
                            if (candidates.empty())
1517
0
                                osCode = entry->pszCode;
1518
0
                            candidates.push_back(entry);
1519
0
                        }
1520
0
                    }
1521
0
                }
1522
0
                if (candidates.size() == 1)
1523
0
                {
1524
0
                    oRet.push_back(std::move(osCode));
1525
0
                }
1526
0
                else
1527
0
                {
1528
0
                    if (sDatasetLongLatEnv.IsInit())
1529
0
                    {
1530
0
                        std::sort(
1531
0
                            candidates.begin(), candidates.end(),
1532
0
                            [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1533
0
                            {
1534
0
                                const double dfXa =
1535
0
                                    a->dfWestLongitudeDeg >
1536
0
                                            a->dfEastLongitudeDeg
1537
0
                                        ? a->dfWestLongitudeDeg -
1538
0
                                              a->dfEastLongitudeDeg
1539
0
                                        : (180 - a->dfWestLongitudeDeg) +
1540
0
                                              (a->dfEastLongitudeDeg - -180);
1541
0
                                const double dfYa = a->dfNorthLatitudeDeg -
1542
0
                                                    a->dfSouthLatitudeDeg;
1543
0
                                const double dfXb =
1544
0
                                    b->dfWestLongitudeDeg >
1545
0
                                            b->dfEastLongitudeDeg
1546
0
                                        ? b->dfWestLongitudeDeg -
1547
0
                                              b->dfEastLongitudeDeg
1548
0
                                        : (180 - b->dfWestLongitudeDeg) +
1549
0
                                              (b->dfEastLongitudeDeg - -180);
1550
0
                                const double dfYb = b->dfNorthLatitudeDeg -
1551
0
                                                    b->dfSouthLatitudeDeg;
1552
0
                                const double diffArea =
1553
0
                                    dfXa * dfYa - dfXb * dfYb;
1554
0
                                if (diffArea < 0)
1555
0
                                    return true;
1556
0
                                if (diffArea == 0)
1557
0
                                {
1558
0
                                    if (std::string_view(a->pszName) ==
1559
0
                                        b->pszName)
1560
0
                                    {
1561
0
                                        if (a->eType ==
1562
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1563
0
                                            b->eType !=
1564
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_2D)
1565
0
                                            return true;
1566
0
                                        if (a->eType ==
1567
0
                                                OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1568
0
                                            b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1569
0
                                            return true;
1570
0
                                        return false;
1571
0
                                    }
1572
0
                                    return std::string_view(a->pszCode) <
1573
0
                                           b->pszCode;
1574
0
                                }
1575
0
                                return false;
1576
0
                            });
1577
0
                    }
1578
1579
0
                    for (const auto *entry : candidates)
1580
0
                    {
1581
0
                        std::string val = std::string(entry->pszCode)
1582
0
                                              .append(" -- ")
1583
0
                                              .append(entry->pszName);
1584
0
                        if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1585
0
                            val.append(" (geographic 2D)");
1586
0
                        else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1587
0
                            val.append(" (geographic 3D)");
1588
0
                        else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1589
0
                            val.append(" (geocentric)");
1590
0
                        oRet.push_back(std::move(val));
1591
0
                    }
1592
0
                }
1593
0
            }
1594
0
            if (currentValue.empty() || oRet.empty())
1595
0
            {
1596
0
                const CPLStringList aosAuthorities(
1597
0
                    OSRGetAuthorityListFromDatabase());
1598
0
                for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1599
0
                {
1600
0
                    int nCount = 0;
1601
0
                    OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1602
0
                        pszAuth, nullptr, &nCount));
1603
0
                    if (nCount)
1604
0
                        oRet.push_back(std::string(pszAuth).append(":"));
1605
0
                }
1606
0
            }
1607
0
            return oRet;
1608
0
        });
1609
1610
1
    return *this;
1611
1
}
1612
1613
/************************************************************************/
1614
/*                     GDALAlgorithm::GDALAlgorithm()                  */
1615
/************************************************************************/
1616
1617
GDALAlgorithm::GDALAlgorithm(const std::string &name,
1618
                             const std::string &description,
1619
                             const std::string &helpURL)
1620
19.9k
    : m_name(name), m_description(description), m_helpURL(helpURL),
1621
19.9k
      m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1622
19.9k
                        ? "https://gdal.org" + m_helpURL
1623
19.9k
                        : m_helpURL)
1624
19.9k
{
1625
19.9k
    AddArg("help", 'h', _("Display help message and exit"), &m_helpRequested)
1626
19.9k
        .SetOnlyForCLI()
1627
19.9k
        .SetCategory(GAAC_COMMON)
1628
19.9k
        .AddAction([this]() { m_specialActionRequested = true; });
1629
19.9k
    AddArg("help-doc", 0, _("Display help message for use by documentation"),
1630
19.9k
           &m_helpDocRequested)
1631
19.9k
        .SetHidden()
1632
19.9k
        .AddAction([this]() { m_specialActionRequested = true; });
1633
19.9k
    AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1634
19.9k
           &m_JSONUsageRequested)
1635
19.9k
        .SetOnlyForCLI()
1636
19.9k
        .SetCategory(GAAC_COMMON)
1637
19.9k
        .AddAction([this]() { m_specialActionRequested = true; });
1638
19.9k
    AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1639
19.9k
        .SetMetaVar("<KEY>=<VALUE>")
1640
19.9k
        .SetOnlyForCLI()
1641
19.9k
        .SetCategory(GAAC_COMMON)
1642
19.9k
        .AddAction(
1643
19.9k
            [this]()
1644
19.9k
            {
1645
890
                ReportError(
1646
890
                    CE_Warning, CPLE_AppDefined,
1647
890
                    "Configuration options passed with the 'config' argument "
1648
890
                    "are ignored");
1649
890
            });
1650
19.9k
}
1651
1652
/************************************************************************/
1653
/*                     GDALAlgorithm::~GDALAlgorithm()                  */
1654
/************************************************************************/
1655
1656
19.9k
GDALAlgorithm::~GDALAlgorithm() = default;
1657
1658
/************************************************************************/
1659
/*                    GDALAlgorithm::ParseArgument()                    */
1660
/************************************************************************/
1661
1662
bool GDALAlgorithm::ParseArgument(
1663
    GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1664
    std::map<
1665
        GDALAlgorithmArg *,
1666
        std::variant<std::vector<std::string>, std::vector<int>,
1667
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
1668
        &inConstructionValues)
1669
0
{
1670
0
    const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1671
0
    if (arg->IsExplicitlySet() && !isListArg)
1672
0
    {
1673
        // Hack for "gdal info" to be able to pass an opened raster dataset
1674
        // by "gdal raster info" to the "gdal vector info" algorithm.
1675
0
        if (arg->SkipIfAlreadySet())
1676
0
        {
1677
0
            arg->SetSkipIfAlreadySet(false);
1678
0
            return true;
1679
0
        }
1680
1681
0
        ReportError(CE_Failure, CPLE_IllegalArg,
1682
0
                    "Argument '%s' has already been specified.", name.c_str());
1683
0
        return false;
1684
0
    }
1685
1686
0
    if (!arg->GetRepeatedArgAllowed() &&
1687
0
        cpl::contains(inConstructionValues, arg))
1688
0
    {
1689
0
        ReportError(CE_Failure, CPLE_IllegalArg,
1690
0
                    "Argument '%s' has already been specified.", name.c_str());
1691
0
        return false;
1692
0
    }
1693
1694
0
    switch (arg->GetType())
1695
0
    {
1696
0
        case GAAT_BOOLEAN:
1697
0
        {
1698
0
            if (value.empty() || value == "true")
1699
0
                return arg->Set(true);
1700
0
            else if (value == "false")
1701
0
                return arg->Set(false);
1702
0
            else
1703
0
            {
1704
0
                ReportError(
1705
0
                    CE_Failure, CPLE_IllegalArg,
1706
0
                    "Invalid value '%s' for boolean argument '%s'. Should be "
1707
0
                    "'true' or 'false'.",
1708
0
                    value.c_str(), name.c_str());
1709
0
                return false;
1710
0
            }
1711
0
        }
1712
1713
0
        case GAAT_STRING:
1714
0
        {
1715
0
            return arg->Set(value);
1716
0
        }
1717
1718
0
        case GAAT_INTEGER:
1719
0
        {
1720
0
            errno = 0;
1721
0
            char *endptr = nullptr;
1722
0
            const auto val = std::strtol(value.c_str(), &endptr, 10);
1723
0
            if (errno == 0 && endptr &&
1724
0
                endptr == value.c_str() + value.size() && val >= INT_MIN &&
1725
0
                val <= INT_MAX)
1726
0
            {
1727
0
                return arg->Set(static_cast<int>(val));
1728
0
            }
1729
0
            else
1730
0
            {
1731
0
                ReportError(CE_Failure, CPLE_IllegalArg,
1732
0
                            "Expected integer value for argument '%s', "
1733
0
                            "but got '%s'.",
1734
0
                            name.c_str(), value.c_str());
1735
0
                return false;
1736
0
            }
1737
0
        }
1738
1739
0
        case GAAT_REAL:
1740
0
        {
1741
0
            char *endptr = nullptr;
1742
0
            double dfValue = CPLStrtod(value.c_str(), &endptr);
1743
0
            if (endptr != value.c_str() + value.size())
1744
0
            {
1745
0
                ReportError(
1746
0
                    CE_Failure, CPLE_IllegalArg,
1747
0
                    "Expected real value for argument '%s', but got '%s'.",
1748
0
                    name.c_str(), value.c_str());
1749
0
                return false;
1750
0
            }
1751
0
            return arg->Set(dfValue);
1752
0
        }
1753
1754
0
        case GAAT_DATASET:
1755
0
        {
1756
0
            return arg->SetDatasetName(value);
1757
0
        }
1758
1759
0
        case GAAT_STRING_LIST:
1760
0
        {
1761
0
            const CPLStringList aosTokens(
1762
0
                arg->GetPackedValuesAllowed()
1763
0
                    ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
1764
0
                    : CSLAddString(nullptr, value.c_str()));
1765
0
            if (!cpl::contains(inConstructionValues, arg))
1766
0
            {
1767
0
                inConstructionValues[arg] = std::vector<std::string>();
1768
0
            }
1769
0
            auto &valueVector =
1770
0
                std::get<std::vector<std::string>>(inConstructionValues[arg]);
1771
0
            for (const char *v : aosTokens)
1772
0
            {
1773
0
                valueVector.push_back(v);
1774
0
            }
1775
0
            break;
1776
0
        }
1777
1778
0
        case GAAT_INTEGER_LIST:
1779
0
        {
1780
0
            const CPLStringList aosTokens(
1781
0
                arg->GetPackedValuesAllowed()
1782
0
                    ? CSLTokenizeString2(
1783
0
                          value.c_str(), ",",
1784
0
                          CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1785
0
                              CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1786
0
                    : CSLAddString(nullptr, value.c_str()));
1787
0
            if (!cpl::contains(inConstructionValues, arg))
1788
0
            {
1789
0
                inConstructionValues[arg] = std::vector<int>();
1790
0
            }
1791
0
            auto &valueVector =
1792
0
                std::get<std::vector<int>>(inConstructionValues[arg]);
1793
0
            for (const char *v : aosTokens)
1794
0
            {
1795
0
                errno = 0;
1796
0
                char *endptr = nullptr;
1797
0
                const auto val = std::strtol(v, &endptr, 10);
1798
0
                if (errno == 0 && endptr && endptr == v + strlen(v) &&
1799
0
                    val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
1800
0
                {
1801
0
                    valueVector.push_back(static_cast<int>(val));
1802
0
                }
1803
0
                else
1804
0
                {
1805
0
                    ReportError(
1806
0
                        CE_Failure, CPLE_IllegalArg,
1807
0
                        "Expected list of integer value for argument '%s', "
1808
0
                        "but got '%s'.",
1809
0
                        name.c_str(), value.c_str());
1810
0
                    return false;
1811
0
                }
1812
0
            }
1813
0
            break;
1814
0
        }
1815
1816
0
        case GAAT_REAL_LIST:
1817
0
        {
1818
0
            const CPLStringList aosTokens(
1819
0
                arg->GetPackedValuesAllowed()
1820
0
                    ? CSLTokenizeString2(
1821
0
                          value.c_str(), ",",
1822
0
                          CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1823
0
                              CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1824
0
                    : CSLAddString(nullptr, value.c_str()));
1825
0
            if (!cpl::contains(inConstructionValues, arg))
1826
0
            {
1827
0
                inConstructionValues[arg] = std::vector<double>();
1828
0
            }
1829
0
            auto &valueVector =
1830
0
                std::get<std::vector<double>>(inConstructionValues[arg]);
1831
0
            for (const char *v : aosTokens)
1832
0
            {
1833
0
                char *endptr = nullptr;
1834
0
                double dfValue = CPLStrtod(v, &endptr);
1835
0
                if (strlen(v) == 0 || endptr != v + strlen(v))
1836
0
                {
1837
0
                    ReportError(
1838
0
                        CE_Failure, CPLE_IllegalArg,
1839
0
                        "Expected list of real value for argument '%s', "
1840
0
                        "but got '%s'.",
1841
0
                        name.c_str(), value.c_str());
1842
0
                    return false;
1843
0
                }
1844
0
                valueVector.push_back(dfValue);
1845
0
            }
1846
0
            break;
1847
0
        }
1848
1849
0
        case GAAT_DATASET_LIST:
1850
0
        {
1851
0
            const CPLStringList aosTokens(
1852
0
                CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS));
1853
0
            if (!cpl::contains(inConstructionValues, arg))
1854
0
            {
1855
0
                inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
1856
0
            }
1857
0
            auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1858
0
                inConstructionValues[arg]);
1859
0
            for (const char *v : aosTokens)
1860
0
            {
1861
0
                valueVector.push_back(GDALArgDatasetValue(v));
1862
0
            }
1863
0
            break;
1864
0
        }
1865
0
    }
1866
1867
0
    return true;
1868
0
}
1869
1870
/************************************************************************/
1871
/*               GDALAlgorithm::ParseCommandLineArguments()             */
1872
/************************************************************************/
1873
1874
bool GDALAlgorithm::ParseCommandLineArguments(
1875
    const std::vector<std::string> &args)
1876
0
{
1877
0
    if (m_parsedSubStringAlreadyCalled)
1878
0
    {
1879
0
        ReportError(CE_Failure, CPLE_AppDefined,
1880
0
                    "ParseCommandLineArguments() can only be called once per "
1881
0
                    "instance.");
1882
0
        return false;
1883
0
    }
1884
0
    m_parsedSubStringAlreadyCalled = true;
1885
1886
    // AWS like syntax supported too (not advertized)
1887
0
    if (args.size() == 1 && args[0] == "help")
1888
0
    {
1889
0
        auto arg = GetArg("help");
1890
0
        assert(arg);
1891
0
        arg->Set(true);
1892
0
        arg->RunActions();
1893
0
        return true;
1894
0
    }
1895
1896
0
    if (HasSubAlgorithms())
1897
0
    {
1898
0
        if (args.empty())
1899
0
        {
1900
0
            ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
1901
0
                        m_callPath.size() == 1 ? "command" : "subcommand");
1902
0
            return false;
1903
0
        }
1904
0
        if (!args[0].empty() && args[0][0] == '-')
1905
0
        {
1906
            // go on argument parsing
1907
0
        }
1908
0
        else
1909
0
        {
1910
0
            const auto nCounter = CPLGetErrorCounter();
1911
0
            m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
1912
0
            if (m_selectedSubAlgHolder)
1913
0
            {
1914
0
                m_selectedSubAlg = m_selectedSubAlgHolder.get();
1915
0
                m_selectedSubAlg->SetReferencePathForRelativePaths(
1916
0
                    m_referencePath);
1917
0
                m_selectedSubAlg->m_executionForStreamOutput =
1918
0
                    m_executionForStreamOutput;
1919
0
                m_selectedSubAlg->m_calledFromCommandLine =
1920
0
                    m_calledFromCommandLine;
1921
0
                bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
1922
0
                    std::vector<std::string>(args.begin() + 1, args.end()));
1923
0
                m_selectedSubAlg->PropagateSpecialActionTo(this);
1924
0
                return bRet;
1925
0
            }
1926
0
            else
1927
0
            {
1928
0
                if (!(CPLGetErrorCounter() == nCounter + 1 &&
1929
0
                      strstr(CPLGetLastErrorMsg(), "Do you mean")))
1930
0
                {
1931
0
                    ReportError(CE_Failure, CPLE_AppDefined,
1932
0
                                "Unknown command: '%s'", args[0].c_str());
1933
0
                }
1934
0
                return false;
1935
0
            }
1936
0
        }
1937
0
    }
1938
1939
0
    std::map<
1940
0
        GDALAlgorithmArg *,
1941
0
        std::variant<std::vector<std::string>, std::vector<int>,
1942
0
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
1943
0
        inConstructionValues;
1944
1945
0
    std::vector<std::string> lArgs(args);
1946
0
    bool helpValueRequested = false;
1947
0
    for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
1948
0
    {
1949
0
        const auto &strArg = lArgs[i];
1950
0
        GDALAlgorithmArg *arg = nullptr;
1951
0
        std::string name;
1952
0
        std::string value;
1953
0
        bool hasValue = false;
1954
0
        if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
1955
0
            helpValueRequested = true;
1956
0
        if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
1957
0
        {
1958
0
            const auto equalPos = strArg.find('=');
1959
0
            name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
1960
0
                                                   : strArg;
1961
0
            const std::string nameWithoutDash = name.substr(2);
1962
0
            const auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
1963
0
            if (iterArg == m_mapLongNameToArg.end())
1964
0
            {
1965
0
                const std::string bestCandidate =
1966
0
                    GetSuggestionForArgumentName(nameWithoutDash);
1967
0
                if (!bestCandidate.empty())
1968
0
                {
1969
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
1970
0
                                "Option '%s' is unknown. Do you mean '--%s'?",
1971
0
                                name.c_str(), bestCandidate.c_str());
1972
0
                }
1973
0
                else
1974
0
                {
1975
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
1976
0
                                "Option '%s' is unknown.", name.c_str());
1977
0
                }
1978
0
                return false;
1979
0
            }
1980
0
            arg = iterArg->second;
1981
0
            if (equalPos != std::string::npos)
1982
0
            {
1983
0
                hasValue = true;
1984
0
                value = strArg.substr(equalPos + 1);
1985
0
            }
1986
0
        }
1987
0
        else if (strArg.size() >= 2 && strArg[0] == '-' &&
1988
0
                 CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
1989
0
        {
1990
0
            for (size_t j = 1; j < strArg.size(); ++j)
1991
0
            {
1992
0
                name.clear();
1993
0
                name += strArg[j];
1994
0
                const auto iterArg = m_mapShortNameToArg.find(name);
1995
0
                if (iterArg == m_mapShortNameToArg.end())
1996
0
                {
1997
0
                    const std::string nameWithoutDash = strArg.substr(1);
1998
0
                    if (m_mapLongNameToArg.find(nameWithoutDash) !=
1999
0
                        m_mapLongNameToArg.end())
2000
0
                    {
2001
0
                        ReportError(CE_Failure, CPLE_IllegalArg,
2002
0
                                    "Short name option '%s' is unknown. Do you "
2003
0
                                    "mean '--%s' (with leading double dash) ?",
2004
0
                                    name.c_str(), nameWithoutDash.c_str());
2005
0
                    }
2006
0
                    else
2007
0
                    {
2008
0
                        const std::string bestCandidate =
2009
0
                            GetSuggestionForArgumentName(nameWithoutDash);
2010
0
                        if (!bestCandidate.empty())
2011
0
                        {
2012
0
                            ReportError(
2013
0
                                CE_Failure, CPLE_IllegalArg,
2014
0
                                "Short name option '%s' is unknown. Do you "
2015
0
                                "mean '--%s' (with leading double dash) ?",
2016
0
                                name.c_str(), bestCandidate.c_str());
2017
0
                        }
2018
0
                        else
2019
0
                        {
2020
0
                            ReportError(CE_Failure, CPLE_IllegalArg,
2021
0
                                        "Short name option '%s' is unknown.",
2022
0
                                        name.c_str());
2023
0
                        }
2024
0
                    }
2025
0
                    return false;
2026
0
                }
2027
0
                arg = iterArg->second;
2028
0
                if (strArg.size() > 2)
2029
0
                {
2030
0
                    if (arg->GetType() != GAAT_BOOLEAN)
2031
0
                    {
2032
0
                        ReportError(CE_Failure, CPLE_IllegalArg,
2033
0
                                    "Invalid argument '%s'. Option '%s' is not "
2034
0
                                    "a boolean option.",
2035
0
                                    strArg.c_str(), name.c_str());
2036
0
                        return false;
2037
0
                    }
2038
2039
0
                    if (!ParseArgument(arg, name, "true", inConstructionValues))
2040
0
                        return false;
2041
0
                }
2042
0
            }
2043
0
            if (strArg.size() > 2)
2044
0
            {
2045
0
                lArgs.erase(lArgs.begin() + i);
2046
0
                continue;
2047
0
            }
2048
0
        }
2049
0
        else
2050
0
        {
2051
0
            ++i;
2052
0
            continue;
2053
0
        }
2054
0
        CPLAssert(arg);
2055
2056
0
        if (arg && arg->GetType() == GAAT_BOOLEAN)
2057
0
        {
2058
0
            if (!hasValue)
2059
0
            {
2060
0
                hasValue = true;
2061
0
                value = "true";
2062
0
            }
2063
0
        }
2064
2065
0
        if (!hasValue)
2066
0
        {
2067
0
            if (i + 1 == lArgs.size())
2068
0
            {
2069
0
                if (m_parseForAutoCompletion)
2070
0
                {
2071
0
                    lArgs.erase(lArgs.begin() + i);
2072
0
                    break;
2073
0
                }
2074
0
                ReportError(
2075
0
                    CE_Failure, CPLE_IllegalArg,
2076
0
                    "Expected value for argument '%s', but ran short of tokens",
2077
0
                    name.c_str());
2078
0
                return false;
2079
0
            }
2080
0
            value = lArgs[i + 1];
2081
0
            lArgs.erase(lArgs.begin() + i + 1);
2082
0
        }
2083
2084
0
        if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2085
0
            return false;
2086
2087
0
        lArgs.erase(lArgs.begin() + i);
2088
0
    }
2089
2090
0
    if (m_specialActionRequested)
2091
0
    {
2092
0
        return true;
2093
0
    }
2094
2095
0
    const auto ProcessInConstructionValues = [&inConstructionValues]()
2096
0
    {
2097
0
        for (auto &[arg, value] : inConstructionValues)
2098
0
        {
2099
0
            if (arg->GetType() == GAAT_STRING_LIST)
2100
0
            {
2101
0
                if (!arg->Set(std::get<std::vector<std::string>>(
2102
0
                        inConstructionValues[arg])))
2103
0
                {
2104
0
                    return false;
2105
0
                }
2106
0
            }
2107
0
            else if (arg->GetType() == GAAT_INTEGER_LIST)
2108
0
            {
2109
0
                if (!arg->Set(
2110
0
                        std::get<std::vector<int>>(inConstructionValues[arg])))
2111
0
                {
2112
0
                    return false;
2113
0
                }
2114
0
            }
2115
0
            else if (arg->GetType() == GAAT_REAL_LIST)
2116
0
            {
2117
0
                if (!arg->Set(std::get<std::vector<double>>(
2118
0
                        inConstructionValues[arg])))
2119
0
                {
2120
0
                    return false;
2121
0
                }
2122
0
            }
2123
0
            else if (arg->GetType() == GAAT_DATASET_LIST)
2124
0
            {
2125
0
                if (!arg->Set(
2126
0
                        std::move(std::get<std::vector<GDALArgDatasetValue>>(
2127
0
                            inConstructionValues[arg]))))
2128
0
                {
2129
0
                    return false;
2130
0
                }
2131
0
            }
2132
0
        }
2133
0
        return true;
2134
0
    };
2135
2136
    // Process positional arguments that have not been set through their
2137
    // option name.
2138
0
    size_t i = 0;
2139
0
    size_t iCurPosArg = 0;
2140
2141
    // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2142
0
    if (m_positionalArgs.size() == 3 &&
2143
0
        (m_positionalArgs[0]->IsRequired() ||
2144
0
         m_positionalArgs[0]->GetMinCount() == 1) &&
2145
0
        m_positionalArgs[0]->GetMaxCount() == 1 &&
2146
0
        (m_positionalArgs[1]->IsRequired() ||
2147
0
         m_positionalArgs[1]->GetMinCount() == 1) &&
2148
        /* Second argument may have several occurrences */
2149
0
        m_positionalArgs[1]->GetMaxCount() >= 1 &&
2150
0
        (m_positionalArgs[2]->IsRequired() ||
2151
0
         m_positionalArgs[2]->GetMinCount() == 1) &&
2152
0
        m_positionalArgs[2]->GetMaxCount() == 1 &&
2153
0
        !m_positionalArgs[0]->IsExplicitlySet() &&
2154
0
        !m_positionalArgs[1]->IsExplicitlySet() &&
2155
0
        !m_positionalArgs[2]->IsExplicitlySet())
2156
0
    {
2157
0
        if (lArgs.size() - i < 3)
2158
0
        {
2159
0
            ReportError(CE_Failure, CPLE_AppDefined,
2160
0
                        "Not enough positional values.");
2161
0
            return false;
2162
0
        }
2163
0
        bool ok = ParseArgument(m_positionalArgs[0],
2164
0
                                m_positionalArgs[0]->GetName().c_str(),
2165
0
                                lArgs[i], inConstructionValues);
2166
0
        if (ok)
2167
0
        {
2168
0
            ++i;
2169
0
            for (; i + 1 < lArgs.size() && ok; ++i)
2170
0
            {
2171
0
                ok = ParseArgument(m_positionalArgs[1],
2172
0
                                   m_positionalArgs[1]->GetName().c_str(),
2173
0
                                   lArgs[i], inConstructionValues);
2174
0
            }
2175
0
        }
2176
0
        if (ok)
2177
0
        {
2178
0
            ok = ParseArgument(m_positionalArgs[2],
2179
0
                               m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2180
0
                               inConstructionValues);
2181
0
            ++i;
2182
0
        }
2183
0
        if (!ok)
2184
0
        {
2185
0
            ProcessInConstructionValues();
2186
0
            return false;
2187
0
        }
2188
0
    }
2189
2190
0
    while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2191
0
    {
2192
0
        GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2193
0
        while (arg->IsExplicitlySet())
2194
0
        {
2195
0
            ++iCurPosArg;
2196
0
            if (iCurPosArg == m_positionalArgs.size())
2197
0
                break;
2198
0
            arg = m_positionalArgs[iCurPosArg];
2199
0
        }
2200
0
        if (iCurPosArg == m_positionalArgs.size())
2201
0
        {
2202
0
            break;
2203
0
        }
2204
0
        if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2205
0
            arg->GetMinCount() != arg->GetMaxCount())
2206
0
        {
2207
0
            if (iCurPosArg == 0)
2208
0
            {
2209
0
                size_t nCountAtEnd = 0;
2210
0
                for (size_t j = 1; j < m_positionalArgs.size(); j++)
2211
0
                {
2212
0
                    const auto *otherArg = m_positionalArgs[j];
2213
0
                    if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2214
0
                    {
2215
0
                        if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2216
0
                        {
2217
0
                            ReportError(
2218
0
                                CE_Failure, CPLE_AppDefined,
2219
0
                                "Ambiguity in definition of positional "
2220
0
                                "argument "
2221
0
                                "'%s' given it has a varying number of values, "
2222
0
                                "but follows argument '%s' which also has a "
2223
0
                                "varying number of values",
2224
0
                                otherArg->GetName().c_str(),
2225
0
                                arg->GetName().c_str());
2226
0
                            ProcessInConstructionValues();
2227
0
                            return false;
2228
0
                        }
2229
0
                        nCountAtEnd += otherArg->GetMinCount();
2230
0
                    }
2231
0
                    else
2232
0
                    {
2233
0
                        if (!otherArg->IsRequired())
2234
0
                        {
2235
0
                            ReportError(
2236
0
                                CE_Failure, CPLE_AppDefined,
2237
0
                                "Ambiguity in definition of positional "
2238
0
                                "argument "
2239
0
                                "'%s', given it is not required but follows "
2240
0
                                "argument '%s' which has a varying number of "
2241
0
                                "values",
2242
0
                                otherArg->GetName().c_str(),
2243
0
                                arg->GetName().c_str());
2244
0
                            ProcessInConstructionValues();
2245
0
                            return false;
2246
0
                        }
2247
0
                        nCountAtEnd++;
2248
0
                    }
2249
0
                }
2250
0
                if (lArgs.size() < nCountAtEnd)
2251
0
                {
2252
0
                    ReportError(CE_Failure, CPLE_AppDefined,
2253
0
                                "Not enough positional values.");
2254
0
                    ProcessInConstructionValues();
2255
0
                    return false;
2256
0
                }
2257
0
                for (; i < lArgs.size() - nCountAtEnd; ++i)
2258
0
                {
2259
0
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2260
0
                                       inConstructionValues))
2261
0
                    {
2262
0
                        ProcessInConstructionValues();
2263
0
                        return false;
2264
0
                    }
2265
0
                }
2266
0
            }
2267
0
            else if (iCurPosArg == m_positionalArgs.size() - 1)
2268
0
            {
2269
0
                for (; i < lArgs.size(); ++i)
2270
0
                {
2271
0
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2272
0
                                       inConstructionValues))
2273
0
                    {
2274
0
                        ProcessInConstructionValues();
2275
0
                        return false;
2276
0
                    }
2277
0
                }
2278
0
            }
2279
0
            else
2280
0
            {
2281
0
                ReportError(CE_Failure, CPLE_AppDefined,
2282
0
                            "Ambiguity in definition of positional arguments: "
2283
0
                            "arguments with varying number of values must be "
2284
0
                            "first or last one.");
2285
0
                return false;
2286
0
            }
2287
0
        }
2288
0
        else
2289
0
        {
2290
0
            if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2291
0
            {
2292
0
                ReportError(CE_Failure, CPLE_AppDefined,
2293
0
                            "Not enough positional values.");
2294
0
                return false;
2295
0
            }
2296
0
            const size_t iMax = i + arg->GetMaxCount();
2297
0
            for (; i < iMax; ++i)
2298
0
            {
2299
0
                if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2300
0
                                   inConstructionValues))
2301
0
                {
2302
0
                    ProcessInConstructionValues();
2303
0
                    return false;
2304
0
                }
2305
0
            }
2306
0
        }
2307
0
        ++iCurPosArg;
2308
0
    }
2309
2310
0
    if (i < lArgs.size())
2311
0
    {
2312
0
        ReportError(CE_Failure, CPLE_AppDefined,
2313
0
                    "Positional values starting at '%s' are not expected.",
2314
0
                    lArgs[i].c_str());
2315
0
        return false;
2316
0
    }
2317
2318
0
    if (!ProcessInConstructionValues())
2319
0
    {
2320
0
        return false;
2321
0
    }
2322
2323
    // Skip to first unset positional argument.
2324
0
    while (iCurPosArg < m_positionalArgs.size() &&
2325
0
           m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2326
0
    {
2327
0
        ++iCurPosArg;
2328
0
    }
2329
    // Check if this positional argument is required.
2330
0
    if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2331
0
        (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2332
0
             ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2333
0
             : m_positionalArgs[iCurPosArg]->IsRequired()))
2334
0
    {
2335
0
        ReportError(CE_Failure, CPLE_AppDefined,
2336
0
                    "Positional arguments starting at '%s' have not been "
2337
0
                    "specified.",
2338
0
                    m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2339
0
        return false;
2340
0
    }
2341
2342
0
    if (m_calledFromCommandLine)
2343
0
    {
2344
0
        for (auto &arg : m_args)
2345
0
        {
2346
0
            if (arg->IsExplicitlySet() &&
2347
0
                ((arg->GetType() == GAAT_STRING &&
2348
0
                  arg->Get<std::string>() == "?") ||
2349
0
                 (arg->GetType() == GAAT_STRING_LIST &&
2350
0
                  arg->Get<std::vector<std::string>>().size() == 1 &&
2351
0
                  arg->Get<std::vector<std::string>>()[0] == "?")))
2352
0
            {
2353
0
                {
2354
0
                    CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2355
0
                    ValidateArguments();
2356
0
                }
2357
2358
0
                auto choices = arg->GetChoices();
2359
0
                if (choices.empty())
2360
0
                    choices = arg->GetAutoCompleteChoices(std::string());
2361
0
                if (!choices.empty())
2362
0
                {
2363
0
                    if (choices.size() == 1)
2364
0
                    {
2365
0
                        ReportError(
2366
0
                            CE_Failure, CPLE_AppDefined,
2367
0
                            "Single potential value for argument '%s' is '%s'",
2368
0
                            arg->GetName().c_str(), choices.front().c_str());
2369
0
                    }
2370
0
                    else
2371
0
                    {
2372
0
                        std::string msg("Potential values for argument '");
2373
0
                        msg += arg->GetName();
2374
0
                        msg += "' are:";
2375
0
                        for (const auto &v : choices)
2376
0
                        {
2377
0
                            msg += "\n- ";
2378
0
                            msg += v;
2379
0
                        }
2380
0
                        ReportError(CE_Failure, CPLE_AppDefined, "%s",
2381
0
                                    msg.c_str());
2382
0
                    }
2383
0
                    return false;
2384
0
                }
2385
0
            }
2386
0
        }
2387
0
    }
2388
2389
0
    return m_skipValidationInParseCommandLine || ValidateArguments();
2390
0
}
2391
2392
/************************************************************************/
2393
/*                     GDALAlgorithm::ReportError()                     */
2394
/************************************************************************/
2395
2396
//! @cond Doxygen_Suppress
2397
void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2398
                                const char *fmt, ...) const
2399
2.18k
{
2400
2.18k
    va_list args;
2401
2.18k
    va_start(args, fmt);
2402
2.18k
    CPLError(eErrClass, err_no, "%s",
2403
2.18k
             std::string(m_name)
2404
2.18k
                 .append(": ")
2405
2.18k
                 .append(CPLString().vPrintf(fmt, args))
2406
2.18k
                 .c_str());
2407
2.18k
    va_end(args);
2408
2.18k
}
2409
2410
//! @endcond
2411
2412
/************************************************************************/
2413
/*                   GDALAlgorithm::ProcessDatasetArg()                 */
2414
/************************************************************************/
2415
2416
bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2417
                                      GDALAlgorithm *algForOutput)
2418
6.58k
{
2419
6.58k
    bool ret = true;
2420
2421
6.58k
    const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2422
6.58k
    const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2423
6.58k
    const bool update = hasUpdateArg && updateArg->Get<bool>();
2424
6.58k
    const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2425
6.58k
    const bool overwrite =
2426
6.58k
        (arg->IsOutput() && overwriteArg &&
2427
6.58k
         overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2428
6.58k
    auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2429
6.58k
    auto &val = [arg]() -> GDALArgDatasetValue &
2430
6.58k
    {
2431
6.58k
        if (arg->GetType() == GAAT_DATASET_LIST)
2432
0
            return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2433
6.58k
        else
2434
6.58k
            return arg->Get<GDALArgDatasetValue>();
2435
6.58k
    }();
2436
6.58k
    const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2437
6.58k
        arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2438
6.58k
        !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2439
6.58k
        !overwrite;
2440
6.58k
    if (!val.GetDatasetRef() && !val.IsNameSet())
2441
0
    {
2442
0
        ReportError(CE_Failure, CPLE_AppDefined,
2443
0
                    "Argument '%s' has no dataset object or dataset name.",
2444
0
                    arg->GetName().c_str());
2445
0
        ret = false;
2446
0
    }
2447
6.58k
    else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2448
0
    {
2449
0
        return false;
2450
0
    }
2451
6.58k
    else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2452
6.58k
             (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2453
6.58k
              onlyInputSpecifiedInUpdateAndOutputNotRequired))
2454
6.58k
    {
2455
6.58k
        int flags = arg->GetDatasetType();
2456
6.58k
        bool assignToOutputArg = false;
2457
2458
        // Check if input and output parameters point to the same
2459
        // filename (for vector datasets)
2460
6.58k
        if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2461
6.58k
            outputArg && outputArg->GetType() == GAAT_DATASET)
2462
0
        {
2463
0
            auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2464
0
            if (!outputVal.GetDatasetRef() &&
2465
0
                outputVal.GetName() == val.GetName() &&
2466
0
                (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2467
0
            {
2468
0
                assignToOutputArg = true;
2469
0
                flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2470
0
            }
2471
0
            else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2472
0
            {
2473
0
                if (updateArg->GetMutualExclusionGroup().empty() ||
2474
0
                    outputArg->GetMutualExclusionGroup().empty() ||
2475
0
                    updateArg->GetMutualExclusionGroup() !=
2476
0
                        outputArg->GetMutualExclusionGroup())
2477
0
                {
2478
0
                    assignToOutputArg = true;
2479
0
                }
2480
0
                flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2481
0
            }
2482
0
        }
2483
2484
6.58k
        if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2485
6.58k
            flags |= GDAL_OF_VERBOSE_ERROR;
2486
6.58k
        if ((arg == outputArg || !outputArg) && update)
2487
0
            flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2488
2489
6.58k
        const auto readOnlyArg = algForOutput->GetArg(GDAL_ARG_NAME_READ_ONLY);
2490
6.58k
        const bool readOnly =
2491
6.58k
            (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2492
6.58k
             readOnlyArg->Get<bool>());
2493
6.58k
        if (readOnly)
2494
0
            flags &= ~GDAL_OF_UPDATE;
2495
2496
6.58k
        CPLStringList aosOpenOptions;
2497
6.58k
        CPLStringList aosAllowedDrivers;
2498
6.58k
        if (arg->IsInput())
2499
6.58k
        {
2500
6.58k
            const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2501
6.58k
            if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2502
0
                aosOpenOptions =
2503
0
                    CPLStringList(ooArg->Get<std::vector<std::string>>());
2504
2505
6.58k
            const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2506
6.58k
            if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2507
0
                aosAllowedDrivers =
2508
0
                    CPLStringList(ifArg->Get<std::vector<std::string>>());
2509
6.58k
        }
2510
2511
6.58k
        std::string osDatasetName = val.GetName();
2512
6.58k
        if (!m_referencePath.empty())
2513
0
        {
2514
0
            osDatasetName = GDALDataset::BuildFilename(
2515
0
                osDatasetName.c_str(), m_referencePath.c_str(), true);
2516
0
        }
2517
6.58k
        if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2518
0
            osDatasetName = "/vsistdin/";
2519
2520
        // Handle special case of overview delete in GTiff which would fail
2521
        // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2522
6.58k
        if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2523
6.58k
            m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2524
6.58k
            aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2525
0
        {
2526
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2527
0
            GDALDriverH hDrv =
2528
0
                GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2529
0
            if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2530
0
            {
2531
                // Cleaning does not break COG layout
2532
0
                aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2533
0
            }
2534
0
        }
2535
2536
6.58k
        auto poDS =
2537
6.58k
            GDALDataset::Open(osDatasetName.c_str(), flags,
2538
6.58k
                              aosAllowedDrivers.List(), aosOpenOptions.List());
2539
6.58k
        if (poDS)
2540
1.28k
        {
2541
1.28k
            if (assignToOutputArg)
2542
0
            {
2543
                // Avoid opening twice the same datasource if it is both
2544
                // the input and output.
2545
                // Known to cause problems with at least FGdb, SQLite
2546
                // and GPKG drivers. See #4270
2547
                // Restrict to those 3 drivers. For example it is known
2548
                // to break with the PG driver due to the way it
2549
                // manages transactions.
2550
0
                auto poDriver = poDS->GetDriver();
2551
0
                if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2552
0
                                 EQUAL(poDriver->GetDescription(), "SQLite") ||
2553
0
                                 EQUAL(poDriver->GetDescription(), "GPKG")))
2554
0
                {
2555
0
                    outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2556
0
                }
2557
0
                else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2558
0
                {
2559
0
                    outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2560
0
                }
2561
0
            }
2562
1.28k
            val.SetDatasetOpenedByAlgorithm();
2563
1.28k
            val.Set(poDS);
2564
1.28k
            poDS->ReleaseRef();
2565
1.28k
        }
2566
5.30k
        else
2567
5.30k
        {
2568
5.30k
            ret = false;
2569
5.30k
        }
2570
6.58k
    }
2571
0
    else if (onlyInputSpecifiedInUpdateAndOutputNotRequired &&
2572
0
             val.GetDatasetRef())
2573
0
    {
2574
0
        if (updateArg->GetMutualExclusionGroup().empty() ||
2575
0
            outputArg->GetMutualExclusionGroup().empty() ||
2576
0
            updateArg->GetMutualExclusionGroup() !=
2577
0
                outputArg->GetMutualExclusionGroup())
2578
0
        {
2579
0
            outputArg->Get<GDALArgDatasetValue>().Set(val.GetDatasetRef());
2580
0
        }
2581
0
    }
2582
2583
    // Deal with overwriting the output dataset
2584
6.58k
    if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2585
0
    {
2586
0
        const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2587
0
        const bool hasAppendArg =
2588
0
            appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2589
0
        const bool append = (hasAppendArg && appendArg->Get<bool>());
2590
0
        if (!append)
2591
0
        {
2592
            // If outputting to MEM, do not try to erase a real file of the same name!
2593
0
            const auto outputFormatArg =
2594
0
                algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2595
0
            if (!(outputFormatArg &&
2596
0
                  outputFormatArg->GetType() == GAAT_STRING &&
2597
0
                  (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2598
0
                   EQUAL(outputFormatArg->Get<std::string>().c_str(),
2599
0
                         "stream") ||
2600
0
                   EQUAL(outputFormatArg->Get<std::string>().c_str(),
2601
0
                         "Memory"))))
2602
0
            {
2603
0
                const char *pszType = "";
2604
0
                GDALDriver *poDriver = nullptr;
2605
0
                if (!val.GetName().empty() &&
2606
0
                    GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
2607
0
                                               &poDriver))
2608
0
                {
2609
0
                    if (!overwrite)
2610
0
                    {
2611
0
                        std::string options;
2612
0
                        if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
2613
0
                        {
2614
0
                            options += "--";
2615
0
                            options += GDAL_ARG_NAME_OVERWRITE_LAYER;
2616
0
                        }
2617
0
                        if (hasAppendArg)
2618
0
                        {
2619
0
                            if (!options.empty())
2620
0
                                options += '/';
2621
0
                            options += "--";
2622
0
                            options += GDAL_ARG_NAME_APPEND;
2623
0
                        }
2624
0
                        if (hasUpdateArg)
2625
0
                        {
2626
0
                            if (!options.empty())
2627
0
                                options += '/';
2628
0
                            options += "--";
2629
0
                            options += GDAL_ARG_NAME_UPDATE;
2630
0
                        }
2631
2632
0
                        if (poDriver)
2633
0
                        {
2634
0
                            const char *pszPrefix = poDriver->GetMetadataItem(
2635
0
                                GDAL_DMD_CONNECTION_PREFIX);
2636
0
                            if (pszPrefix &&
2637
0
                                STARTS_WITH_CI(val.GetName().c_str(),
2638
0
                                               pszPrefix))
2639
0
                            {
2640
0
                                bool bExists = false;
2641
0
                                {
2642
0
                                    CPLErrorStateBackuper oBackuper(
2643
0
                                        CPLQuietErrorHandler);
2644
0
                                    bExists = std::unique_ptr<GDALDataset>(
2645
0
                                                  GDALDataset::Open(
2646
0
                                                      val.GetName().c_str())) !=
2647
0
                                              nullptr;
2648
0
                                }
2649
0
                                if (bExists)
2650
0
                                {
2651
0
                                    if (!options.empty())
2652
0
                                        options = " You may specify the " +
2653
0
                                                  options + " option.";
2654
0
                                    ReportError(CE_Failure, CPLE_AppDefined,
2655
0
                                                "%s '%s' already exists.%s",
2656
0
                                                pszType, val.GetName().c_str(),
2657
0
                                                options.c_str());
2658
0
                                    return false;
2659
0
                                }
2660
2661
0
                                return true;
2662
0
                            }
2663
0
                        }
2664
2665
0
                        if (!options.empty())
2666
0
                            options = '/' + options;
2667
0
                        ReportError(
2668
0
                            CE_Failure, CPLE_AppDefined,
2669
0
                            "%s '%s' already exists. You may specify the "
2670
0
                            "--overwrite%s option.",
2671
0
                            pszType, val.GetName().c_str(), options.c_str());
2672
0
                        return false;
2673
0
                    }
2674
0
                    else if (EQUAL(pszType, "File"))
2675
0
                    {
2676
0
                        VSIUnlink(val.GetName().c_str());
2677
0
                    }
2678
0
                    else if (EQUAL(pszType, "Directory"))
2679
0
                    {
2680
                        // We don't want the user to accidentally erase a non-GDAL dataset
2681
0
                        ReportError(CE_Failure, CPLE_AppDefined,
2682
0
                                    "Directory '%s' already exists, but is not "
2683
0
                                    "recognized as a valid GDAL dataset. "
2684
0
                                    "Please manually delete it before retrying",
2685
0
                                    val.GetName().c_str());
2686
0
                        return false;
2687
0
                    }
2688
0
                    else if (poDriver)
2689
0
                    {
2690
0
                        CPLStringList aosDrivers;
2691
0
                        aosDrivers.AddString(poDriver->GetDescription());
2692
0
                        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2693
0
                        GDALDriver::QuietDelete(val.GetName().c_str(),
2694
0
                                                aosDrivers.List());
2695
0
                    }
2696
0
                }
2697
0
            }
2698
0
        }
2699
0
    }
2700
2701
6.58k
    return ret;
2702
6.58k
}
2703
2704
/************************************************************************/
2705
/*                   GDALAlgorithm::ValidateArguments()                 */
2706
/************************************************************************/
2707
2708
bool GDALAlgorithm::ValidateArguments()
2709
6.62k
{
2710
6.62k
    if (m_selectedSubAlg)
2711
0
        return m_selectedSubAlg->ValidateArguments();
2712
2713
6.62k
    if (m_specialActionRequested)
2714
0
        return true;
2715
2716
    // If only --output=format=MEM/stream is specified and not --output,
2717
    // then set empty name for --output.
2718
6.62k
    auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2719
6.62k
    auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2720
6.62k
    if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
2721
6.62k
        !outputArg->IsExplicitlySet() &&
2722
6.62k
        outputFormatArg->GetType() == GAAT_STRING &&
2723
6.62k
        (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2724
0
         EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
2725
6.62k
        outputArg->GetType() == GAAT_DATASET &&
2726
6.62k
        (outputArg->GetDatasetInputFlags() & GADV_NAME))
2727
0
    {
2728
0
        outputArg->Get<GDALArgDatasetValue>().Set("");
2729
0
    }
2730
2731
    // The method may emit several errors if several constraints are not met.
2732
6.62k
    bool ret = true;
2733
6.62k
    std::map<std::string, std::string> mutualExclusionGroupUsed;
2734
6.62k
    for (auto &arg : m_args)
2735
46.3k
    {
2736
        // Check mutually exclusive arguments
2737
46.3k
        if (arg->IsExplicitlySet())
2738
6.73k
        {
2739
6.73k
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
2740
6.73k
            if (!mutualExclusionGroup.empty())
2741
0
            {
2742
0
                auto oIter =
2743
0
                    mutualExclusionGroupUsed.find(mutualExclusionGroup);
2744
0
                if (oIter != mutualExclusionGroupUsed.end())
2745
0
                {
2746
0
                    ret = false;
2747
0
                    ReportError(
2748
0
                        CE_Failure, CPLE_AppDefined,
2749
0
                        "Argument '%s' is mutually exclusive with '%s'.",
2750
0
                        arg->GetName().c_str(), oIter->second.c_str());
2751
0
                }
2752
0
                else
2753
0
                {
2754
0
                    mutualExclusionGroupUsed[mutualExclusionGroup] =
2755
0
                        arg->GetName();
2756
0
                }
2757
0
            }
2758
6.73k
        }
2759
2760
46.3k
        if (arg->IsRequired() && !arg->IsExplicitlySet() &&
2761
46.3k
            !arg->HasDefaultValue())
2762
8
        {
2763
8
            ReportError(CE_Failure, CPLE_AppDefined,
2764
8
                        "Required argument '%s' has not been specified.",
2765
8
                        arg->GetName().c_str());
2766
8
            ret = false;
2767
8
        }
2768
46.3k
        else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
2769
6.58k
        {
2770
6.58k
            if (!ProcessDatasetArg(arg.get(), this))
2771
5.30k
                ret = false;
2772
6.58k
        }
2773
39.7k
        else if (arg->IsExplicitlySet() &&
2774
39.7k
                 GDALAlgorithmArgTypeIsList(arg->GetType()))
2775
16
        {
2776
16
            int valueCount = 0;
2777
16
            if (arg->GetType() == GAAT_STRING_LIST)
2778
16
            {
2779
16
                valueCount = static_cast<int>(
2780
16
                    arg->Get<std::vector<std::string>>().size());
2781
16
            }
2782
0
            else if (arg->GetType() == GAAT_INTEGER_LIST)
2783
0
            {
2784
0
                valueCount =
2785
0
                    static_cast<int>(arg->Get<std::vector<int>>().size());
2786
0
            }
2787
0
            else if (arg->GetType() == GAAT_REAL_LIST)
2788
0
            {
2789
0
                valueCount =
2790
0
                    static_cast<int>(arg->Get<std::vector<double>>().size());
2791
0
            }
2792
0
            else if (arg->GetType() == GAAT_DATASET_LIST)
2793
0
            {
2794
0
                valueCount = static_cast<int>(
2795
0
                    arg->Get<std::vector<GDALArgDatasetValue>>().size());
2796
0
            }
2797
2798
16
            if (valueCount != arg->GetMinCount() &&
2799
16
                arg->GetMinCount() == arg->GetMaxCount())
2800
0
            {
2801
0
                ReportError(CE_Failure, CPLE_AppDefined,
2802
0
                            "%d value%s been specified for argument '%s', "
2803
0
                            "whereas exactly %d %s expected.",
2804
0
                            valueCount, valueCount > 1 ? "s have" : " has",
2805
0
                            arg->GetName().c_str(), arg->GetMinCount(),
2806
0
                            arg->GetMinCount() > 1 ? "were" : "was");
2807
0
                ret = false;
2808
0
            }
2809
16
            else if (valueCount < arg->GetMinCount())
2810
0
            {
2811
0
                ReportError(CE_Failure, CPLE_AppDefined,
2812
0
                            "Only %d value%s been specified for argument '%s', "
2813
0
                            "whereas at least %d %s expected.",
2814
0
                            valueCount, valueCount > 1 ? "s have" : " has",
2815
0
                            arg->GetName().c_str(), arg->GetMinCount(),
2816
0
                            arg->GetMinCount() > 1 ? "were" : "was");
2817
0
                ret = false;
2818
0
            }
2819
16
            else if (valueCount > arg->GetMaxCount())
2820
0
            {
2821
0
                ReportError(CE_Failure, CPLE_AppDefined,
2822
0
                            "%d value%s been specified for argument '%s', "
2823
0
                            "whereas at most %d %s expected.",
2824
0
                            valueCount, valueCount > 1 ? "s have" : " has",
2825
0
                            arg->GetName().c_str(), arg->GetMaxCount(),
2826
0
                            arg->GetMaxCount() > 1 ? "were" : "was");
2827
0
                ret = false;
2828
0
            }
2829
16
        }
2830
2831
46.3k
        if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
2832
46.3k
            arg->AutoOpenDataset())
2833
0
        {
2834
0
            auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
2835
0
            if (listVal.size() == 1)
2836
0
            {
2837
0
                if (!ProcessDatasetArg(arg.get(), this))
2838
0
                    ret = false;
2839
0
            }
2840
0
            else
2841
0
            {
2842
0
                for (auto &val : listVal)
2843
0
                {
2844
0
                    if (!val.GetDatasetRef() && val.GetName().empty())
2845
0
                    {
2846
0
                        ReportError(CE_Failure, CPLE_AppDefined,
2847
0
                                    "Argument '%s' has no dataset object or "
2848
0
                                    "dataset name.",
2849
0
                                    arg->GetName().c_str());
2850
0
                        ret = false;
2851
0
                    }
2852
0
                    else if (!val.GetDatasetRef())
2853
0
                    {
2854
0
                        int flags =
2855
0
                            arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
2856
2857
0
                        CPLStringList aosOpenOptions;
2858
0
                        CPLStringList aosAllowedDrivers;
2859
0
                        if (arg->GetName() == GDAL_ARG_NAME_INPUT)
2860
0
                        {
2861
0
                            const auto ooArg =
2862
0
                                GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2863
0
                            if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2864
0
                            {
2865
0
                                aosOpenOptions = CPLStringList(
2866
0
                                    ooArg->Get<std::vector<std::string>>());
2867
0
                            }
2868
2869
0
                            const auto ifArg =
2870
0
                                GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2871
0
                            if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2872
0
                            {
2873
0
                                aosAllowedDrivers = CPLStringList(
2874
0
                                    ifArg->Get<std::vector<std::string>>());
2875
0
                            }
2876
2877
0
                            const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
2878
0
                            if (updateArg &&
2879
0
                                updateArg->GetType() == GAAT_BOOLEAN &&
2880
0
                                updateArg->Get<bool>())
2881
0
                            {
2882
0
                                flags |= GDAL_OF_UPDATE;
2883
0
                            }
2884
0
                        }
2885
2886
0
                        auto poDS = std::unique_ptr<GDALDataset>(
2887
0
                            GDALDataset::Open(val.GetName().c_str(), flags,
2888
0
                                              aosAllowedDrivers.List(),
2889
0
                                              aosOpenOptions.List()));
2890
0
                        if (poDS)
2891
0
                        {
2892
0
                            val.Set(std::move(poDS));
2893
0
                        }
2894
0
                        else
2895
0
                        {
2896
0
                            ret = false;
2897
0
                        }
2898
0
                    }
2899
0
                }
2900
0
            }
2901
0
        }
2902
46.3k
    }
2903
2904
6.62k
    for (const auto &f : m_validationActions)
2905
6.59k
    {
2906
6.59k
        if (!f())
2907
0
            ret = false;
2908
6.59k
    }
2909
2910
6.62k
    return ret;
2911
6.62k
}
2912
2913
/************************************************************************/
2914
/*                GDALAlgorithm::InstantiateSubAlgorithm                */
2915
/************************************************************************/
2916
2917
std::unique_ptr<GDALAlgorithm>
2918
GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
2919
                                       bool suggestionAllowed) const
2920
19.8k
{
2921
19.8k
    auto ret = m_subAlgRegistry.Instantiate(name);
2922
19.8k
    auto childCallPath = m_callPath;
2923
19.8k
    childCallPath.push_back(name);
2924
19.8k
    if (!ret)
2925
19.8k
    {
2926
19.8k
        ret = GDALGlobalAlgorithmRegistry::GetSingleton()
2927
19.8k
                  .InstantiateDeclaredSubAlgorithm(childCallPath);
2928
19.8k
    }
2929
19.8k
    if (ret)
2930
13.2k
    {
2931
13.2k
        ret->SetCallPath(childCallPath);
2932
13.2k
    }
2933
6.64k
    else if (suggestionAllowed)
2934
6.64k
    {
2935
6.64k
        std::string bestCandidate;
2936
6.64k
        size_t bestDistance = std::numeric_limits<size_t>::max();
2937
6.64k
        for (const std::string &candidate : GetSubAlgorithmNames())
2938
119
        {
2939
119
            const size_t distance =
2940
119
                CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
2941
119
                                       /* transpositionAllowed = */ true);
2942
119
            if (distance < bestDistance)
2943
51
            {
2944
51
                bestCandidate = candidate;
2945
51
                bestDistance = distance;
2946
51
            }
2947
68
            else if (distance == bestDistance)
2948
51
            {
2949
51
                bestCandidate.clear();
2950
51
            }
2951
119
        }
2952
6.64k
        if (!bestCandidate.empty() && bestDistance <= 2)
2953
0
        {
2954
0
            CPLError(CE_Failure, CPLE_AppDefined,
2955
0
                     "Algorithm '%s' is unknown. Do you mean '%s'?",
2956
0
                     name.c_str(), bestCandidate.c_str());
2957
0
        }
2958
6.64k
    }
2959
19.8k
    return ret;
2960
19.8k
}
2961
2962
/************************************************************************/
2963
/*             GDALAlgorithm::GetSuggestionForArgumentName()            */
2964
/************************************************************************/
2965
2966
std::string
2967
GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
2968
0
{
2969
0
    if (osName.size() >= 3)
2970
0
    {
2971
0
        std::string bestCandidate;
2972
0
        size_t bestDistance = std::numeric_limits<size_t>::max();
2973
0
        for (const auto &[key, value] : m_mapLongNameToArg)
2974
0
        {
2975
0
            CPL_IGNORE_RET_VAL(value);
2976
0
            const size_t distance = CPLLevenshteinDistance(
2977
0
                osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
2978
0
            if (distance < bestDistance)
2979
0
            {
2980
0
                bestCandidate = key;
2981
0
                bestDistance = distance;
2982
0
            }
2983
0
            else if (distance == bestDistance)
2984
0
            {
2985
0
                bestCandidate.clear();
2986
0
            }
2987
0
        }
2988
0
        if (!bestCandidate.empty() &&
2989
0
            bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
2990
0
        {
2991
0
            return bestCandidate;
2992
0
        }
2993
0
    }
2994
0
    return std::string();
2995
0
}
2996
2997
/************************************************************************/
2998
/*                      GDALAlgorithm::GetArg()                         */
2999
/************************************************************************/
3000
3001
const GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3002
                                              bool suggestionAllowed) const
3003
78.1k
{
3004
78.1k
    const auto nPos = osName.find_first_not_of('-');
3005
78.1k
    if (nPos == std::string::npos)
3006
838
        return nullptr;
3007
77.2k
    const std::string osKey = osName.substr(nPos);
3008
77.2k
    {
3009
77.2k
        const auto oIter = m_mapLongNameToArg.find(osKey);
3010
77.2k
        if (oIter != m_mapLongNameToArg.end())
3011
7.95k
            return oIter->second;
3012
77.2k
    }
3013
69.3k
    {
3014
69.3k
        const auto oIter = m_mapShortNameToArg.find(osKey);
3015
69.3k
        if (oIter != m_mapShortNameToArg.end())
3016
13.2k
            return oIter->second;
3017
69.3k
    }
3018
3019
56.0k
    if (suggestionAllowed)
3020
0
    {
3021
0
        const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3022
0
        ;
3023
0
        if (!bestCandidate.empty())
3024
0
        {
3025
0
            CPLError(CE_Failure, CPLE_AppDefined,
3026
0
                     "Argument '%s' is unknown. Do you mean '%s'?",
3027
0
                     osName.c_str(), bestCandidate.c_str());
3028
0
        }
3029
0
    }
3030
3031
56.0k
    return nullptr;
3032
69.3k
}
3033
3034
/************************************************************************/
3035
/*                   GDALAlgorithm::AddAliasFor()                       */
3036
/************************************************************************/
3037
3038
//! @cond Doxygen_Suppress
3039
void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3040
                                const std::string &alias)
3041
13.2k
{
3042
13.2k
    if (cpl::contains(m_mapLongNameToArg, alias))
3043
0
    {
3044
0
        ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3045
0
                    alias.c_str());
3046
0
    }
3047
13.2k
    else
3048
13.2k
    {
3049
13.2k
        m_mapLongNameToArg[alias] = arg;
3050
13.2k
    }
3051
13.2k
}
3052
3053
//! @endcond
3054
3055
/************************************************************************/
3056
/*                 GDALAlgorithm::AddShortNameAliasFor()                */
3057
/************************************************************************/
3058
3059
//! @cond Doxygen_Suppress
3060
void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3061
                                         char shortNameAlias)
3062
0
{
3063
0
    std::string alias;
3064
0
    alias += shortNameAlias;
3065
0
    if (cpl::contains(m_mapShortNameToArg, alias))
3066
0
    {
3067
0
        ReportError(CE_Failure, CPLE_AppDefined,
3068
0
                    "Short name '%s' already declared.", alias.c_str());
3069
0
    }
3070
0
    else
3071
0
    {
3072
0
        m_mapShortNameToArg[alias] = arg;
3073
0
    }
3074
0
}
3075
3076
//! @endcond
3077
3078
/************************************************************************/
3079
/*                   GDALAlgorithm::SetPositional()                     */
3080
/************************************************************************/
3081
3082
//! @cond Doxygen_Suppress
3083
void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3084
6.61k
{
3085
6.61k
    CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3086
6.61k
                        arg) == m_positionalArgs.end());
3087
6.61k
    m_positionalArgs.push_back(arg);
3088
6.61k
}
3089
3090
//! @endcond
3091
3092
/************************************************************************/
3093
/*                  GDALAlgorithm::HasSubAlgorithms()                   */
3094
/************************************************************************/
3095
3096
bool GDALAlgorithm::HasSubAlgorithms() const
3097
0
{
3098
0
    if (!m_subAlgRegistry.empty())
3099
0
        return true;
3100
0
    return !GDALGlobalAlgorithmRegistry::GetSingleton()
3101
0
                .GetDeclaredSubAlgorithmNames(m_callPath)
3102
0
                .empty();
3103
0
}
3104
3105
/************************************************************************/
3106
/*                GDALAlgorithm::GetSubAlgorithmNames()                 */
3107
/************************************************************************/
3108
3109
std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3110
6.64k
{
3111
6.64k
    std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3112
6.64k
    const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3113
6.64k
                           .GetDeclaredSubAlgorithmNames(m_callPath);
3114
6.64k
    ret.insert(ret.end(), other.begin(), other.end());
3115
6.64k
    if (!other.empty())
3116
44
        std::sort(ret.begin(), ret.end());
3117
6.64k
    return ret;
3118
6.64k
}
3119
3120
/************************************************************************/
3121
/*                     GDALAlgorithm::AddArg()                          */
3122
/************************************************************************/
3123
3124
GDALInConstructionAlgorithmArg &
3125
GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3126
99.7k
{
3127
99.7k
    auto argRaw = arg.get();
3128
99.7k
    const auto &longName = argRaw->GetName();
3129
99.7k
    if (!longName.empty())
3130
99.7k
    {
3131
99.7k
        if (longName[0] == '-')
3132
0
        {
3133
0
            ReportError(CE_Failure, CPLE_AppDefined,
3134
0
                        "Long name '%s' should not start with '-'",
3135
0
                        longName.c_str());
3136
0
        }
3137
99.7k
        if (longName.find('=') != std::string::npos)
3138
0
        {
3139
0
            ReportError(CE_Failure, CPLE_AppDefined,
3140
0
                        "Long name '%s' should not contain a '=' character",
3141
0
                        longName.c_str());
3142
0
        }
3143
99.7k
        if (cpl::contains(m_mapLongNameToArg, longName))
3144
0
        {
3145
0
            ReportError(CE_Failure, CPLE_AppDefined,
3146
0
                        "Long name '%s' already declared", longName.c_str());
3147
0
        }
3148
99.7k
        m_mapLongNameToArg[longName] = argRaw;
3149
99.7k
    }
3150
99.7k
    const auto &shortName = argRaw->GetShortName();
3151
99.7k
    if (!shortName.empty())
3152
33.2k
    {
3153
33.2k
        if (shortName.size() != 1 ||
3154
33.2k
            !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3155
33.2k
              (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3156
33.2k
              (shortName[0] >= '0' && shortName[0] <= '9')))
3157
0
        {
3158
0
            ReportError(CE_Failure, CPLE_AppDefined,
3159
0
                        "Short name '%s' should be a single letter or digit",
3160
0
                        shortName.c_str());
3161
0
        }
3162
33.2k
        if (cpl::contains(m_mapShortNameToArg, shortName))
3163
0
        {
3164
0
            ReportError(CE_Failure, CPLE_AppDefined,
3165
0
                        "Short name '%s' already declared", shortName.c_str());
3166
0
        }
3167
33.2k
        m_mapShortNameToArg[shortName] = argRaw;
3168
33.2k
    }
3169
99.7k
    m_args.emplace_back(std::move(arg));
3170
99.7k
    return *(
3171
99.7k
        cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3172
99.7k
}
3173
3174
GDALInConstructionAlgorithmArg &
3175
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3176
                      const std::string &helpMessage, bool *pValue)
3177
59.9k
{
3178
59.9k
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3179
59.9k
        this,
3180
59.9k
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3181
59.9k
        pValue));
3182
59.9k
}
3183
3184
GDALInConstructionAlgorithmArg &
3185
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3186
                      const std::string &helpMessage, std::string *pValue)
3187
13.2k
{
3188
13.2k
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3189
13.2k
        this,
3190
13.2k
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3191
13.2k
        pValue));
3192
13.2k
}
3193
3194
GDALInConstructionAlgorithmArg &
3195
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3196
                      const std::string &helpMessage, int *pValue)
3197
1
{
3198
1
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3199
1
        this,
3200
1
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3201
1
        pValue));
3202
1
}
3203
3204
GDALInConstructionAlgorithmArg &
3205
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3206
                      const std::string &helpMessage, double *pValue)
3207
2
{
3208
2
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3209
2
        this,
3210
2
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3211
2
        pValue));
3212
2
}
3213
3214
GDALInConstructionAlgorithmArg &
3215
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3216
                      const std::string &helpMessage,
3217
                      GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3218
6.61k
{
3219
6.61k
    auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3220
6.61k
                           this,
3221
6.61k
                           GDALAlgorithmArgDecl(longName, chShortName,
3222
6.61k
                                                helpMessage, GAAT_DATASET),
3223
6.61k
                           pValue))
3224
6.61k
                    .SetDatasetType(type);
3225
6.61k
    pValue->SetOwnerArgument(&arg);
3226
6.61k
    return arg;
3227
6.61k
}
3228
3229
GDALInConstructionAlgorithmArg &
3230
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3231
                      const std::string &helpMessage,
3232
                      std::vector<std::string> *pValue)
3233
19.9k
{
3234
19.9k
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3235
19.9k
        this,
3236
19.9k
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3237
19.9k
                             GAAT_STRING_LIST),
3238
19.9k
        pValue));
3239
19.9k
}
3240
3241
GDALInConstructionAlgorithmArg &
3242
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3243
                      const std::string &helpMessage, std::vector<int> *pValue)
3244
0
{
3245
0
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3246
0
        this,
3247
0
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3248
0
                             GAAT_INTEGER_LIST),
3249
0
        pValue));
3250
0
}
3251
3252
GDALInConstructionAlgorithmArg &
3253
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3254
                      const std::string &helpMessage,
3255
                      std::vector<double> *pValue)
3256
3
{
3257
3
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3258
3
        this,
3259
3
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3260
3
                             GAAT_REAL_LIST),
3261
3
        pValue));
3262
3
}
3263
3264
GDALInConstructionAlgorithmArg &
3265
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3266
                      const std::string &helpMessage,
3267
                      std::vector<GDALArgDatasetValue> *pValue,
3268
                      GDALArgDatasetType type)
3269
1
{
3270
1
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3271
1
                      this,
3272
1
                      GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3273
1
                                           GAAT_DATASET_LIST),
3274
1
                      pValue))
3275
1
        .SetDatasetType(type);
3276
1
}
3277
3278
/************************************************************************/
3279
/*                               MsgOrDefault()                         */
3280
/************************************************************************/
3281
3282
inline const char *MsgOrDefault(const char *helpMessage,
3283
                                const char *defaultMessage)
3284
19.8k
{
3285
19.8k
    return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3286
19.8k
}
3287
3288
/************************************************************************/
3289
/*          GDALAlgorithm::SetAutoCompleteFunctionForFilename()         */
3290
/************************************************************************/
3291
3292
/* static */
3293
void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3294
    GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3295
6.61k
{
3296
6.61k
    arg.SetAutoCompleteFunction(
3297
6.61k
        [type](const std::string &currentValue) -> std::vector<std::string>
3298
6.61k
        {
3299
0
            std::vector<std::string> oRet;
3300
3301
0
            {
3302
0
                CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3303
0
                VSIStatBufL sStat;
3304
0
                if (!currentValue.empty() && currentValue.back() != '/' &&
3305
0
                    VSIStatL(currentValue.c_str(), &sStat) == 0)
3306
0
                {
3307
0
                    return oRet;
3308
0
                }
3309
0
            }
3310
3311
0
            auto poDM = GetGDALDriverManager();
3312
0
            std::set<std::string> oExtensions;
3313
0
            if (type)
3314
0
            {
3315
0
                for (int i = 0; i < poDM->GetDriverCount(); ++i)
3316
0
                {
3317
0
                    auto poDriver = poDM->GetDriver(i);
3318
0
                    if (((type & GDAL_OF_RASTER) != 0 &&
3319
0
                         poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3320
0
                        ((type & GDAL_OF_VECTOR) != 0 &&
3321
0
                         poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3322
0
                        ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3323
0
                         poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3324
0
                    {
3325
0
                        const char *pszExtensions =
3326
0
                            poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3327
0
                        if (pszExtensions)
3328
0
                        {
3329
0
                            const CPLStringList aosExts(
3330
0
                                CSLTokenizeString2(pszExtensions, " ", 0));
3331
0
                            for (const char *pszExt : cpl::Iterate(aosExts))
3332
0
                                oExtensions.insert(CPLString(pszExt).tolower());
3333
0
                        }
3334
0
                    }
3335
0
                }
3336
0
            }
3337
3338
0
            std::string osDir;
3339
0
            const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3340
0
            std::string osPrefix;
3341
0
            if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3342
0
            {
3343
0
                for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3344
0
                {
3345
0
                    if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3346
0
                    {
3347
0
                        osPrefix = pszPrefix;
3348
0
                        break;
3349
0
                    }
3350
0
                }
3351
0
                if (osPrefix.empty())
3352
0
                    return aosVSIPrefixes;
3353
0
                if (currentValue == osPrefix)
3354
0
                    osDir = osPrefix;
3355
0
            }
3356
0
            if (osDir.empty())
3357
0
            {
3358
0
                osDir = CPLGetDirnameSafe(currentValue.c_str());
3359
0
                if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3360
0
                    osDir = std::move(osPrefix);
3361
0
            }
3362
3363
0
            auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3364
0
            const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3365
0
            if (currentValue.empty())
3366
0
                osDir.clear();
3367
0
            const std::string currentFilename =
3368
0
                CPLGetFilename(currentValue.c_str());
3369
0
            if (psDir)
3370
0
            {
3371
0
                while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3372
0
                {
3373
0
                    if ((currentFilename.empty() ||
3374
0
                         STARTS_WITH(psEntry->pszName,
3375
0
                                     currentFilename.c_str())) &&
3376
0
                        strcmp(psEntry->pszName, ".") != 0 &&
3377
0
                        strcmp(psEntry->pszName, "..") != 0 &&
3378
0
                        (oExtensions.empty() ||
3379
0
                         !strstr(psEntry->pszName, ".aux.xml")))
3380
0
                    {
3381
0
                        if (oExtensions.empty() ||
3382
0
                            cpl::contains(
3383
0
                                oExtensions,
3384
0
                                CPLString(CPLGetExtensionSafe(psEntry->pszName))
3385
0
                                    .tolower()) ||
3386
0
                            VSI_ISDIR(psEntry->nMode))
3387
0
                        {
3388
0
                            std::string osVal;
3389
0
                            if (osDir.empty() || osDir == ".")
3390
0
                                osVal = psEntry->pszName;
3391
0
                            else
3392
0
                                osVal = CPLFormFilenameSafe(
3393
0
                                    osDir.c_str(), psEntry->pszName, nullptr);
3394
0
                            if (VSI_ISDIR(psEntry->nMode))
3395
0
                                osVal += osSep;
3396
0
                            oRet.push_back(std::move(osVal));
3397
0
                        }
3398
0
                    }
3399
0
                }
3400
0
                VSICloseDir(psDir);
3401
0
            }
3402
0
            return oRet;
3403
0
        });
3404
6.61k
}
3405
3406
/************************************************************************/
3407
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
3408
/************************************************************************/
3409
3410
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3411
    GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3412
    bool positionalAndRequired, const char *helpMessage)
3413
6.61k
{
3414
6.61k
    auto &arg = AddArg(
3415
6.61k
        GDAL_ARG_NAME_INPUT, 'i',
3416
6.61k
        MsgOrDefault(helpMessage,
3417
6.61k
                     CPLSPrintf("Input %s dataset",
3418
6.61k
                                GDALAlgorithmArgDatasetTypeName(type).c_str())),
3419
6.61k
        pValue, type);
3420
6.61k
    if (positionalAndRequired)
3421
6.61k
        arg.SetPositional().SetRequired();
3422
3423
6.61k
    SetAutoCompleteFunctionForFilename(arg, type);
3424
3425
6.61k
    AddValidationAction(
3426
6.61k
        [pValue]()
3427
6.61k
        {
3428
6.59k
            if (pValue->GetName() == "-")
3429
0
                pValue->Set("/vsistdin/");
3430
6.59k
            return true;
3431
6.59k
        });
3432
3433
6.61k
    return arg;
3434
6.61k
}
3435
3436
/************************************************************************/
3437
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
3438
/************************************************************************/
3439
3440
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3441
    std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
3442
    bool positionalAndRequired, const char *helpMessage)
3443
1
{
3444
1
    auto &arg = AddArg(
3445
1
        GDAL_ARG_NAME_INPUT, 'i',
3446
1
        MsgOrDefault(helpMessage,
3447
1
                     CPLSPrintf("Input %s datasets",
3448
1
                                GDALAlgorithmArgDatasetTypeName(type).c_str())),
3449
1
        pValue, type);
3450
1
    if (positionalAndRequired)
3451
1
        arg.SetPositional().SetRequired();
3452
3453
1
    AddValidationAction(
3454
1
        [pValue]()
3455
1
        {
3456
0
            for (auto &val : *pValue)
3457
0
            {
3458
0
                if (val.GetName() == "-")
3459
0
                    val.Set("/vsistdin/");
3460
0
            }
3461
0
            return true;
3462
0
        });
3463
1
    return arg;
3464
1
}
3465
3466
/************************************************************************/
3467
/*                 GDALAlgorithm::AddOutputDatasetArg()                 */
3468
/************************************************************************/
3469
3470
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
3471
    GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3472
    bool positionalAndRequired, const char *helpMessage)
3473
1
{
3474
1
    auto &arg =
3475
1
        AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
3476
1
               MsgOrDefault(
3477
1
                   helpMessage,
3478
1
                   CPLSPrintf("Output %s dataset",
3479
1
                              GDALAlgorithmArgDatasetTypeName(type).c_str())),
3480
1
               pValue, type)
3481
1
            .SetIsInput(true)
3482
1
            .SetIsOutput(true)
3483
1
            .SetDatasetInputFlags(GADV_NAME)
3484
1
            .SetDatasetOutputFlags(GADV_OBJECT);
3485
1
    if (positionalAndRequired)
3486
1
        arg.SetPositional().SetRequired();
3487
3488
1
    AddValidationAction(
3489
1
        [this, &arg, pValue]()
3490
1
        {
3491
0
            if (pValue->GetName() == "-")
3492
0
                pValue->Set("/vsistdout/");
3493
3494
0
            auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3495
0
            if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
3496
0
                (!outputFormatArg->IsExplicitlySet() ||
3497
0
                 outputFormatArg->Get<std::string>().empty()) &&
3498
0
                arg.IsExplicitlySet())
3499
0
            {
3500
0
                const auto vrtCompatible =
3501
0
                    outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3502
0
                if (vrtCompatible && !vrtCompatible->empty() &&
3503
0
                    vrtCompatible->front() == "false" &&
3504
0
                    EQUAL(
3505
0
                        CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
3506
0
                        "VRT"))
3507
0
                {
3508
0
                    ReportError(
3509
0
                        CE_Failure, CPLE_NotSupported,
3510
0
                        "VRT output is not supported.%s",
3511
0
                        outputFormatArg->GetDescription().find("GDALG") !=
3512
0
                                std::string::npos
3513
0
                            ? " Consider using the GDALG driver instead (files "
3514
0
                              "with .gdalg.json extension)"
3515
0
                            : "");
3516
0
                    return false;
3517
0
                }
3518
0
                else if (pValue->GetName().size() > strlen(".gdalg.json") &&
3519
0
                         EQUAL(pValue->GetName()
3520
0
                                   .substr(pValue->GetName().size() -
3521
0
                                           strlen(".gdalg.json"))
3522
0
                                   .c_str(),
3523
0
                               ".gdalg.json") &&
3524
0
                         outputFormatArg->GetDescription().find("GDALG") ==
3525
0
                             std::string::npos)
3526
0
                {
3527
0
                    ReportError(CE_Failure, CPLE_NotSupported,
3528
0
                                "GDALG output is not supported");
3529
0
                    return false;
3530
0
                }
3531
0
            }
3532
0
            return true;
3533
0
        });
3534
3535
1
    return arg;
3536
1
}
3537
3538
/************************************************************************/
3539
/*                 GDALAlgorithm::AddOverwriteArg()                     */
3540
/************************************************************************/
3541
3542
GDALInConstructionAlgorithmArg &
3543
GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
3544
1
{
3545
1
    return AddArg(GDAL_ARG_NAME_OVERWRITE, 0,
3546
1
                  MsgOrDefault(
3547
1
                      helpMessage,
3548
1
                      _("Whether overwriting existing output is allowed")),
3549
1
                  pValue)
3550
1
        .SetDefault(false);
3551
1
}
3552
3553
/************************************************************************/
3554
/*                GDALAlgorithm::AddOverwriteLayerArg()                 */
3555
/************************************************************************/
3556
3557
GDALInConstructionAlgorithmArg &
3558
GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
3559
1
{
3560
1
    AddValidationAction(
3561
1
        [this]
3562
1
        {
3563
0
            auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3564
0
            if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3565
0
            {
3566
0
                ReportError(CE_Failure, CPLE_AppDefined,
3567
0
                            "--update argument must exist for "
3568
0
                            "--overwrite-layer, even if hidden");
3569
0
                return false;
3570
0
            }
3571
0
            return true;
3572
0
        });
3573
1
    return AddArg(GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
3574
1
                  MsgOrDefault(
3575
1
                      helpMessage,
3576
1
                      _("Whether overwriting existing output is allowed")),
3577
1
                  pValue)
3578
1
        .SetDefault(false)
3579
1
        .AddAction(
3580
1
            [this]
3581
1
            {
3582
0
                auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3583
0
                if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3584
0
                {
3585
0
                    updateArg->Set(true);
3586
0
                }
3587
0
            });
3588
1
}
3589
3590
/************************************************************************/
3591
/*                 GDALAlgorithm::AddUpdateArg()                        */
3592
/************************************************************************/
3593
3594
GDALInConstructionAlgorithmArg &
3595
GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
3596
1
{
3597
1
    return AddArg(GDAL_ARG_NAME_UPDATE, 0,
3598
1
                  MsgOrDefault(
3599
1
                      helpMessage,
3600
1
                      _("Whether to open existing dataset in update mode")),
3601
1
                  pValue)
3602
1
        .SetDefault(false);
3603
1
}
3604
3605
/************************************************************************/
3606
/*                GDALAlgorithm::AddAppendLayerArg()                    */
3607
/************************************************************************/
3608
3609
GDALInConstructionAlgorithmArg &
3610
GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
3611
0
{
3612
0
    AddValidationAction(
3613
0
        [this]
3614
0
        {
3615
0
            auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3616
0
            if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3617
0
            {
3618
0
                ReportError(CE_Failure, CPLE_AppDefined,
3619
0
                            "--update argument must exist for --append, even "
3620
0
                            "if hidden");
3621
0
                return false;
3622
0
            }
3623
0
            return true;
3624
0
        });
3625
0
    return AddArg(GDAL_ARG_NAME_APPEND, 0,
3626
0
                  MsgOrDefault(
3627
0
                      helpMessage,
3628
0
                      _("Whether appending to existing layer is allowed")),
3629
0
                  pValue)
3630
0
        .SetDefault(false)
3631
0
        .AddAction(
3632
0
            [this]
3633
0
            {
3634
0
                auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3635
0
                if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3636
0
                {
3637
0
                    updateArg->Set(true);
3638
0
                }
3639
0
            });
3640
0
}
3641
3642
/************************************************************************/
3643
/*                 GDALAlgorithm::AddOptionsSuggestions()               */
3644
/************************************************************************/
3645
3646
/* static */
3647
bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
3648
                                          const std::string &currentValue,
3649
                                          std::vector<std::string> &oRet)
3650
0
{
3651
0
    if (!pszXML)
3652
0
        return false;
3653
0
    CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
3654
0
    if (!poTree)
3655
0
        return false;
3656
3657
0
    std::string typedOptionName = currentValue;
3658
0
    const auto posEqual = typedOptionName.find('=');
3659
0
    std::string typedValue;
3660
0
    if (posEqual != 0 && posEqual != std::string::npos)
3661
0
    {
3662
0
        typedValue = currentValue.substr(posEqual + 1);
3663
0
        typedOptionName.resize(posEqual);
3664
0
    }
3665
3666
0
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
3667
0
         psChild = psChild->psNext)
3668
0
    {
3669
0
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
3670
0
        if (pszName && typedOptionName == pszName &&
3671
0
            (strcmp(psChild->pszValue, "Option") == 0 ||
3672
0
             strcmp(psChild->pszValue, "Argument") == 0))
3673
0
        {
3674
0
            const char *pszType = CPLGetXMLValue(psChild, "type", "");
3675
0
            const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
3676
0
            const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
3677
0
            if (EQUAL(pszType, "string-select"))
3678
0
            {
3679
0
                for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
3680
0
                     psChild2 = psChild2->psNext)
3681
0
                {
3682
0
                    if (EQUAL(psChild2->pszValue, "Value"))
3683
0
                    {
3684
0
                        oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
3685
0
                    }
3686
0
                }
3687
0
            }
3688
0
            else if (EQUAL(pszType, "boolean"))
3689
0
            {
3690
0
                if (typedValue == "YES" || typedValue == "NO")
3691
0
                {
3692
0
                    oRet.push_back(currentValue);
3693
0
                    return true;
3694
0
                }
3695
0
                oRet.push_back("NO");
3696
0
                oRet.push_back("YES");
3697
0
            }
3698
0
            else if (EQUAL(pszType, "int"))
3699
0
            {
3700
0
                if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
3701
0
                    atoi(pszMax) - atoi(pszMin) < 25)
3702
0
                {
3703
0
                    const int nMax = atoi(pszMax);
3704
0
                    for (int i = atoi(pszMin); i <= nMax; ++i)
3705
0
                        oRet.push_back(std::to_string(i));
3706
0
                }
3707
0
            }
3708
3709
0
            if (oRet.empty())
3710
0
            {
3711
0
                if (pszMin && pszMax)
3712
0
                {
3713
0
                    oRet.push_back(std::string("##"));
3714
0
                    oRet.push_back(std::string("validity range: [")
3715
0
                                       .append(pszMin)
3716
0
                                       .append(",")
3717
0
                                       .append(pszMax)
3718
0
                                       .append("]"));
3719
0
                }
3720
0
                else if (pszMin)
3721
0
                {
3722
0
                    oRet.push_back(std::string("##"));
3723
0
                    oRet.push_back(
3724
0
                        std::string("validity range: >= ").append(pszMin));
3725
0
                }
3726
0
                else if (pszMax)
3727
0
                {
3728
0
                    oRet.push_back(std::string("##"));
3729
0
                    oRet.push_back(
3730
0
                        std::string("validity range: <= ").append(pszMax));
3731
0
                }
3732
0
                else if (const char *pszDescription =
3733
0
                             CPLGetXMLValue(psChild, "description", nullptr))
3734
0
                {
3735
0
                    oRet.push_back(std::string("##"));
3736
0
                    oRet.push_back(std::string("type: ")
3737
0
                                       .append(pszType)
3738
0
                                       .append(", description: ")
3739
0
                                       .append(pszDescription));
3740
0
                }
3741
0
            }
3742
3743
0
            return true;
3744
0
        }
3745
0
    }
3746
3747
0
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
3748
0
         psChild = psChild->psNext)
3749
0
    {
3750
0
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
3751
0
        if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
3752
0
                        strcmp(psChild->pszValue, "Argument") == 0))
3753
0
        {
3754
0
            const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
3755
0
            if (!pszScope ||
3756
0
                (EQUAL(pszScope, "raster") &&
3757
0
                 (datasetType & GDAL_OF_RASTER) != 0) ||
3758
0
                (EQUAL(pszScope, "vector") &&
3759
0
                 (datasetType & GDAL_OF_VECTOR) != 0))
3760
0
            {
3761
0
                oRet.push_back(std::string(pszName).append("="));
3762
0
            }
3763
0
        }
3764
0
    }
3765
3766
0
    return false;
3767
0
}
3768
3769
/************************************************************************/
3770
/*                 GDALAlgorithm::AddOpenOptionsArg()                   */
3771
/************************************************************************/
3772
3773
GDALInConstructionAlgorithmArg &
3774
GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
3775
                                 const char *helpMessage)
3776
0
{
3777
0
    auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
3778
0
                       MsgOrDefault(helpMessage, _("Open options")), pValue)
3779
0
                    .AddAlias("oo")
3780
0
                    .SetMetaVar("<KEY>=<VALUE>")
3781
0
                    .SetPackedValuesAllowed(false)
3782
0
                    .SetCategory(GAAC_ADVANCED);
3783
3784
0
    arg.AddValidationAction([this, &arg]()
3785
0
                            { return ParseAndValidateKeyValue(arg); });
3786
3787
0
    arg.SetAutoCompleteFunction(
3788
0
        [this](const std::string &currentValue)
3789
0
        {
3790
0
            std::vector<std::string> oRet;
3791
3792
0
            int datasetType =
3793
0
                GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
3794
0
            auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
3795
0
            if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
3796
0
                             inputArg->GetType() == GAAT_DATASET_LIST))
3797
0
            {
3798
0
                datasetType = inputArg->GetDatasetType();
3799
0
            }
3800
3801
0
            auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3802
0
            if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
3803
0
                inputFormat->IsExplicitlySet())
3804
0
            {
3805
0
                const auto &aosAllowedDrivers =
3806
0
                    inputFormat->Get<std::vector<std::string>>();
3807
0
                if (aosAllowedDrivers.size() == 1)
3808
0
                {
3809
0
                    auto poDriver = GetGDALDriverManager()->GetDriverByName(
3810
0
                        aosAllowedDrivers[0].c_str());
3811
0
                    if (poDriver)
3812
0
                    {
3813
0
                        AddOptionsSuggestions(
3814
0
                            poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
3815
0
                            datasetType, currentValue, oRet);
3816
0
                    }
3817
0
                    return oRet;
3818
0
                }
3819
0
            }
3820
3821
0
            const auto AddSuggestions =
3822
0
                [datasetType, &currentValue,
3823
0
                 &oRet](const GDALArgDatasetValue &datasetValue)
3824
0
            {
3825
0
                auto poDM = GetGDALDriverManager();
3826
3827
0
                const auto &osDSName = datasetValue.GetName();
3828
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
3829
0
                if (!osExt.empty())
3830
0
                {
3831
0
                    std::set<std::string> oVisitedExtensions;
3832
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
3833
0
                    {
3834
0
                        auto poDriver = poDM->GetDriver(i);
3835
0
                        if (((datasetType & GDAL_OF_RASTER) != 0 &&
3836
0
                             poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3837
0
                            ((datasetType & GDAL_OF_VECTOR) != 0 &&
3838
0
                             poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3839
0
                            ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3840
0
                             poDriver->GetMetadataItem(
3841
0
                                 GDAL_DCAP_MULTIDIM_RASTER)))
3842
0
                        {
3843
0
                            const char *pszExtensions =
3844
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3845
0
                            if (pszExtensions)
3846
0
                            {
3847
0
                                const CPLStringList aosExts(
3848
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
3849
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
3850
0
                                {
3851
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
3852
0
                                        !cpl::contains(oVisitedExtensions,
3853
0
                                                       pszExt))
3854
0
                                    {
3855
0
                                        oVisitedExtensions.insert(pszExt);
3856
0
                                        if (AddOptionsSuggestions(
3857
0
                                                poDriver->GetMetadataItem(
3858
0
                                                    GDAL_DMD_OPENOPTIONLIST),
3859
0
                                                datasetType, currentValue,
3860
0
                                                oRet))
3861
0
                                        {
3862
0
                                            return;
3863
0
                                        }
3864
0
                                        break;
3865
0
                                    }
3866
0
                                }
3867
0
                            }
3868
0
                        }
3869
0
                    }
3870
0
                }
3871
0
            };
3872
3873
0
            if (inputArg && inputArg->GetType() == GAAT_DATASET)
3874
0
            {
3875
0
                auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
3876
0
                AddSuggestions(datasetValue);
3877
0
            }
3878
0
            else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
3879
0
            {
3880
0
                auto &datasetValues =
3881
0
                    inputArg->Get<std::vector<GDALArgDatasetValue>>();
3882
0
                if (datasetValues.size() == 1)
3883
0
                    AddSuggestions(datasetValues[0]);
3884
0
            }
3885
3886
0
            return oRet;
3887
0
        });
3888
3889
0
    return arg;
3890
0
}
3891
3892
/************************************************************************/
3893
/*                            ValidateFormat()                          */
3894
/************************************************************************/
3895
3896
bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
3897
                                   bool bStreamAllowed,
3898
                                   bool bGDALGAllowed) const
3899
2.21k
{
3900
2.21k
    if (arg.GetChoices().empty())
3901
0
    {
3902
0
        const auto Validate =
3903
0
            [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
3904
0
        {
3905
0
            if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
3906
0
                return true;
3907
3908
0
            if (EQUAL(val.c_str(), "GDALG") &&
3909
0
                arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
3910
0
            {
3911
0
                if (bGDALGAllowed)
3912
0
                {
3913
0
                    return true;
3914
0
                }
3915
0
                else
3916
0
                {
3917
0
                    ReportError(CE_Failure, CPLE_NotSupported,
3918
0
                                "GDALG output is not supported.");
3919
0
                    return false;
3920
0
                }
3921
0
            }
3922
3923
0
            const auto vrtCompatible =
3924
0
                arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3925
0
            if (vrtCompatible && !vrtCompatible->empty() &&
3926
0
                vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
3927
0
            {
3928
0
                ReportError(CE_Failure, CPLE_NotSupported,
3929
0
                            "VRT output is not supported.%s",
3930
0
                            bGDALGAllowed
3931
0
                                ? " Consider using the GDALG driver instead "
3932
0
                                  "(files with .gdalg.json extension)."
3933
0
                                : "");
3934
0
                return false;
3935
0
            }
3936
3937
0
            auto hDriver = GDALGetDriverByName(val.c_str());
3938
0
            if (!hDriver)
3939
0
            {
3940
0
                auto poMissingDriver =
3941
0
                    GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
3942
0
                if (poMissingDriver)
3943
0
                {
3944
0
                    const std::string msg =
3945
0
                        GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
3946
0
                    ReportError(CE_Failure, CPLE_AppDefined,
3947
0
                                "Invalid value for argument '%s'. Driver '%s' "
3948
0
                                "not found but it known. However plugin %s",
3949
0
                                arg.GetName().c_str(), val.c_str(),
3950
0
                                msg.c_str());
3951
0
                }
3952
0
                else
3953
0
                {
3954
0
                    ReportError(CE_Failure, CPLE_AppDefined,
3955
0
                                "Invalid value for argument '%s'. Driver '%s' "
3956
0
                                "does not exist.",
3957
0
                                arg.GetName().c_str(), val.c_str());
3958
0
                }
3959
0
                return false;
3960
0
            }
3961
3962
0
            const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
3963
0
            if (caps)
3964
0
            {
3965
0
                for (const std::string &cap : *caps)
3966
0
                {
3967
0
                    const char *pszVal =
3968
0
                        GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
3969
0
                    if (!(pszVal && pszVal[0]))
3970
0
                    {
3971
0
                        if (cap == GDAL_DCAP_CREATECOPY &&
3972
0
                            std::find(caps->begin(), caps->end(),
3973
0
                                      GDAL_DCAP_RASTER) != caps->end() &&
3974
0
                            GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
3975
0
                                                nullptr) &&
3976
0
                            GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
3977
0
                                                nullptr))
3978
0
                        {
3979
                            // if it supports Create, it supports CreateCopy
3980
0
                        }
3981
0
                        else if (cap == GDAL_DMD_EXTENSIONS)
3982
0
                        {
3983
0
                            ReportError(
3984
0
                                CE_Failure, CPLE_AppDefined,
3985
0
                                "Invalid value for argument '%s'. Driver '%s' "
3986
0
                                "does "
3987
0
                                "not advertise any file format extension.",
3988
0
                                arg.GetName().c_str(), val.c_str());
3989
0
                            return false;
3990
0
                        }
3991
0
                        else
3992
0
                        {
3993
0
                            ReportError(
3994
0
                                CE_Failure, CPLE_AppDefined,
3995
0
                                "Invalid value for argument '%s'. Driver '%s' "
3996
0
                                "does "
3997
0
                                "not expose the required '%s' capability.",
3998
0
                                arg.GetName().c_str(), val.c_str(),
3999
0
                                cap.c_str());
4000
0
                            return false;
4001
0
                        }
4002
0
                    }
4003
0
                }
4004
0
            }
4005
0
            return true;
4006
0
        };
4007
4008
0
        if (arg.GetType() == GAAT_STRING)
4009
0
        {
4010
0
            return Validate(arg.Get<std::string>());
4011
0
        }
4012
0
        else if (arg.GetType() == GAAT_STRING_LIST)
4013
0
        {
4014
0
            for (const auto &val : arg.Get<std::vector<std::string>>())
4015
0
            {
4016
0
                if (!Validate(val))
4017
0
                    return false;
4018
0
            }
4019
0
        }
4020
0
    }
4021
4022
2.21k
    return true;
4023
2.21k
}
4024
4025
/************************************************************************/
4026
/*                    FormatAutoCompleteFunction()                      */
4027
/************************************************************************/
4028
4029
/* static */
4030
std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4031
    const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4032
0
{
4033
0
    std::vector<std::string> res;
4034
0
    auto poDM = GetGDALDriverManager();
4035
0
    const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4036
0
    const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4037
0
    for (int i = 0; i < poDM->GetDriverCount(); ++i)
4038
0
    {
4039
0
        auto poDriver = poDM->GetDriver(i);
4040
4041
0
        if (vrtCompatible && !vrtCompatible->empty() &&
4042
0
            vrtCompatible->front() == "false" &&
4043
0
            EQUAL(poDriver->GetDescription(), "VRT"))
4044
0
        {
4045
            // do nothing
4046
0
        }
4047
0
        else if (caps)
4048
0
        {
4049
0
            bool ok = true;
4050
0
            for (const std::string &cap : *caps)
4051
0
            {
4052
0
                if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4053
0
                {
4054
0
                    if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4055
0
                        !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4056
0
                    {
4057
0
                        ok = false;
4058
0
                        break;
4059
0
                    }
4060
0
                }
4061
0
                else if (const char *pszVal =
4062
0
                             poDriver->GetMetadataItem(cap.c_str());
4063
0
                         pszVal && pszVal[0])
4064
0
                {
4065
0
                }
4066
0
                else if (cap == GDAL_DCAP_CREATECOPY &&
4067
0
                         (std::find(caps->begin(), caps->end(),
4068
0
                                    GDAL_DCAP_RASTER) != caps->end() &&
4069
0
                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4070
0
                         poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4071
0
                {
4072
                    // if it supports Create, it supports CreateCopy
4073
0
                }
4074
0
                else
4075
0
                {
4076
0
                    ok = false;
4077
0
                    break;
4078
0
                }
4079
0
            }
4080
0
            if (ok)
4081
0
            {
4082
0
                res.push_back(poDriver->GetDescription());
4083
0
            }
4084
0
        }
4085
0
    }
4086
0
    if (bGDALGAllowed)
4087
0
        res.push_back("GDALG");
4088
0
    return res;
4089
0
}
4090
4091
/************************************************************************/
4092
/*                 GDALAlgorithm::AddInputFormatsArg()                  */
4093
/************************************************************************/
4094
4095
GDALInConstructionAlgorithmArg &
4096
GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4097
                                  const char *helpMessage)
4098
0
{
4099
0
    auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4100
0
                       MsgOrDefault(helpMessage, _("Input formats")), pValue)
4101
0
                    .AddAlias("if")
4102
0
                    .SetCategory(GAAC_ADVANCED);
4103
0
    arg.AddValidationAction([this, &arg]()
4104
0
                            { return ValidateFormat(arg, false, false); });
4105
0
    arg.SetAutoCompleteFunction(
4106
0
        [&arg](const std::string &)
4107
0
        { return FormatAutoCompleteFunction(arg, false, false); });
4108
0
    return arg;
4109
0
}
4110
4111
/************************************************************************/
4112
/*                 GDALAlgorithm::AddOutputFormatArg()                  */
4113
/************************************************************************/
4114
4115
GDALInConstructionAlgorithmArg &
4116
GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4117
                                  bool bGDALGAllowed, const char *helpMessage)
4118
6.61k
{
4119
6.61k
    auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4120
6.61k
                       MsgOrDefault(helpMessage,
4121
6.61k
                                    bGDALGAllowed
4122
6.61k
                                        ? _("Output format (\"GDALG\" allowed)")
4123
6.61k
                                        : _("Output format")),
4124
6.61k
                       pValue)
4125
6.61k
                    .AddAlias("of")
4126
6.61k
                    .AddAlias("format");
4127
6.61k
    arg.AddValidationAction(
4128
6.61k
        [this, &arg, bStreamAllowed, bGDALGAllowed]()
4129
6.61k
        { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4130
6.61k
    arg.SetAutoCompleteFunction(
4131
6.61k
        [&arg, bStreamAllowed, bGDALGAllowed](const std::string &) {
4132
0
            return FormatAutoCompleteFunction(arg, bStreamAllowed,
4133
0
                                              bGDALGAllowed);
4134
0
        });
4135
6.61k
    return arg;
4136
6.61k
}
4137
4138
/************************************************************************/
4139
/*                 GDALAlgorithm::AddOutputDataTypeArg()                */
4140
/************************************************************************/
4141
GDALInConstructionAlgorithmArg &
4142
GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4143
                                    const char *helpMessage)
4144
1
{
4145
1
    auto &arg =
4146
1
        AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4147
1
               MsgOrDefault(helpMessage, _("Output data type")), pValue)
4148
1
            .AddAlias("ot")
4149
1
            .AddAlias("datatype")
4150
1
            .AddMetadataItem("type", {"GDALDataType"})
4151
1
            .SetChoices("Byte", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4152
1
                        "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4153
1
                        "Float32", "Float64", "CFloat32", "CFloat64");
4154
1
    return arg;
4155
1
}
4156
4157
/************************************************************************/
4158
/*                    GDALAlgorithm::AddNodataArg()                     */
4159
/************************************************************************/
4160
4161
GDALInConstructionAlgorithmArg &
4162
GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4163
                            const std::string &optionName,
4164
                            const char *helpMessage)
4165
0
{
4166
0
    auto &arg = AddArg(
4167
0
        optionName, 0,
4168
0
        MsgOrDefault(helpMessage,
4169
0
                     noneAllowed
4170
0
                         ? _("Assign a specified nodata value to output bands "
4171
0
                             "('none', numeric value, 'nan', 'inf', '-inf')")
4172
0
                         : _("Assign a specified nodata value to output bands "
4173
0
                             "(numeric value, 'nan', 'inf', '-inf')")),
4174
0
        pValue);
4175
0
    arg.AddValidationAction(
4176
0
        [this, pValue, noneAllowed, optionName]()
4177
0
        {
4178
0
            if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4179
0
            {
4180
0
                char *endptr = nullptr;
4181
0
                CPLStrtod(pValue->c_str(), &endptr);
4182
0
                if (endptr != pValue->c_str() + pValue->size())
4183
0
                {
4184
0
                    ReportError(CE_Failure, CPLE_IllegalArg,
4185
0
                                "Value of '%s' should be %sa "
4186
0
                                "numeric value, 'nan', 'inf' or '-inf'",
4187
0
                                optionName.c_str(),
4188
0
                                noneAllowed ? "'none', " : "");
4189
0
                    return false;
4190
0
                }
4191
0
            }
4192
0
            return true;
4193
0
        });
4194
0
    return arg;
4195
0
}
4196
4197
/************************************************************************/
4198
/*                 GDALAlgorithm::AddOutputStringArg()                  */
4199
/************************************************************************/
4200
4201
GDALInConstructionAlgorithmArg &
4202
GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4203
6.61k
{
4204
6.61k
    return AddArg(
4205
6.61k
               "output-string", 0,
4206
6.61k
               MsgOrDefault(helpMessage,
4207
6.61k
                            _("Output string, in which the result is placed")),
4208
6.61k
               pValue)
4209
6.61k
        .SetHiddenForCLI()
4210
6.61k
        .SetIsInput(false)
4211
6.61k
        .SetIsOutput(true);
4212
6.61k
}
4213
4214
/************************************************************************/
4215
/*                    GDALAlgorithm::AddLayerNameArg()                  */
4216
/************************************************************************/
4217
4218
GDALInConstructionAlgorithmArg &
4219
GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4220
1
{
4221
1
    return AddArg("layer", 'l', MsgOrDefault(helpMessage, _("Layer name")),
4222
1
                  pValue);
4223
1
}
4224
4225
/************************************************************************/
4226
/*                    GDALAlgorithm::AddLayerNameArg()                  */
4227
/************************************************************************/
4228
4229
GDALInConstructionAlgorithmArg &
4230
GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
4231
                               const char *helpMessage)
4232
0
{
4233
0
    return AddArg("layer", 'l', MsgOrDefault(helpMessage, _("Layer name")),
4234
0
                  pValue);
4235
0
}
4236
4237
/************************************************************************/
4238
/*                 GDALAlgorithm::AddGeometryTypeArg()                  */
4239
/************************************************************************/
4240
4241
GDALInConstructionAlgorithmArg &
4242
GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
4243
0
{
4244
0
    return AddArg("geometry-type", 0,
4245
0
                  MsgOrDefault(helpMessage, _("Geometry type")), pValue)
4246
0
        .SetAutoCompleteFunction(
4247
0
            [](const std::string &currentValue)
4248
0
            {
4249
0
                std::vector<std::string> oRet;
4250
0
                for (const char *type :
4251
0
                     {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
4252
0
                      "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
4253
0
                      "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
4254
0
                      "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
4255
0
                      "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
4256
0
                {
4257
0
                    if (currentValue.empty() ||
4258
0
                        STARTS_WITH(type, currentValue.c_str()))
4259
0
                    {
4260
0
                        oRet.push_back(type);
4261
0
                        oRet.push_back(std::string(type).append("Z"));
4262
0
                        oRet.push_back(std::string(type).append("M"));
4263
0
                        oRet.push_back(std::string(type).append("ZM"));
4264
0
                    }
4265
0
                }
4266
0
                return oRet;
4267
0
            })
4268
0
        .AddValidationAction(
4269
0
            [this, pValue]()
4270
0
            {
4271
0
                if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
4272
0
                        wkbUnknown &&
4273
0
                    !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
4274
0
                {
4275
0
                    ReportError(CE_Failure, CPLE_AppDefined,
4276
0
                                "Invalid geometry type '%s'", pValue->c_str());
4277
0
                    return false;
4278
0
                }
4279
0
                return true;
4280
0
            });
4281
0
}
4282
4283
/************************************************************************/
4284
/*          GDALAlgorithm::SetAutoCompleteFunctionForLayerName()        */
4285
/************************************************************************/
4286
4287
/* static */
4288
void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
4289
    GDALInConstructionAlgorithmArg &layerArg,
4290
    GDALInConstructionAlgorithmArg &datasetArg)
4291
0
{
4292
0
    CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
4293
0
              datasetArg.GetType() == GAAT_DATASET_LIST);
4294
4295
0
    layerArg.SetAutoCompleteFunction(
4296
0
        [&datasetArg](const std::string &currentValue)
4297
0
        {
4298
0
            std::vector<std::string> ret;
4299
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4300
0
            GDALArgDatasetValue *dsVal = nullptr;
4301
0
            if (datasetArg.GetType() == GAAT_DATASET)
4302
0
            {
4303
0
                dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
4304
0
            }
4305
0
            else
4306
0
            {
4307
0
                auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
4308
0
                if (val.size() == 1)
4309
0
                {
4310
0
                    dsVal = &val[0];
4311
0
                }
4312
0
            }
4313
0
            if (dsVal && !dsVal->GetName().empty())
4314
0
            {
4315
0
                auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4316
0
                    dsVal->GetName().c_str(), GDAL_OF_VECTOR));
4317
0
                if (poDS)
4318
0
                {
4319
0
                    for (auto &&poLayer : poDS->GetLayers())
4320
0
                    {
4321
0
                        if (currentValue == poLayer->GetDescription())
4322
0
                        {
4323
0
                            ret.clear();
4324
0
                            ret.push_back(poLayer->GetDescription());
4325
0
                            break;
4326
0
                        }
4327
0
                        ret.push_back(poLayer->GetDescription());
4328
0
                    }
4329
0
                }
4330
0
            }
4331
0
            return ret;
4332
0
        });
4333
0
}
4334
4335
/************************************************************************/
4336
/*                  GDALAlgorithm::ValidateBandArg()                    */
4337
/************************************************************************/
4338
4339
bool GDALAlgorithm::ValidateBandArg() const
4340
0
{
4341
0
    bool ret = true;
4342
0
    const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
4343
0
    const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
4344
0
    if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
4345
0
        (inputDatasetArg->GetType() == GAAT_DATASET ||
4346
0
         inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
4347
0
        (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
4348
0
    {
4349
0
        const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
4350
0
        {
4351
0
            if (nBand > poDS->GetRasterCount())
4352
0
            {
4353
0
                ReportError(CE_Failure, CPLE_AppDefined,
4354
0
                            "Value of 'band' should be greater or equal than "
4355
0
                            "1 and less or equal than %d.",
4356
0
                            poDS->GetRasterCount());
4357
0
                return false;
4358
0
            }
4359
0
            return true;
4360
0
        };
4361
4362
0
        const auto ValidateForOneDataset =
4363
0
            [&bandArg, &CheckBand](const GDALDataset *poDS)
4364
0
        {
4365
0
            bool l_ret = true;
4366
0
            if (bandArg->GetType() == GAAT_INTEGER)
4367
0
            {
4368
0
                l_ret = CheckBand(poDS, bandArg->Get<int>());
4369
0
            }
4370
0
            else if (bandArg->GetType() == GAAT_INTEGER_LIST)
4371
0
            {
4372
0
                for (int nBand : bandArg->Get<std::vector<int>>())
4373
0
                {
4374
0
                    l_ret = l_ret && CheckBand(poDS, nBand);
4375
0
                }
4376
0
            }
4377
0
            return l_ret;
4378
0
        };
4379
4380
0
        if (inputDatasetArg->GetType() == GAAT_DATASET)
4381
0
        {
4382
0
            auto poDS =
4383
0
                inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
4384
0
            if (poDS && !ValidateForOneDataset(poDS))
4385
0
                ret = false;
4386
0
        }
4387
0
        else
4388
0
        {
4389
0
            CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
4390
0
            for (auto &datasetValue :
4391
0
                 inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
4392
0
            {
4393
0
                auto poDS = datasetValue.GetDatasetRef();
4394
0
                if (poDS && !ValidateForOneDataset(poDS))
4395
0
                    ret = false;
4396
0
            }
4397
0
        }
4398
0
    }
4399
0
    return ret;
4400
0
}
4401
4402
/************************************************************************/
4403
/*             GDALAlgorithm::RunPreStepPipelineValidations()           */
4404
/************************************************************************/
4405
4406
bool GDALAlgorithm::RunPreStepPipelineValidations() const
4407
0
{
4408
0
    return ValidateBandArg();
4409
0
}
4410
4411
/************************************************************************/
4412
/*                    GDALAlgorithm::AddBandArg()                       */
4413
/************************************************************************/
4414
4415
GDALInConstructionAlgorithmArg &
4416
GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
4417
0
{
4418
0
    AddValidationAction([this]() { return ValidateBandArg(); });
4419
4420
0
    return AddArg(GDAL_ARG_NAME_BAND, 'b',
4421
0
                  MsgOrDefault(helpMessage, _("Input band (1-based index)")),
4422
0
                  pValue)
4423
0
        .AddValidationAction(
4424
0
            [pValue]()
4425
0
            {
4426
0
                if (*pValue <= 0)
4427
0
                {
4428
0
                    CPLError(CE_Failure, CPLE_AppDefined,
4429
0
                             "Value of 'band' should greater or equal to 1.");
4430
0
                    return false;
4431
0
                }
4432
0
                return true;
4433
0
            });
4434
0
}
4435
4436
/************************************************************************/
4437
/*                    GDALAlgorithm::AddBandArg()                       */
4438
/************************************************************************/
4439
4440
GDALInConstructionAlgorithmArg &
4441
GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
4442
0
{
4443
0
    AddValidationAction([this]() { return ValidateBandArg(); });
4444
4445
0
    return AddArg(GDAL_ARG_NAME_BAND, 'b',
4446
0
                  MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
4447
0
                  pValue)
4448
0
        .AddValidationAction(
4449
0
            [pValue]()
4450
0
            {
4451
0
                for (int val : *pValue)
4452
0
                {
4453
0
                    if (val <= 0)
4454
0
                    {
4455
0
                        CPLError(CE_Failure, CPLE_AppDefined,
4456
0
                                 "Value of 'band' should greater or equal "
4457
0
                                 "to 1.");
4458
0
                        return false;
4459
0
                    }
4460
0
                }
4461
0
                return true;
4462
0
            });
4463
0
}
4464
4465
/************************************************************************/
4466
/*                     ParseAndValidateKeyValue()                       */
4467
/************************************************************************/
4468
4469
bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
4470
0
{
4471
0
    const auto Validate = [this, &arg](const std::string &val)
4472
0
    {
4473
0
        if (val.find('=') == std::string::npos)
4474
0
        {
4475
0
            ReportError(
4476
0
                CE_Failure, CPLE_AppDefined,
4477
0
                "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
4478
0
                arg.GetName().c_str());
4479
0
            return false;
4480
0
        }
4481
4482
0
        return true;
4483
0
    };
4484
4485
0
    if (arg.GetType() == GAAT_STRING)
4486
0
    {
4487
0
        return Validate(arg.Get<std::string>());
4488
0
    }
4489
0
    else if (arg.GetType() == GAAT_STRING_LIST)
4490
0
    {
4491
0
        std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
4492
0
        if (vals.size() == 1)
4493
0
        {
4494
            // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
4495
0
            std::vector<std::string> newVals;
4496
0
            std::string curToken;
4497
0
            bool canSplitOnComma = true;
4498
0
            char lastSep = 0;
4499
0
            bool inString = false;
4500
0
            bool equalFoundInLastToken = false;
4501
0
            for (char c : vals[0])
4502
0
            {
4503
0
                if (!inString && c == ',')
4504
0
                {
4505
0
                    if (lastSep != '=' || !equalFoundInLastToken)
4506
0
                    {
4507
0
                        canSplitOnComma = false;
4508
0
                        break;
4509
0
                    }
4510
0
                    lastSep = c;
4511
0
                    newVals.push_back(curToken);
4512
0
                    curToken.clear();
4513
0
                    equalFoundInLastToken = false;
4514
0
                }
4515
0
                else if (!inString && c == '=')
4516
0
                {
4517
0
                    if (lastSep == '=')
4518
0
                    {
4519
0
                        canSplitOnComma = false;
4520
0
                        break;
4521
0
                    }
4522
0
                    equalFoundInLastToken = true;
4523
0
                    lastSep = c;
4524
0
                    curToken += c;
4525
0
                }
4526
0
                else if (c == '"')
4527
0
                {
4528
0
                    inString = !inString;
4529
0
                    curToken += c;
4530
0
                }
4531
0
                else
4532
0
                {
4533
0
                    curToken += c;
4534
0
                }
4535
0
            }
4536
0
            if (canSplitOnComma && !inString && equalFoundInLastToken)
4537
0
            {
4538
0
                if (!curToken.empty())
4539
0
                    newVals.emplace_back(std::move(curToken));
4540
0
                vals = std::move(newVals);
4541
0
            }
4542
0
        }
4543
4544
0
        for (const auto &val : vals)
4545
0
        {
4546
0
            if (!Validate(val))
4547
0
                return false;
4548
0
        }
4549
0
    }
4550
4551
0
    return true;
4552
0
}
4553
4554
/************************************************************************/
4555
/*                             IsGDALGOutput()                          */
4556
/************************************************************************/
4557
4558
bool GDALAlgorithm::IsGDALGOutput() const
4559
0
{
4560
0
    bool isGDALGOutput = false;
4561
0
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4562
0
    const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4563
0
    if (outputArg && outputArg->GetType() == GAAT_DATASET &&
4564
0
        outputArg->IsExplicitlySet())
4565
0
    {
4566
0
        if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4567
0
            outputFormatArg->IsExplicitlySet())
4568
0
        {
4569
0
            const auto &val =
4570
0
                outputFormatArg->GDALAlgorithmArg::Get<std::string>();
4571
0
            isGDALGOutput = EQUAL(val.c_str(), "GDALG");
4572
0
        }
4573
0
        else
4574
0
        {
4575
0
            const auto &filename =
4576
0
                outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
4577
0
            isGDALGOutput =
4578
0
                filename.GetName().size() > strlen(".gdalg.json") &&
4579
0
                EQUAL(filename.GetName().c_str() + filename.GetName().size() -
4580
0
                          strlen(".gdalg.json"),
4581
0
                      ".gdalg.json");
4582
0
        }
4583
0
    }
4584
0
    return isGDALGOutput;
4585
0
}
4586
4587
/************************************************************************/
4588
/*                          ProcessGDALGOutput()                        */
4589
/************************************************************************/
4590
4591
GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
4592
1.31k
{
4593
1.31k
    if (!SupportsStreamedOutput())
4594
1.31k
        return ProcessGDALGOutputRet::NOT_GDALG;
4595
4596
0
    if (IsGDALGOutput())
4597
0
    {
4598
0
        const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4599
0
        const auto &filename =
4600
0
            outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
4601
0
        VSIStatBufL sStat;
4602
0
        if (VSIStatL(filename.c_str(), &sStat) == 0)
4603
0
        {
4604
0
            const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
4605
0
            if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
4606
0
            {
4607
0
                if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
4608
0
                {
4609
0
                    CPLError(CE_Failure, CPLE_AppDefined,
4610
0
                             "File '%s' already exists. Specify the "
4611
0
                             "--overwrite option to overwrite it.",
4612
0
                             filename.c_str());
4613
0
                    return ProcessGDALGOutputRet::GDALG_ERROR;
4614
0
                }
4615
0
            }
4616
0
        }
4617
4618
0
        std::string osCommandLine;
4619
4620
0
        for (const auto &path : GDALAlgorithm::m_callPath)
4621
0
        {
4622
0
            if (!osCommandLine.empty())
4623
0
                osCommandLine += ' ';
4624
0
            osCommandLine += path;
4625
0
        }
4626
4627
0
        for (const auto &arg : GetArgs())
4628
0
        {
4629
0
            if (arg->IsExplicitlySet() &&
4630
0
                arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
4631
0
                arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
4632
0
                arg->GetName() != GDAL_ARG_NAME_UPDATE &&
4633
0
                arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
4634
0
            {
4635
0
                osCommandLine += ' ';
4636
0
                std::string strArg;
4637
0
                if (!arg->Serialize(strArg))
4638
0
                {
4639
0
                    CPLError(CE_Failure, CPLE_AppDefined,
4640
0
                             "Cannot serialize argument %s",
4641
0
                             arg->GetName().c_str());
4642
0
                    return ProcessGDALGOutputRet::GDALG_ERROR;
4643
0
                }
4644
0
                osCommandLine += strArg;
4645
0
            }
4646
0
        }
4647
4648
0
        osCommandLine += " --output-format stream --output streamed_dataset";
4649
4650
0
        return SaveGDALG(filename, osCommandLine)
4651
0
                   ? ProcessGDALGOutputRet::GDALG_OK
4652
0
                   : ProcessGDALGOutputRet::GDALG_ERROR;
4653
0
    }
4654
4655
0
    return ProcessGDALGOutputRet::NOT_GDALG;
4656
0
}
4657
4658
/************************************************************************/
4659
/*                      GDALAlgorithm::SaveGDALG()                      */
4660
/************************************************************************/
4661
4662
/* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
4663
                                           const std::string &commandLine)
4664
0
{
4665
0
    CPLJSONDocument oDoc;
4666
0
    oDoc.GetRoot().Add("type", "gdal_streamed_alg");
4667
0
    oDoc.GetRoot().Add("command_line", commandLine);
4668
0
    oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
4669
4670
0
    return oDoc.Save(filename);
4671
0
}
4672
4673
/************************************************************************/
4674
/*                 GDALAlgorithm::AddCreationOptionsArg()               */
4675
/************************************************************************/
4676
4677
GDALInConstructionAlgorithmArg &
4678
GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
4679
                                     const char *helpMessage)
4680
1
{
4681
1
    auto &arg = AddArg("creation-option", 0,
4682
1
                       MsgOrDefault(helpMessage, _("Creation option")), pValue)
4683
1
                    .AddAlias("co")
4684
1
                    .SetMetaVar("<KEY>=<VALUE>")
4685
1
                    .SetPackedValuesAllowed(false);
4686
1
    arg.AddValidationAction([this, &arg]()
4687
1
                            { return ParseAndValidateKeyValue(arg); });
4688
4689
1
    arg.SetAutoCompleteFunction(
4690
1
        [this](const std::string &currentValue)
4691
1
        {
4692
0
            std::vector<std::string> oRet;
4693
4694
0
            int datasetType =
4695
0
                GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4696
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4697
0
            if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
4698
0
                              outputArg->GetType() == GAAT_DATASET_LIST))
4699
0
            {
4700
0
                datasetType = outputArg->GetDatasetType();
4701
0
            }
4702
4703
0
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4704
0
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
4705
0
                outputFormat->IsExplicitlySet())
4706
0
            {
4707
0
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
4708
0
                    outputFormat->Get<std::string>().c_str());
4709
0
                if (poDriver)
4710
0
                {
4711
0
                    AddOptionsSuggestions(
4712
0
                        poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
4713
0
                        datasetType, currentValue, oRet);
4714
0
                }
4715
0
                return oRet;
4716
0
            }
4717
4718
0
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
4719
0
            {
4720
0
                auto poDM = GetGDALDriverManager();
4721
0
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
4722
0
                const auto &osDSName = datasetValue.GetName();
4723
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4724
0
                if (!osExt.empty())
4725
0
                {
4726
0
                    std::set<std::string> oVisitedExtensions;
4727
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
4728
0
                    {
4729
0
                        auto poDriver = poDM->GetDriver(i);
4730
0
                        if (((datasetType & GDAL_OF_RASTER) != 0 &&
4731
0
                             poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4732
0
                            ((datasetType & GDAL_OF_VECTOR) != 0 &&
4733
0
                             poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4734
0
                            ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4735
0
                             poDriver->GetMetadataItem(
4736
0
                                 GDAL_DCAP_MULTIDIM_RASTER)))
4737
0
                        {
4738
0
                            const char *pszExtensions =
4739
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4740
0
                            if (pszExtensions)
4741
0
                            {
4742
0
                                const CPLStringList aosExts(
4743
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
4744
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
4745
0
                                {
4746
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
4747
0
                                        !cpl::contains(oVisitedExtensions,
4748
0
                                                       pszExt))
4749
0
                                    {
4750
0
                                        oVisitedExtensions.insert(pszExt);
4751
0
                                        if (AddOptionsSuggestions(
4752
0
                                                poDriver->GetMetadataItem(
4753
0
                                                    GDAL_DMD_CREATIONOPTIONLIST),
4754
0
                                                datasetType, currentValue,
4755
0
                                                oRet))
4756
0
                                        {
4757
0
                                            return oRet;
4758
0
                                        }
4759
0
                                        break;
4760
0
                                    }
4761
0
                                }
4762
0
                            }
4763
0
                        }
4764
0
                    }
4765
0
                }
4766
0
            }
4767
4768
0
            return oRet;
4769
0
        });
4770
4771
1
    return arg;
4772
1
}
4773
4774
/************************************************************************/
4775
/*                GDALAlgorithm::AddLayerCreationOptionsArg()           */
4776
/************************************************************************/
4777
4778
GDALInConstructionAlgorithmArg &
4779
GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
4780
                                          const char *helpMessage)
4781
1
{
4782
1
    auto &arg =
4783
1
        AddArg("layer-creation-option", 0,
4784
1
               MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
4785
1
            .AddAlias("lco")
4786
1
            .SetMetaVar("<KEY>=<VALUE>")
4787
1
            .SetPackedValuesAllowed(false);
4788
1
    arg.AddValidationAction([this, &arg]()
4789
1
                            { return ParseAndValidateKeyValue(arg); });
4790
4791
1
    arg.SetAutoCompleteFunction(
4792
1
        [this](const std::string &currentValue)
4793
1
        {
4794
0
            std::vector<std::string> oRet;
4795
4796
0
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4797
0
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
4798
0
                outputFormat->IsExplicitlySet())
4799
0
            {
4800
0
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
4801
0
                    outputFormat->Get<std::string>().c_str());
4802
0
                if (poDriver)
4803
0
                {
4804
0
                    AddOptionsSuggestions(poDriver->GetMetadataItem(
4805
0
                                              GDAL_DS_LAYER_CREATIONOPTIONLIST),
4806
0
                                          GDAL_OF_VECTOR, currentValue, oRet);
4807
0
                }
4808
0
                return oRet;
4809
0
            }
4810
4811
0
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4812
0
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
4813
0
            {
4814
0
                auto poDM = GetGDALDriverManager();
4815
0
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
4816
0
                const auto &osDSName = datasetValue.GetName();
4817
0
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4818
0
                if (!osExt.empty())
4819
0
                {
4820
0
                    std::set<std::string> oVisitedExtensions;
4821
0
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
4822
0
                    {
4823
0
                        auto poDriver = poDM->GetDriver(i);
4824
0
                        if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
4825
0
                        {
4826
0
                            const char *pszExtensions =
4827
0
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4828
0
                            if (pszExtensions)
4829
0
                            {
4830
0
                                const CPLStringList aosExts(
4831
0
                                    CSLTokenizeString2(pszExtensions, " ", 0));
4832
0
                                for (const char *pszExt : cpl::Iterate(aosExts))
4833
0
                                {
4834
0
                                    if (EQUAL(pszExt, osExt.c_str()) &&
4835
0
                                        !cpl::contains(oVisitedExtensions,
4836
0
                                                       pszExt))
4837
0
                                    {
4838
0
                                        oVisitedExtensions.insert(pszExt);
4839
0
                                        if (AddOptionsSuggestions(
4840
0
                                                poDriver->GetMetadataItem(
4841
0
                                                    GDAL_DS_LAYER_CREATIONOPTIONLIST),
4842
0
                                                GDAL_OF_VECTOR, currentValue,
4843
0
                                                oRet))
4844
0
                                        {
4845
0
                                            return oRet;
4846
0
                                        }
4847
0
                                        break;
4848
0
                                    }
4849
0
                                }
4850
0
                            }
4851
0
                        }
4852
0
                    }
4853
0
                }
4854
0
            }
4855
4856
0
            return oRet;
4857
0
        });
4858
4859
1
    return arg;
4860
1
}
4861
4862
/************************************************************************/
4863
/*                        GDALAlgorithm::AddBBOXArg()                   */
4864
/************************************************************************/
4865
4866
/** Add bbox=xmin,ymin,xmax,ymax argument. */
4867
GDALInConstructionAlgorithmArg &
4868
GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
4869
1
{
4870
1
    auto &arg = AddArg("bbox", 0,
4871
1
                       MsgOrDefault(helpMessage,
4872
1
                                    _("Bounding box as xmin,ymin,xmax,ymax")),
4873
1
                       pValue)
4874
1
                    .SetRepeatedArgAllowed(false)
4875
1
                    .SetMinCount(4)
4876
1
                    .SetMaxCount(4)
4877
1
                    .SetDisplayHintAboutRepetition(false);
4878
1
    arg.AddValidationAction(
4879
1
        [&arg]()
4880
1
        {
4881
0
            const auto &val = arg.Get<std::vector<double>>();
4882
0
            CPLAssert(val.size() == 4);
4883
0
            if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
4884
0
            {
4885
0
                CPLError(CE_Failure, CPLE_AppDefined,
4886
0
                         "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
4887
0
                         "xmin <= xmax and ymin <= ymax");
4888
0
                return false;
4889
0
            }
4890
0
            return true;
4891
0
        });
4892
1
    return arg;
4893
1
}
4894
4895
/************************************************************************/
4896
/*                  GDALAlgorithm::AddActiveLayerArg()                  */
4897
/************************************************************************/
4898
4899
GDALInConstructionAlgorithmArg &
4900
GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
4901
0
{
4902
0
    return AddArg("active-layer", 0,
4903
0
                  MsgOrDefault(helpMessage,
4904
0
                               _("Set active layer (if not specified, all)")),
4905
0
                  pValue);
4906
0
}
4907
4908
/************************************************************************/
4909
/*                  GDALAlgorithm::AddNumThreadsArg()                   */
4910
/************************************************************************/
4911
4912
GDALInConstructionAlgorithmArg &
4913
GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
4914
                                const char *helpMessage)
4915
0
{
4916
0
    auto &arg =
4917
0
        AddArg("num-threads", 'j',
4918
0
               MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
4919
0
               pStrValue);
4920
0
    auto lambda = [this, &arg, pValue, pStrValue]
4921
0
    {
4922
#ifdef DEBUG
4923
        const int nCPUCount = std::max(
4924
            1, atoi(CPLGetConfigOption("GDAL_DEBUG_CPU_COUNT",
4925
                                       CPLSPrintf("%d", CPLGetNumCPUs()))));
4926
#else
4927
0
        const int nCPUCount = std::max(1, CPLGetNumCPUs());
4928
0
#endif
4929
0
        int nNumThreads = nCPUCount;
4930
0
        const char *pszThreads =
4931
0
            CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
4932
0
        if (pszThreads && !EQUAL(pszThreads, "ALL_CPUS"))
4933
0
        {
4934
0
            nNumThreads = std::clamp(atoi(pszThreads), 1, nNumThreads);
4935
0
        }
4936
0
        if (EQUAL(pStrValue->c_str(), "ALL_CPUS"))
4937
0
        {
4938
0
            *pValue = nNumThreads;
4939
0
            return true;
4940
0
        }
4941
0
        else
4942
0
        {
4943
0
            char *endptr = nullptr;
4944
0
            const auto res = std::strtol(pStrValue->c_str(), &endptr, 10);
4945
0
            if (endptr == pStrValue->c_str() + pStrValue->size() && res >= 0 &&
4946
0
                res <= INT_MAX)
4947
0
            {
4948
0
                *pValue = std::min(static_cast<int>(res), nNumThreads);
4949
0
                return true;
4950
0
            }
4951
0
            ReportError(CE_Failure, CPLE_IllegalArg,
4952
0
                        "Invalid value for '%s' argument",
4953
0
                        arg.GetName().c_str());
4954
0
            return false;
4955
0
        }
4956
0
    };
4957
0
    if (!pStrValue->empty())
4958
0
    {
4959
0
        arg.SetDefault(*pStrValue);
4960
0
        lambda();
4961
0
    }
4962
0
    arg.AddValidationAction(std::move(lambda));
4963
0
    return arg;
4964
0
}
4965
4966
/************************************************************************/
4967
/*                 GDALAlgorithm::AddAbsolutePathArg()                  */
4968
/************************************************************************/
4969
4970
GDALInConstructionAlgorithmArg &
4971
GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
4972
1
{
4973
1
    return AddArg(
4974
1
        "absolute-path", 0,
4975
1
        MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
4976
1
                                    "should be stored as an absolute path")),
4977
1
        pValue);
4978
1
}
4979
4980
/************************************************************************/
4981
/*               GDALAlgorithm::AddPixelFunctionNameArg()               */
4982
/************************************************************************/
4983
4984
GDALInConstructionAlgorithmArg &
4985
GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
4986
                                       const char *helpMessage)
4987
0
{
4988
4989
0
    const auto pixelFunctionNames =
4990
0
        VRTDerivedRasterBand::GetPixelFunctionNames();
4991
0
    return AddArg(
4992
0
               "pixel-function", 0,
4993
0
               MsgOrDefault(
4994
0
                   helpMessage,
4995
0
                   _("Specify a pixel function to calculate output value from "
4996
0
                     "overlapping inputs")),
4997
0
               pValue)
4998
0
        .SetChoices(pixelFunctionNames);
4999
0
}
5000
5001
/************************************************************************/
5002
/*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
5003
/************************************************************************/
5004
5005
GDALInConstructionAlgorithmArg &
5006
GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
5007
                                       const char *helpMessage)
5008
0
{
5009
0
    auto &pixelFunctionArgArg =
5010
0
        AddArg("pixel-function-arg", 0,
5011
0
               MsgOrDefault(
5012
0
                   helpMessage,
5013
0
                   _("Specify argument(s) to pass to the pixel function")),
5014
0
               pValue)
5015
0
            .SetMetaVar("<NAME>=<VALUE>")
5016
0
            .SetRepeatedArgAllowed(true);
5017
0
    pixelFunctionArgArg.AddValidationAction(
5018
0
        [this, &pixelFunctionArgArg]()
5019
0
        { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
5020
5021
0
    pixelFunctionArgArg.SetAutoCompleteFunction(
5022
0
        [this](const std::string &currentValue)
5023
0
        {
5024
0
            std::string pixelFunction;
5025
0
            const auto pixelFunctionArg = GetArg("pixel-function");
5026
0
            if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
5027
0
            {
5028
0
                pixelFunction = pixelFunctionArg->Get<std::string>();
5029
0
            }
5030
5031
0
            std::vector<std::string> ret;
5032
5033
0
            if (!pixelFunction.empty())
5034
0
            {
5035
0
                const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
5036
0
                    pixelFunction.c_str());
5037
0
                if (!pair)
5038
0
                {
5039
0
                    ret.push_back("**");
5040
                    // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5041
0
                    ret.push_back(std::string("\xC2\xA0"
5042
0
                                              "Invalid pixel function name"));
5043
0
                }
5044
0
                else if (pair->second.find("Argument name=") ==
5045
0
                         std::string::npos)
5046
0
                {
5047
0
                    ret.push_back("**");
5048
                    // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5049
0
                    ret.push_back(
5050
0
                        std::string(
5051
0
                            "\xC2\xA0"
5052
0
                            "No pixel function arguments for pixel function '")
5053
0
                            .append(pixelFunction)
5054
0
                            .append("'"));
5055
0
                }
5056
0
                else
5057
0
                {
5058
0
                    AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
5059
0
                                          ret);
5060
0
                }
5061
0
            }
5062
5063
0
            return ret;
5064
0
        });
5065
5066
0
    return pixelFunctionArgArg;
5067
0
}
5068
5069
/************************************************************************/
5070
/*                  GDALAlgorithm::AddProgressArg()                     */
5071
/************************************************************************/
5072
5073
void GDALAlgorithm::AddProgressArg()
5074
1
{
5075
1
    AddArg("quiet", 'q', _("Quiet mode (no progress bar)"), &m_quiet)
5076
1
        .SetOnlyForCLI()
5077
1
        .SetCategory(GAAC_COMMON)
5078
1
        .AddAction([this]() { m_progressBarRequested = false; });
5079
5080
1
    AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
5081
1
        .SetHidden();
5082
1
}
5083
5084
/************************************************************************/
5085
/*                       GDALAlgorithm::Run()                           */
5086
/************************************************************************/
5087
5088
bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
5089
6.64k
{
5090
6.64k
    WarnIfDeprecated();
5091
5092
6.64k
    if (m_selectedSubAlg)
5093
0
    {
5094
0
        if (m_calledFromCommandLine)
5095
0
            m_selectedSubAlg->m_calledFromCommandLine = true;
5096
0
        return m_selectedSubAlg->Run(pfnProgress, pProgressData);
5097
0
    }
5098
5099
6.64k
    if (m_helpRequested || m_helpDocRequested)
5100
15
    {
5101
15
        if (m_calledFromCommandLine)
5102
0
            printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
5103
15
        return true;
5104
15
    }
5105
5106
6.62k
    if (m_JSONUsageRequested)
5107
0
    {
5108
0
        if (m_calledFromCommandLine)
5109
0
            printf("%s", GetUsageAsJSON().c_str()); /*ok*/
5110
0
        return true;
5111
0
    }
5112
5113
6.62k
    if (!ValidateArguments())
5114
5.31k
        return false;
5115
5116
1.31k
    switch (ProcessGDALGOutput())
5117
1.31k
    {
5118
0
        case ProcessGDALGOutputRet::GDALG_ERROR:
5119
0
            return false;
5120
5121
0
        case ProcessGDALGOutputRet::GDALG_OK:
5122
0
            return true;
5123
5124
1.31k
        case ProcessGDALGOutputRet::NOT_GDALG:
5125
1.31k
            break;
5126
1.31k
    }
5127
5128
1.31k
    if (m_executionForStreamOutput)
5129
0
    {
5130
0
        if (!CheckSafeForStreamOutput())
5131
0
        {
5132
0
            return false;
5133
0
        }
5134
0
    }
5135
5136
1.31k
    return RunImpl(pfnProgress, pProgressData);
5137
1.31k
}
5138
5139
/************************************************************************/
5140
/*              GDALAlgorithm::CheckSafeForStreamOutput()               */
5141
/************************************************************************/
5142
5143
bool GDALAlgorithm::CheckSafeForStreamOutput()
5144
0
{
5145
0
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5146
0
    if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
5147
0
    {
5148
0
        const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5149
0
        if (!EQUAL(val.c_str(), "stream"))
5150
0
        {
5151
            // For security reasons, to avoid that reading a .gdalg.json file
5152
            // writes a file on the file system.
5153
0
            ReportError(
5154
0
                CE_Failure, CPLE_NotSupported,
5155
0
                "in streamed execution, --format stream should be used");
5156
0
            return false;
5157
0
        }
5158
0
    }
5159
0
    return true;
5160
0
}
5161
5162
/************************************************************************/
5163
/*                     GDALAlgorithm::Finalize()                        */
5164
/************************************************************************/
5165
5166
bool GDALAlgorithm::Finalize()
5167
6.64k
{
5168
6.64k
    bool ret = true;
5169
6.64k
    if (m_selectedSubAlg)
5170
0
        ret = m_selectedSubAlg->Finalize();
5171
5172
6.64k
    for (auto &arg : m_args)
5173
46.3k
    {
5174
46.3k
        if (arg->GetType() == GAAT_DATASET)
5175
6.60k
        {
5176
6.60k
            ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
5177
6.60k
        }
5178
39.7k
        else if (arg->GetType() == GAAT_DATASET_LIST)
5179
0
        {
5180
0
            for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
5181
0
            {
5182
0
                ret = ds.Close() && ret;
5183
0
            }
5184
0
        }
5185
46.3k
    }
5186
6.64k
    return ret;
5187
6.64k
}
5188
5189
/************************************************************************/
5190
/*                   GDALAlgorithm::GetArgNamesForCLI()                 */
5191
/************************************************************************/
5192
5193
std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
5194
GDALAlgorithm::GetArgNamesForCLI() const
5195
0
{
5196
0
    std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5197
5198
0
    size_t maxOptLen = 0;
5199
0
    for (const auto &arg : m_args)
5200
0
    {
5201
0
        if (arg->IsHidden() || arg->IsHiddenForCLI())
5202
0
            continue;
5203
0
        std::string opt;
5204
0
        bool addComma = false;
5205
0
        if (!arg->GetShortName().empty())
5206
0
        {
5207
0
            opt += '-';
5208
0
            opt += arg->GetShortName();
5209
0
            addComma = true;
5210
0
        }
5211
0
        for (char alias : arg->GetShortNameAliases())
5212
0
        {
5213
0
            if (addComma)
5214
0
                opt += ", ";
5215
0
            opt += "-";
5216
0
            opt += alias;
5217
0
            addComma = true;
5218
0
        }
5219
0
        for (const std::string &alias : arg->GetAliases())
5220
0
        {
5221
0
            if (addComma)
5222
0
                opt += ", ";
5223
0
            opt += "--";
5224
0
            opt += alias;
5225
0
            addComma = true;
5226
0
        }
5227
0
        if (!arg->GetName().empty())
5228
0
        {
5229
0
            if (addComma)
5230
0
                opt += ", ";
5231
0
            opt += "--";
5232
0
            opt += arg->GetName();
5233
0
        }
5234
0
        const auto &metaVar = arg->GetMetaVar();
5235
0
        if (!metaVar.empty())
5236
0
        {
5237
0
            opt += ' ';
5238
0
            if (metaVar.front() != '<')
5239
0
                opt += '<';
5240
0
            opt += metaVar;
5241
0
            if (metaVar.back() != '>')
5242
0
                opt += '>';
5243
0
        }
5244
0
        maxOptLen = std::max(maxOptLen, opt.size());
5245
0
        options.emplace_back(arg.get(), opt);
5246
0
    }
5247
5248
0
    return std::make_pair(std::move(options), maxOptLen);
5249
0
}
5250
5251
/************************************************************************/
5252
/*                    GDALAlgorithm::GetUsageForCLI()                   */
5253
/************************************************************************/
5254
5255
std::string
5256
GDALAlgorithm::GetUsageForCLI(bool shortUsage,
5257
                              const UsageOptions &usageOptions) const
5258
0
{
5259
0
    if (m_selectedSubAlg)
5260
0
        return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
5261
5262
0
    std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
5263
0
    std::string osPath;
5264
0
    for (const std::string &s : m_callPath)
5265
0
    {
5266
0
        if (!osPath.empty())
5267
0
            osPath += ' ';
5268
0
        osPath += s;
5269
0
    }
5270
0
    osRet += ' ';
5271
0
    osRet += osPath;
5272
5273
0
    bool hasNonPositionals = false;
5274
0
    for (const auto &arg : m_args)
5275
0
    {
5276
0
        if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
5277
0
            hasNonPositionals = true;
5278
0
    }
5279
5280
0
    if (HasSubAlgorithms())
5281
0
    {
5282
0
        if (m_callPath.size() == 1)
5283
0
        {
5284
0
            osRet += " <COMMAND>";
5285
0
            if (hasNonPositionals)
5286
0
                osRet += " [OPTIONS]";
5287
0
            osRet += "\nwhere <COMMAND> is one of:\n";
5288
0
        }
5289
0
        else
5290
0
        {
5291
0
            osRet += " <SUBCOMMAND>";
5292
0
            if (hasNonPositionals)
5293
0
                osRet += " [OPTIONS]";
5294
0
            osRet += "\nwhere <SUBCOMMAND> is one of:\n";
5295
0
        }
5296
0
        size_t maxNameLen = 0;
5297
0
        for (const auto &subAlgName : GetSubAlgorithmNames())
5298
0
        {
5299
0
            maxNameLen = std::max(maxNameLen, subAlgName.size());
5300
0
        }
5301
0
        for (const auto &subAlgName : GetSubAlgorithmNames())
5302
0
        {
5303
0
            auto subAlg = InstantiateSubAlgorithm(subAlgName);
5304
0
            if (subAlg && !subAlg->IsHidden())
5305
0
            {
5306
0
                const std::string &name(subAlg->GetName());
5307
0
                osRet += "  - ";
5308
0
                osRet += name;
5309
0
                osRet += ": ";
5310
0
                osRet.append(maxNameLen - name.size(), ' ');
5311
0
                osRet += subAlg->GetDescription();
5312
0
                if (!subAlg->m_aliases.empty())
5313
0
                {
5314
0
                    bool first = true;
5315
0
                    for (const auto &alias : subAlg->GetAliases())
5316
0
                    {
5317
0
                        if (alias ==
5318
0
                            GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
5319
0
                            break;
5320
0
                        if (first)
5321
0
                            osRet += " (alias: ";
5322
0
                        else
5323
0
                            osRet += ", ";
5324
0
                        osRet += alias;
5325
0
                        first = false;
5326
0
                    }
5327
0
                    if (!first)
5328
0
                    {
5329
0
                        osRet += ')';
5330
0
                    }
5331
0
                }
5332
0
                osRet += '\n';
5333
0
            }
5334
0
        }
5335
5336
0
        if (shortUsage && hasNonPositionals)
5337
0
        {
5338
0
            osRet += "\nTry '";
5339
0
            osRet += osPath;
5340
0
            osRet += " --help' for help.\n";
5341
0
        }
5342
0
    }
5343
0
    else
5344
0
    {
5345
0
        if (!m_args.empty())
5346
0
        {
5347
0
            if (hasNonPositionals)
5348
0
                osRet += " [OPTIONS]";
5349
0
            for (const auto *arg : m_positionalArgs)
5350
0
            {
5351
0
                const bool optional =
5352
0
                    (!arg->IsRequired() && !(GetName() == "pipeline" &&
5353
0
                                             arg->GetName() == "pipeline"));
5354
0
                osRet += ' ';
5355
0
                if (optional)
5356
0
                    osRet += '[';
5357
0
                const std::string &metavar = arg->GetMetaVar();
5358
0
                if (!metavar.empty() && metavar[0] == '<')
5359
0
                {
5360
0
                    osRet += metavar;
5361
0
                }
5362
0
                else
5363
0
                {
5364
0
                    osRet += '<';
5365
0
                    osRet += metavar;
5366
0
                    osRet += '>';
5367
0
                }
5368
0
                if (arg->GetType() == GAAT_DATASET_LIST &&
5369
0
                    arg->GetMaxCount() > 1)
5370
0
                {
5371
0
                    osRet += "...";
5372
0
                }
5373
0
                if (optional)
5374
0
                    osRet += ']';
5375
0
            }
5376
0
        }
5377
5378
0
        const size_t nLenFirstLine = osRet.size();
5379
0
        osRet += '\n';
5380
0
        if (usageOptions.isPipelineStep)
5381
0
        {
5382
0
            osRet.append(nLenFirstLine, '-');
5383
0
            osRet += '\n';
5384
0
        }
5385
5386
0
        if (shortUsage)
5387
0
        {
5388
0
            osRet += "Try '";
5389
0
            osRet += osPath;
5390
0
            osRet += " --help' for help.\n";
5391
0
            return osRet;
5392
0
        }
5393
5394
0
        osRet += '\n';
5395
0
        osRet += m_description;
5396
0
        osRet += '\n';
5397
0
    }
5398
5399
0
    if (!m_args.empty() && !shortUsage)
5400
0
    {
5401
0
        std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5402
0
        size_t maxOptLen;
5403
0
        std::tie(options, maxOptLen) = GetArgNamesForCLI();
5404
0
        if (usageOptions.maxOptLen)
5405
0
            maxOptLen = usageOptions.maxOptLen;
5406
5407
0
        const auto OutputArg =
5408
0
            [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
5409
0
                                      const std::string &opt)
5410
0
        {
5411
0
            osRet += "  ";
5412
0
            osRet += opt;
5413
0
            osRet += "  ";
5414
0
            osRet.append(maxOptLen - opt.size(), ' ');
5415
0
            osRet += arg->GetDescription();
5416
5417
0
            const auto &choices = arg->GetChoices();
5418
0
            if (!choices.empty())
5419
0
            {
5420
0
                osRet += ". ";
5421
0
                osRet += arg->GetMetaVar();
5422
0
                osRet += '=';
5423
0
                bool firstChoice = true;
5424
0
                for (const auto &choice : choices)
5425
0
                {
5426
0
                    if (!firstChoice)
5427
0
                        osRet += '|';
5428
0
                    osRet += choice;
5429
0
                    firstChoice = false;
5430
0
                }
5431
0
            }
5432
5433
0
            if (arg->GetType() == GAAT_DATASET ||
5434
0
                arg->GetType() == GAAT_DATASET_LIST)
5435
0
            {
5436
0
                if (arg->GetDatasetInputFlags() == GADV_NAME &&
5437
0
                    arg->GetDatasetOutputFlags() == GADV_OBJECT)
5438
0
                {
5439
0
                    osRet += " (created by algorithm)";
5440
0
                }
5441
0
            }
5442
5443
0
            if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
5444
0
            {
5445
0
                osRet += " (default: ";
5446
0
                osRet += arg->GetDefault<std::string>();
5447
0
                osRet += ')';
5448
0
            }
5449
0
            else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
5450
0
            {
5451
0
                if (arg->GetDefault<bool>())
5452
0
                    osRet += " (default: true)";
5453
0
            }
5454
0
            else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
5455
0
            {
5456
0
                osRet += " (default: ";
5457
0
                osRet += CPLSPrintf("%d", arg->GetDefault<int>());
5458
0
                osRet += ')';
5459
0
            }
5460
0
            else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
5461
0
            {
5462
0
                osRet += " (default: ";
5463
0
                osRet += CPLSPrintf("%g", arg->GetDefault<double>());
5464
0
                osRet += ')';
5465
0
            }
5466
0
            else if (arg->GetType() == GAAT_STRING_LIST &&
5467
0
                     arg->HasDefaultValue())
5468
0
            {
5469
0
                const auto &defaultVal =
5470
0
                    arg->GetDefault<std::vector<std::string>>();
5471
0
                if (defaultVal.size() == 1)
5472
0
                {
5473
0
                    osRet += " (default: ";
5474
0
                    osRet += defaultVal[0];
5475
0
                    osRet += ')';
5476
0
                }
5477
0
            }
5478
0
            else if (arg->GetType() == GAAT_INTEGER_LIST &&
5479
0
                     arg->HasDefaultValue())
5480
0
            {
5481
0
                const auto &defaultVal = arg->GetDefault<std::vector<int>>();
5482
0
                if (defaultVal.size() == 1)
5483
0
                {
5484
0
                    osRet += " (default: ";
5485
0
                    osRet += CPLSPrintf("%d", defaultVal[0]);
5486
0
                    osRet += ')';
5487
0
                }
5488
0
            }
5489
0
            else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
5490
0
            {
5491
0
                const auto &defaultVal = arg->GetDefault<std::vector<double>>();
5492
0
                if (defaultVal.size() == 1)
5493
0
                {
5494
0
                    osRet += " (default: ";
5495
0
                    osRet += CPLSPrintf("%g", defaultVal[0]);
5496
0
                    osRet += ')';
5497
0
                }
5498
0
            }
5499
5500
0
            if (arg->GetDisplayHintAboutRepetition())
5501
0
            {
5502
0
                if (arg->GetMinCount() > 0 &&
5503
0
                    arg->GetMinCount() == arg->GetMaxCount())
5504
0
                {
5505
0
                    if (arg->GetMinCount() != 1)
5506
0
                        osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
5507
0
                }
5508
0
                else if (arg->GetMinCount() > 0 &&
5509
0
                         arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
5510
0
                {
5511
0
                    osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
5512
0
                                        arg->GetMaxCount());
5513
0
                }
5514
0
                else if (arg->GetMinCount() > 0)
5515
0
                {
5516
0
                    osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
5517
0
                }
5518
0
                else if (arg->GetMaxCount() > 1)
5519
0
                {
5520
0
                    osRet += " [may be repeated]";
5521
0
                }
5522
0
            }
5523
5524
0
            if (arg->IsRequired())
5525
0
            {
5526
0
                osRet += " [required]";
5527
0
            }
5528
5529
0
            osRet += '\n';
5530
5531
0
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
5532
0
            if (!mutualExclusionGroup.empty())
5533
0
            {
5534
0
                std::string otherArgs;
5535
0
                for (const auto &otherArg : m_args)
5536
0
                {
5537
0
                    if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
5538
0
                        otherArg.get() == arg)
5539
0
                        continue;
5540
0
                    if (otherArg->GetMutualExclusionGroup() ==
5541
0
                        mutualExclusionGroup)
5542
0
                    {
5543
0
                        if (!otherArgs.empty())
5544
0
                            otherArgs += ", ";
5545
0
                        otherArgs += "--";
5546
0
                        otherArgs += otherArg->GetName();
5547
0
                    }
5548
0
                }
5549
0
                if (!otherArgs.empty())
5550
0
                {
5551
0
                    osRet += "  ";
5552
0
                    osRet += "  ";
5553
0
                    osRet.append(maxOptLen, ' ');
5554
0
                    osRet += "Mutually exclusive with ";
5555
0
                    osRet += otherArgs;
5556
0
                    osRet += '\n';
5557
0
                }
5558
0
            }
5559
0
        };
5560
5561
0
        if (!m_positionalArgs.empty())
5562
0
        {
5563
0
            osRet += "\nPositional arguments:\n";
5564
0
            for (const auto &[arg, opt] : options)
5565
0
            {
5566
0
                if (arg->IsPositional())
5567
0
                    OutputArg(arg, opt);
5568
0
            }
5569
0
        }
5570
5571
0
        if (hasNonPositionals)
5572
0
        {
5573
0
            bool hasCommon = false;
5574
0
            bool hasBase = false;
5575
0
            bool hasAdvanced = false;
5576
0
            bool hasEsoteric = false;
5577
0
            std::vector<std::string> categories;
5578
0
            for (const auto &iter : options)
5579
0
            {
5580
0
                const auto &arg = iter.first;
5581
0
                if (!arg->IsPositional())
5582
0
                {
5583
0
                    const auto &category = arg->GetCategory();
5584
0
                    if (category == GAAC_COMMON)
5585
0
                    {
5586
0
                        hasCommon = true;
5587
0
                    }
5588
0
                    else if (category == GAAC_BASE)
5589
0
                    {
5590
0
                        hasBase = true;
5591
0
                    }
5592
0
                    else if (category == GAAC_ADVANCED)
5593
0
                    {
5594
0
                        hasAdvanced = true;
5595
0
                    }
5596
0
                    else if (category == GAAC_ESOTERIC)
5597
0
                    {
5598
0
                        hasEsoteric = true;
5599
0
                    }
5600
0
                    else if (std::find(categories.begin(), categories.end(),
5601
0
                                       category) == categories.end())
5602
0
                    {
5603
0
                        categories.push_back(category);
5604
0
                    }
5605
0
                }
5606
0
            }
5607
0
            if (hasAdvanced)
5608
0
                categories.insert(categories.begin(), GAAC_ADVANCED);
5609
0
            if (hasBase)
5610
0
                categories.insert(categories.begin(), GAAC_BASE);
5611
0
            if (hasCommon && !usageOptions.isPipelineStep)
5612
0
                categories.insert(categories.begin(), GAAC_COMMON);
5613
0
            if (hasEsoteric)
5614
0
                categories.push_back(GAAC_ESOTERIC);
5615
5616
0
            for (const auto &category : categories)
5617
0
            {
5618
0
                osRet += "\n";
5619
0
                if (category != GAAC_BASE)
5620
0
                {
5621
0
                    osRet += category;
5622
0
                    osRet += ' ';
5623
0
                }
5624
0
                osRet += "Options:\n";
5625
0
                for (const auto &[arg, opt] : options)
5626
0
                {
5627
0
                    if (!arg->IsPositional() && arg->GetCategory() == category)
5628
0
                        OutputArg(arg, opt);
5629
0
                }
5630
0
            }
5631
0
        }
5632
0
    }
5633
5634
0
    if (!m_longDescription.empty())
5635
0
    {
5636
0
        osRet += '\n';
5637
0
        osRet += m_longDescription;
5638
0
        osRet += '\n';
5639
0
    }
5640
5641
0
    if (!m_helpDocRequested && !usageOptions.isPipelineMain)
5642
0
    {
5643
0
        if (!m_helpURL.empty())
5644
0
        {
5645
0
            osRet += "\nFor more details, consult ";
5646
0
            osRet += GetHelpFullURL();
5647
0
            osRet += '\n';
5648
0
        }
5649
0
        osRet += GetUsageForCLIEnd();
5650
0
    }
5651
5652
0
    return osRet;
5653
0
}
5654
5655
/************************************************************************/
5656
/*                   GDALAlgorithm::GetUsageForCLIEnd()                 */
5657
/************************************************************************/
5658
5659
//! @cond Doxygen_Suppress
5660
std::string GDALAlgorithm::GetUsageForCLIEnd() const
5661
0
{
5662
0
    std::string osRet;
5663
5664
0
    if (!m_callPath.empty() && m_callPath[0] == "gdal")
5665
0
    {
5666
0
        osRet += "\nWARNING: the gdal command is provisionally provided as an "
5667
0
                 "alternative interface to GDAL and OGR command line "
5668
0
                 "utilities.\nThe project reserves the right to modify, "
5669
0
                 "rename, reorganize, and change the behavior of the utility\n"
5670
0
                 "until it is officially frozen in a future feature release of "
5671
0
                 "GDAL.\n";
5672
0
    }
5673
0
    return osRet;
5674
0
}
5675
5676
//! @endcond
5677
5678
/************************************************************************/
5679
/*                    GDALAlgorithm::GetUsageAsJSON()                   */
5680
/************************************************************************/
5681
5682
std::string GDALAlgorithm::GetUsageAsJSON() const
5683
0
{
5684
0
    CPLJSONDocument oDoc;
5685
0
    auto oRoot = oDoc.GetRoot();
5686
5687
0
    if (m_displayInJSONUsage)
5688
0
    {
5689
0
        oRoot.Add("name", m_name);
5690
0
        CPLJSONArray jFullPath;
5691
0
        for (const std::string &s : m_callPath)
5692
0
        {
5693
0
            jFullPath.Add(s);
5694
0
        }
5695
0
        oRoot.Add("full_path", jFullPath);
5696
0
    }
5697
5698
0
    oRoot.Add("description", m_description);
5699
0
    if (!m_helpURL.empty())
5700
0
    {
5701
0
        oRoot.Add("short_url", m_helpURL);
5702
0
        oRoot.Add("url", GetHelpFullURL());
5703
0
    }
5704
5705
0
    CPLJSONArray jSubAlgorithms;
5706
0
    for (const auto &subAlgName : GetSubAlgorithmNames())
5707
0
    {
5708
0
        auto subAlg = InstantiateSubAlgorithm(subAlgName);
5709
0
        if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
5710
0
        {
5711
0
            CPLJSONDocument oSubDoc;
5712
0
            CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
5713
0
            jSubAlgorithms.Add(oSubDoc.GetRoot());
5714
0
        }
5715
0
    }
5716
0
    oRoot.Add("sub_algorithms", jSubAlgorithms);
5717
5718
0
    const auto ProcessArg = [](const GDALAlgorithmArg *arg)
5719
0
    {
5720
0
        CPLJSONObject jArg;
5721
0
        jArg.Add("name", arg->GetName());
5722
0
        jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
5723
0
        jArg.Add("description", arg->GetDescription());
5724
5725
0
        const auto &metaVar = arg->GetMetaVar();
5726
0
        if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
5727
0
        {
5728
0
            if (metaVar.front() == '<' && metaVar.back() == '>' &&
5729
0
                metaVar.substr(1, metaVar.size() - 2).find('>') ==
5730
0
                    std::string::npos)
5731
0
                jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
5732
0
            else
5733
0
                jArg.Add("metavar", metaVar);
5734
0
        }
5735
5736
0
        const auto &choices = arg->GetChoices();
5737
0
        if (!choices.empty())
5738
0
        {
5739
0
            CPLJSONArray jChoices;
5740
0
            for (const auto &choice : choices)
5741
0
                jChoices.Add(choice);
5742
0
            jArg.Add("choices", jChoices);
5743
0
        }
5744
0
        if (arg->HasDefaultValue())
5745
0
        {
5746
0
            switch (arg->GetType())
5747
0
            {
5748
0
                case GAAT_BOOLEAN:
5749
0
                    jArg.Add("default", arg->GetDefault<bool>());
5750
0
                    break;
5751
0
                case GAAT_STRING:
5752
0
                    jArg.Add("default", arg->GetDefault<std::string>());
5753
0
                    break;
5754
0
                case GAAT_INTEGER:
5755
0
                    jArg.Add("default", arg->GetDefault<int>());
5756
0
                    break;
5757
0
                case GAAT_REAL:
5758
0
                    jArg.Add("default", arg->GetDefault<double>());
5759
0
                    break;
5760
0
                case GAAT_STRING_LIST:
5761
0
                {
5762
0
                    const auto &val =
5763
0
                        arg->GetDefault<std::vector<std::string>>();
5764
0
                    if (val.size() == 1)
5765
0
                    {
5766
0
                        jArg.Add("default", val[0]);
5767
0
                    }
5768
0
                    else
5769
0
                    {
5770
0
                        CPLJSONArray jArr;
5771
0
                        for (const auto &s : val)
5772
0
                        {
5773
0
                            jArr.Add(s);
5774
0
                        }
5775
0
                        jArg.Add("default", jArr);
5776
0
                    }
5777
0
                    break;
5778
0
                }
5779
0
                case GAAT_INTEGER_LIST:
5780
0
                {
5781
0
                    const auto &val = arg->GetDefault<std::vector<int>>();
5782
0
                    if (val.size() == 1)
5783
0
                    {
5784
0
                        jArg.Add("default", val[0]);
5785
0
                    }
5786
0
                    else
5787
0
                    {
5788
0
                        CPLJSONArray jArr;
5789
0
                        for (int i : val)
5790
0
                        {
5791
0
                            jArr.Add(i);
5792
0
                        }
5793
0
                        jArg.Add("default", jArr);
5794
0
                    }
5795
0
                    break;
5796
0
                }
5797
0
                case GAAT_REAL_LIST:
5798
0
                {
5799
0
                    const auto &val = arg->GetDefault<std::vector<double>>();
5800
0
                    if (val.size() == 1)
5801
0
                    {
5802
0
                        jArg.Add("default", val[0]);
5803
0
                    }
5804
0
                    else
5805
0
                    {
5806
0
                        CPLJSONArray jArr;
5807
0
                        for (double d : val)
5808
0
                        {
5809
0
                            jArr.Add(d);
5810
0
                        }
5811
0
                        jArg.Add("default", jArr);
5812
0
                    }
5813
0
                    break;
5814
0
                }
5815
0
                case GAAT_DATASET:
5816
0
                case GAAT_DATASET_LIST:
5817
0
                    CPLError(CE_Warning, CPLE_AppDefined,
5818
0
                             "Unhandled default value for arg %s",
5819
0
                             arg->GetName().c_str());
5820
0
                    break;
5821
0
            }
5822
0
        }
5823
5824
0
        const auto [minVal, minValIsIncluded] = arg->GetMinValue();
5825
0
        if (!std::isnan(minVal))
5826
0
        {
5827
0
            if (arg->GetType() == GAAT_INTEGER ||
5828
0
                arg->GetType() == GAAT_INTEGER_LIST)
5829
0
                jArg.Add("min_value", static_cast<int>(minVal));
5830
0
            else
5831
0
                jArg.Add("min_value", minVal);
5832
0
            jArg.Add("min_value_is_included", minValIsIncluded);
5833
0
        }
5834
5835
0
        const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
5836
0
        if (!std::isnan(maxVal))
5837
0
        {
5838
0
            if (arg->GetType() == GAAT_INTEGER ||
5839
0
                arg->GetType() == GAAT_INTEGER_LIST)
5840
0
                jArg.Add("max_value", static_cast<int>(maxVal));
5841
0
            else
5842
0
                jArg.Add("max_value", maxVal);
5843
0
            jArg.Add("max_value_is_included", maxValIsIncluded);
5844
0
        }
5845
5846
0
        jArg.Add("required", arg->IsRequired());
5847
0
        if (GDALAlgorithmArgTypeIsList(arg->GetType()))
5848
0
        {
5849
0
            jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
5850
0
            jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
5851
0
            jArg.Add("min_count", arg->GetMinCount());
5852
0
            jArg.Add("max_count", arg->GetMaxCount());
5853
0
        }
5854
0
        jArg.Add("category", arg->GetCategory());
5855
5856
0
        if (arg->GetType() == GAAT_DATASET ||
5857
0
            arg->GetType() == GAAT_DATASET_LIST)
5858
0
        {
5859
0
            {
5860
0
                CPLJSONArray jAr;
5861
0
                if (arg->GetDatasetType() & GDAL_OF_RASTER)
5862
0
                    jAr.Add("raster");
5863
0
                if (arg->GetDatasetType() & GDAL_OF_VECTOR)
5864
0
                    jAr.Add("vector");
5865
0
                if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
5866
0
                    jAr.Add("multidim_raster");
5867
0
                jArg.Add("dataset_type", jAr);
5868
0
            }
5869
5870
0
            const auto GetFlags = [](int flags)
5871
0
            {
5872
0
                CPLJSONArray jAr;
5873
0
                if (flags & GADV_NAME)
5874
0
                    jAr.Add("name");
5875
0
                if (flags & GADV_OBJECT)
5876
0
                    jAr.Add("dataset");
5877
0
                return jAr;
5878
0
            };
5879
5880
0
            if (arg->IsInput())
5881
0
            {
5882
0
                jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
5883
0
            }
5884
0
            if (arg->IsOutput())
5885
0
            {
5886
0
                jArg.Add("output_flags",
5887
0
                         GetFlags(arg->GetDatasetOutputFlags()));
5888
0
            }
5889
0
        }
5890
5891
0
        const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
5892
0
        if (!mutualExclusionGroup.empty())
5893
0
        {
5894
0
            jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
5895
0
        }
5896
5897
0
        const auto &metadata = arg->GetMetadata();
5898
0
        if (!metadata.empty())
5899
0
        {
5900
0
            CPLJSONObject jMetadata;
5901
0
            for (const auto &[key, values] : metadata)
5902
0
            {
5903
0
                CPLJSONArray jValue;
5904
0
                for (const auto &value : values)
5905
0
                    jValue.Add(value);
5906
0
                jMetadata.Add(key, jValue);
5907
0
            }
5908
0
            jArg.Add("metadata", jMetadata);
5909
0
        }
5910
5911
0
        return jArg;
5912
0
    };
5913
5914
0
    {
5915
0
        CPLJSONArray jArgs;
5916
0
        for (const auto &arg : m_args)
5917
0
        {
5918
0
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
5919
0
                !arg->IsOutput())
5920
0
                jArgs.Add(ProcessArg(arg.get()));
5921
0
        }
5922
0
        oRoot.Add("input_arguments", jArgs);
5923
0
    }
5924
5925
0
    {
5926
0
        CPLJSONArray jArgs;
5927
0
        for (const auto &arg : m_args)
5928
0
        {
5929
0
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && !arg->IsInput() &&
5930
0
                arg->IsOutput())
5931
0
                jArgs.Add(ProcessArg(arg.get()));
5932
0
        }
5933
0
        oRoot.Add("output_arguments", jArgs);
5934
0
    }
5935
5936
0
    {
5937
0
        CPLJSONArray jArgs;
5938
0
        for (const auto &arg : m_args)
5939
0
        {
5940
0
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
5941
0
                arg->IsOutput())
5942
0
                jArgs.Add(ProcessArg(arg.get()));
5943
0
        }
5944
0
        oRoot.Add("input_output_arguments", jArgs);
5945
0
    }
5946
5947
0
    if (m_supportsStreamedOutput)
5948
0
    {
5949
0
        oRoot.Add("supports_streamed_output", true);
5950
0
    }
5951
5952
0
    return oDoc.SaveAsString();
5953
0
}
5954
5955
/************************************************************************/
5956
/*                    GDALAlgorithm::GetAutoComplete()                  */
5957
/************************************************************************/
5958
5959
std::vector<std::string>
5960
GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
5961
                               bool lastWordIsComplete, bool showAllOptions)
5962
0
{
5963
0
    std::vector<std::string> ret;
5964
5965
    // Get inner-most algorithm
5966
0
    std::unique_ptr<GDALAlgorithm> curAlgHolder;
5967
0
    GDALAlgorithm *curAlg = this;
5968
0
    while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
5969
0
    {
5970
0
        auto subAlg = curAlg->InstantiateSubAlgorithm(
5971
0
            args.front(), /* suggestionAllowed = */ false);
5972
0
        if (!subAlg)
5973
0
            break;
5974
0
        if (args.size() == 1 && !lastWordIsComplete)
5975
0
        {
5976
0
            int nCount = 0;
5977
0
            for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
5978
0
            {
5979
0
                if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
5980
0
                    nCount++;
5981
0
            }
5982
0
            if (nCount >= 2)
5983
0
            {
5984
0
                for (const std::string &subAlgName :
5985
0
                     curAlg->GetSubAlgorithmNames())
5986
0
                {
5987
0
                    subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
5988
0
                    if (subAlg && !subAlg->IsHidden())
5989
0
                        ret.push_back(subAlg->GetName());
5990
0
                }
5991
0
                return ret;
5992
0
            }
5993
0
        }
5994
0
        showAllOptions = false;
5995
0
        args.erase(args.begin());
5996
0
        curAlgHolder = std::move(subAlg);
5997
0
        curAlg = curAlgHolder.get();
5998
0
    }
5999
0
    if (curAlg != this)
6000
0
    {
6001
0
        return curAlg->GetAutoComplete(args, lastWordIsComplete,
6002
0
                                       /* showAllOptions = */ false);
6003
0
    }
6004
6005
0
    std::string option;
6006
0
    std::string value;
6007
0
    ExtractLastOptionAndValue(args, option, value);
6008
6009
0
    if (option.empty() && !args.empty() && !args.back().empty() &&
6010
0
        args.back()[0] == '-')
6011
0
    {
6012
0
        const auto &lastArg = args.back();
6013
        // List available options
6014
0
        for (const auto &arg : GetArgs())
6015
0
        {
6016
0
            if (arg->IsHidden() || arg->IsHiddenForCLI() ||
6017
0
                (!showAllOptions &&
6018
0
                 (arg->GetName() == "help" || arg->GetName() == "config" ||
6019
0
                  arg->GetName() == "version" ||
6020
0
                  arg->GetName() == "json-usage")))
6021
0
            {
6022
0
                continue;
6023
0
            }
6024
0
            if (!arg->GetShortName().empty())
6025
0
            {
6026
0
                std::string str = std::string("-").append(arg->GetShortName());
6027
0
                if (lastArg == str)
6028
0
                    ret.push_back(std::move(str));
6029
0
            }
6030
0
            if (lastArg != "-" && lastArg != "--")
6031
0
            {
6032
0
                for (const std::string &alias : arg->GetAliases())
6033
0
                {
6034
0
                    std::string str = std::string("--").append(alias);
6035
0
                    if (cpl::starts_with(str, lastArg))
6036
0
                        ret.push_back(std::move(str));
6037
0
                }
6038
0
            }
6039
0
            if (!arg->GetName().empty())
6040
0
            {
6041
0
                std::string str = std::string("--").append(arg->GetName());
6042
0
                if (cpl::starts_with(str, lastArg))
6043
0
                    ret.push_back(std::move(str));
6044
0
            }
6045
0
        }
6046
0
        std::sort(ret.begin(), ret.end());
6047
0
    }
6048
0
    else if (!option.empty())
6049
0
    {
6050
        // List possible choices for current option
6051
0
        auto arg = GetArg(option);
6052
0
        if (arg && arg->GetType() != GAAT_BOOLEAN)
6053
0
        {
6054
0
            ret = arg->GetChoices();
6055
0
            if (ret.empty())
6056
0
            {
6057
0
                {
6058
0
                    CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6059
0
                    SetParseForAutoCompletion();
6060
0
                    CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6061
0
                }
6062
0
                ret = arg->GetAutoCompleteChoices(value);
6063
0
            }
6064
0
            else
6065
0
            {
6066
0
                std::sort(ret.begin(), ret.end());
6067
0
            }
6068
0
            if (!ret.empty() && ret.back() == value)
6069
0
            {
6070
0
                ret.clear();
6071
0
            }
6072
0
            else if (ret.empty())
6073
0
            {
6074
0
                ret.push_back("**");
6075
                // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6076
0
                ret.push_back(std::string("\xC2\xA0"
6077
0
                                          "description: ")
6078
0
                                  .append(arg->GetDescription()));
6079
0
            }
6080
0
        }
6081
0
    }
6082
0
    else
6083
0
    {
6084
        // List possible sub-algorithms
6085
0
        for (const std::string &subAlgName : GetSubAlgorithmNames())
6086
0
        {
6087
0
            auto subAlg = InstantiateSubAlgorithm(subAlgName);
6088
0
            if (subAlg && !subAlg->IsHidden())
6089
0
                ret.push_back(subAlg->GetName());
6090
0
        }
6091
0
        if (!ret.empty())
6092
0
        {
6093
0
            std::sort(ret.begin(), ret.end());
6094
0
        }
6095
6096
        // Try filenames
6097
0
        if (ret.empty() && !args.empty())
6098
0
        {
6099
0
            {
6100
0
                CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6101
0
                SetParseForAutoCompletion();
6102
0
                CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6103
0
            }
6104
6105
0
            const std::string &lastArg = args.back();
6106
0
            GDALAlgorithmArg *arg = nullptr;
6107
0
            for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
6108
0
                                     "like", "source", "destination"})
6109
0
            {
6110
0
                if (!arg)
6111
0
                {
6112
0
                    auto newArg = GetArg(name);
6113
0
                    if (newArg)
6114
0
                    {
6115
0
                        if (!newArg->IsExplicitlySet())
6116
0
                        {
6117
0
                            arg = newArg;
6118
0
                        }
6119
0
                        else if (newArg->GetType() == GAAT_STRING ||
6120
0
                                 newArg->GetType() == GAAT_STRING_LIST ||
6121
0
                                 newArg->GetType() == GAAT_DATASET ||
6122
0
                                 newArg->GetType() == GAAT_DATASET_LIST)
6123
0
                        {
6124
0
                            VSIStatBufL sStat;
6125
0
                            if ((!lastArg.empty() && lastArg.back() == '/') ||
6126
0
                                VSIStatL(lastArg.c_str(), &sStat) != 0)
6127
0
                            {
6128
0
                                arg = newArg;
6129
0
                            }
6130
0
                        }
6131
0
                    }
6132
0
                }
6133
0
            }
6134
0
            if (arg)
6135
0
            {
6136
0
                ret = arg->GetAutoCompleteChoices(lastArg);
6137
0
            }
6138
0
        }
6139
0
    }
6140
6141
0
    return ret;
6142
0
}
6143
6144
/************************************************************************/
6145
/*             GDALAlgorithm::ExtractLastOptionAndValue()               */
6146
/************************************************************************/
6147
6148
void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
6149
                                              std::string &option,
6150
                                              std::string &value) const
6151
0
{
6152
0
    if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
6153
0
    {
6154
0
        const auto nPosEqual = args.back().find('=');
6155
0
        if (nPosEqual == std::string::npos)
6156
0
        {
6157
            // Deal with "gdal ... --option"
6158
0
            if (GetArg(args.back()))
6159
0
            {
6160
0
                option = args.back();
6161
0
                args.pop_back();
6162
0
            }
6163
0
        }
6164
0
        else
6165
0
        {
6166
            // Deal with "gdal ... --option=<value>"
6167
0
            if (GetArg(args.back().substr(0, nPosEqual)))
6168
0
            {
6169
0
                option = args.back().substr(0, nPosEqual);
6170
0
                value = args.back().substr(nPosEqual + 1);
6171
0
                args.pop_back();
6172
0
            }
6173
0
        }
6174
0
    }
6175
0
    else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
6176
0
             args[args.size() - 2][0] == '-')
6177
0
    {
6178
        // Deal with "gdal ... --option <value>"
6179
0
        auto arg = GetArg(args[args.size() - 2]);
6180
0
        if (arg && arg->GetType() != GAAT_BOOLEAN)
6181
0
        {
6182
0
            option = args[args.size() - 2];
6183
0
            value = args.back();
6184
0
            args.pop_back();
6185
0
        }
6186
0
    }
6187
6188
0
    const auto IsKeyValueOption = [](const std::string &osStr)
6189
0
    {
6190
0
        return osStr == "--co" || osStr == "--creation-option" ||
6191
0
               osStr == "--lco" || osStr == "--layer-creation-option" ||
6192
0
               osStr == "--oo" || osStr == "--open-option";
6193
0
    };
6194
6195
0
    if (IsKeyValueOption(option))
6196
0
    {
6197
0
        const auto nPosEqual = value.find('=');
6198
0
        if (nPosEqual != std::string::npos)
6199
0
        {
6200
0
            value.resize(nPosEqual);
6201
0
        }
6202
0
    }
6203
0
}
6204
6205
//! @cond Doxygen_Suppress
6206
6207
/************************************************************************/
6208
/*                 GDALContainerAlgorithm::RunImpl()                    */
6209
/************************************************************************/
6210
6211
bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
6212
28
{
6213
28
    return false;
6214
28
}
6215
6216
//! @endcond
6217
6218
/************************************************************************/
6219
/*                        GDALAlgorithmRelease()                        */
6220
/************************************************************************/
6221
6222
/** Release a handle to an algorithm.
6223
 *
6224
 * @since 3.11
6225
 */
6226
void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
6227
0
{
6228
0
    delete hAlg;
6229
0
}
6230
6231
/************************************************************************/
6232
/*                        GDALAlgorithmGetName()                        */
6233
/************************************************************************/
6234
6235
/** Return the algorithm name.
6236
 *
6237
 * @param hAlg Handle to an algorithm. Must NOT be null.
6238
 * @return algorithm name whose lifetime is bound to hAlg and which must not
6239
 * be freed.
6240
 * @since 3.11
6241
 */
6242
const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
6243
0
{
6244
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6245
0
    return hAlg->ptr->GetName().c_str();
6246
0
}
6247
6248
/************************************************************************/
6249
/*                     GDALAlgorithmGetDescription()                    */
6250
/************************************************************************/
6251
6252
/** Return the algorithm (short) description.
6253
 *
6254
 * @param hAlg Handle to an algorithm. Must NOT be null.
6255
 * @return algorithm description whose lifetime is bound to hAlg and which must
6256
 * not be freed.
6257
 * @since 3.11
6258
 */
6259
const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
6260
0
{
6261
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6262
0
    return hAlg->ptr->GetDescription().c_str();
6263
0
}
6264
6265
/************************************************************************/
6266
/*                     GDALAlgorithmGetLongDescription()                */
6267
/************************************************************************/
6268
6269
/** Return the algorithm (longer) description.
6270
 *
6271
 * @param hAlg Handle to an algorithm. Must NOT be null.
6272
 * @return algorithm description whose lifetime is bound to hAlg and which must
6273
 * not be freed.
6274
 * @since 3.11
6275
 */
6276
const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
6277
0
{
6278
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6279
0
    return hAlg->ptr->GetLongDescription().c_str();
6280
0
}
6281
6282
/************************************************************************/
6283
/*                     GDALAlgorithmGetHelpFullURL()                    */
6284
/************************************************************************/
6285
6286
/** Return the algorithm full URL.
6287
 *
6288
 * @param hAlg Handle to an algorithm. Must NOT be null.
6289
 * @return algorithm URL whose lifetime is bound to hAlg and which must
6290
 * not be freed.
6291
 * @since 3.11
6292
 */
6293
const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
6294
0
{
6295
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6296
0
    return hAlg->ptr->GetHelpFullURL().c_str();
6297
0
}
6298
6299
/************************************************************************/
6300
/*                     GDALAlgorithmHasSubAlgorithms()                  */
6301
/************************************************************************/
6302
6303
/** Return whether the algorithm has sub-algorithms.
6304
 *
6305
 * @param hAlg Handle to an algorithm. Must NOT be null.
6306
 * @since 3.11
6307
 */
6308
bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
6309
0
{
6310
0
    VALIDATE_POINTER1(hAlg, __func__, false);
6311
0
    return hAlg->ptr->HasSubAlgorithms();
6312
0
}
6313
6314
/************************************************************************/
6315
/*                 GDALAlgorithmGetSubAlgorithmNames()                  */
6316
/************************************************************************/
6317
6318
/** Get the names of registered algorithms.
6319
 *
6320
 * @param hAlg Handle to an algorithm. Must NOT be null.
6321
 * @return a NULL terminated list of names, which must be destroyed with
6322
 * CSLDestroy()
6323
 * @since 3.11
6324
 */
6325
char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
6326
0
{
6327
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6328
0
    return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
6329
0
}
6330
6331
/************************************************************************/
6332
/*                GDALAlgorithmInstantiateSubAlgorithm()                */
6333
/************************************************************************/
6334
6335
/** Instantiate an algorithm by its name (or its alias).
6336
 *
6337
 * @param hAlg Handle to an algorithm. Must NOT be null.
6338
 * @param pszSubAlgName Algorithm name. Must NOT be null.
6339
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
6340
 * or NULL if the algorithm does not exist or another error occurred.
6341
 * @since 3.11
6342
 */
6343
GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
6344
                                                    const char *pszSubAlgName)
6345
0
{
6346
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6347
0
    VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
6348
0
    auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
6349
0
    return subAlg
6350
0
               ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
6351
0
               : nullptr;
6352
0
}
6353
6354
/************************************************************************/
6355
/*                GDALAlgorithmParseCommandLineArguments()              */
6356
/************************************************************************/
6357
6358
/** Parse a command line argument, which does not include the algorithm
6359
 * name, to set the value of corresponding arguments.
6360
 *
6361
 * @param hAlg Handle to an algorithm. Must NOT be null.
6362
 * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
6363
 * @return true if successful, false otherwise
6364
 * @since 3.11
6365
 */
6366
6367
bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
6368
                                            CSLConstList papszArgs)
6369
0
{
6370
0
    VALIDATE_POINTER1(hAlg, __func__, false);
6371
0
    return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
6372
0
}
6373
6374
/************************************************************************/
6375
/*                  GDALAlgorithmGetActualAlgorithm()                   */
6376
/************************************************************************/
6377
6378
/** Return the actual algorithm that is going to be invoked, when the
6379
 * current algorithm has sub-algorithms.
6380
 *
6381
 * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
6382
 *
6383
 * Note that the lifetime of the returned algorithm does not exceed the one of
6384
 * the hAlg instance that owns it.
6385
 *
6386
 * @param hAlg Handle to an algorithm. Must NOT be null.
6387
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
6388
 * @since 3.11
6389
 */
6390
GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
6391
0
{
6392
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6393
0
    return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
6394
0
}
6395
6396
/************************************************************************/
6397
/*                          GDALAlgorithmRun()                          */
6398
/************************************************************************/
6399
6400
/** Execute the algorithm, starting with ValidateArguments() and then
6401
 * calling RunImpl().
6402
 *
6403
 * @param hAlg Handle to an algorithm. Must NOT be null.
6404
 * @param pfnProgress Progress callback. May be null.
6405
 * @param pProgressData Progress callback user data. May be null.
6406
 * @return true if successful, false otherwise
6407
 * @since 3.11
6408
 */
6409
6410
bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
6411
                      void *pProgressData)
6412
0
{
6413
0
    VALIDATE_POINTER1(hAlg, __func__, false);
6414
0
    return hAlg->ptr->Run(pfnProgress, pProgressData);
6415
0
}
6416
6417
/************************************************************************/
6418
/*                       GDALAlgorithmFinalize()                        */
6419
/************************************************************************/
6420
6421
/** Complete any pending actions, and return the final status.
6422
 * This is typically useful for algorithm that generate an output dataset.
6423
 *
6424
 * Note that this function does *NOT* release memory associated with the
6425
 * algorithm. GDALAlgorithmRelease() must still be called afterwards.
6426
 *
6427
 * @param hAlg Handle to an algorithm. Must NOT be null.
6428
 * @return true if successful, false otherwise
6429
 * @since 3.11
6430
 */
6431
6432
bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
6433
0
{
6434
0
    VALIDATE_POINTER1(hAlg, __func__, false);
6435
0
    return hAlg->ptr->Finalize();
6436
0
}
6437
6438
/************************************************************************/
6439
/*                    GDALAlgorithmGetUsageAsJSON()                     */
6440
/************************************************************************/
6441
6442
/** Return the usage of the algorithm as a JSON-serialized string.
6443
 *
6444
 * This can be used to dynamically generate interfaces to algorithms.
6445
 *
6446
 * @param hAlg Handle to an algorithm. Must NOT be null.
6447
 * @return a string that must be freed with CPLFree()
6448
 * @since 3.11
6449
 */
6450
char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
6451
0
{
6452
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6453
0
    return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
6454
0
}
6455
6456
/************************************************************************/
6457
/*                      GDALAlgorithmGetArgNames()                      */
6458
/************************************************************************/
6459
6460
/** Return the list of available argument names.
6461
 *
6462
 * @param hAlg Handle to an algorithm. Must NOT be null.
6463
 * @return a NULL terminated list of names, which must be destroyed with
6464
 * CSLDestroy()
6465
 * @since 3.11
6466
 */
6467
char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
6468
0
{
6469
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6470
0
    CPLStringList list;
6471
0
    for (const auto &arg : hAlg->ptr->GetArgs())
6472
0
        list.AddString(arg->GetName().c_str());
6473
0
    return list.StealList();
6474
0
}
6475
6476
/************************************************************************/
6477
/*                        GDALAlgorithmGetArg()                         */
6478
/************************************************************************/
6479
6480
/** Return an argument from its name.
6481
 *
6482
 * The lifetime of the returned object does not exceed the one of hAlg.
6483
 *
6484
 * @param hAlg Handle to an algorithm. Must NOT be null.
6485
 * @param pszArgName Argument name. Must NOT be null.
6486
 * @return an argument that must be released with GDALAlgorithmArgRelease(),
6487
 * or nullptr in case of error
6488
 * @since 3.11
6489
 */
6490
GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
6491
                                      const char *pszArgName)
6492
0
{
6493
0
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
6494
0
    VALIDATE_POINTER1(pszArgName, __func__, nullptr);
6495
0
    auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true);
6496
0
    if (!arg)
6497
0
        return nullptr;
6498
0
    return std::make_unique<GDALAlgorithmArgHS>(arg).release();
6499
0
}
6500
6501
/************************************************************************/
6502
/*                       GDALAlgorithmArgRelease()                      */
6503
/************************************************************************/
6504
6505
/** Release a handle to an argument.
6506
 *
6507
 * @since 3.11
6508
 */
6509
void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
6510
0
{
6511
0
    delete hArg;
6512
0
}
6513
6514
/************************************************************************/
6515
/*                      GDALAlgorithmArgGetName()                       */
6516
/************************************************************************/
6517
6518
/** Return the name of an argument.
6519
 *
6520
 * @param hArg Handle to an argument. Must NOT be null.
6521
 * @return argument name whose lifetime is bound to hArg and which must not
6522
 * be freed.
6523
 * @since 3.11
6524
 */
6525
const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
6526
0
{
6527
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6528
0
    return hArg->ptr->GetName().c_str();
6529
0
}
6530
6531
/************************************************************************/
6532
/*                       GDALAlgorithmArgGetType()                      */
6533
/************************************************************************/
6534
6535
/** Get the type of an argument
6536
 *
6537
 * @param hArg Handle to an argument. Must NOT be null.
6538
 * @since 3.11
6539
 */
6540
GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
6541
0
{
6542
0
    VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
6543
0
    return hArg->ptr->GetType();
6544
0
}
6545
6546
/************************************************************************/
6547
/*                   GDALAlgorithmArgGetDescription()                   */
6548
/************************************************************************/
6549
6550
/** Return the description of an argument.
6551
 *
6552
 * @param hArg Handle to an argument. Must NOT be null.
6553
 * @return argument descriptioin whose lifetime is bound to hArg and which must not
6554
 * be freed.
6555
 * @since 3.11
6556
 */
6557
const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
6558
0
{
6559
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6560
0
    return hArg->ptr->GetDescription().c_str();
6561
0
}
6562
6563
/************************************************************************/
6564
/*                   GDALAlgorithmArgGetShortName()                     */
6565
/************************************************************************/
6566
6567
/** Return the short name, or empty string if there is none
6568
 *
6569
 * @param hArg Handle to an argument. Must NOT be null.
6570
 * @return short name whose lifetime is bound to hArg and which must not
6571
 * be freed.
6572
 * @since 3.11
6573
 */
6574
const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
6575
0
{
6576
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6577
0
    return hArg->ptr->GetShortName().c_str();
6578
0
}
6579
6580
/************************************************************************/
6581
/*                    GDALAlgorithmArgGetAliases()                      */
6582
/************************************************************************/
6583
6584
/** Return the aliases (potentially none)
6585
 *
6586
 * @param hArg Handle to an argument. Must NOT be null.
6587
 * @return a NULL terminated list of names, which must be destroyed with
6588
 * CSLDestroy()
6589
6590
 * @since 3.11
6591
 */
6592
char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
6593
0
{
6594
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6595
0
    return CPLStringList(hArg->ptr->GetAliases()).StealList();
6596
0
}
6597
6598
/************************************************************************/
6599
/*                    GDALAlgorithmArgGetMetaVar()                      */
6600
/************************************************************************/
6601
6602
/** Return the "meta-var" hint.
6603
 *
6604
 * By default, the meta-var value is the long name of the argument in
6605
 * upper case.
6606
 *
6607
 * @param hArg Handle to an argument. Must NOT be null.
6608
 * @return meta-var hint whose lifetime is bound to hArg and which must not
6609
 * be freed.
6610
 * @since 3.11
6611
 */
6612
const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
6613
0
{
6614
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6615
0
    return hArg->ptr->GetMetaVar().c_str();
6616
0
}
6617
6618
/************************************************************************/
6619
/*                   GDALAlgorithmArgGetCategory()                      */
6620
/************************************************************************/
6621
6622
/** Return the argument category
6623
 *
6624
 * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
6625
 *
6626
 * @param hArg Handle to an argument. Must NOT be null.
6627
 * @return category whose lifetime is bound to hArg and which must not
6628
 * be freed.
6629
 * @since 3.11
6630
 */
6631
const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
6632
0
{
6633
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6634
0
    return hArg->ptr->GetCategory().c_str();
6635
0
}
6636
6637
/************************************************************************/
6638
/*                   GDALAlgorithmArgIsPositional()                     */
6639
/************************************************************************/
6640
6641
/** Return if the argument is a positional one.
6642
 *
6643
 * @param hArg Handle to an argument. Must NOT be null.
6644
 * @since 3.11
6645
 */
6646
bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
6647
0
{
6648
0
    VALIDATE_POINTER1(hArg, __func__, false);
6649
0
    return hArg->ptr->IsPositional();
6650
0
}
6651
6652
/************************************************************************/
6653
/*                   GDALAlgorithmArgIsRequired()                       */
6654
/************************************************************************/
6655
6656
/** Return whether the argument is required. Defaults to false.
6657
 *
6658
 * @param hArg Handle to an argument. Must NOT be null.
6659
 * @since 3.11
6660
 */
6661
bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
6662
0
{
6663
0
    VALIDATE_POINTER1(hArg, __func__, false);
6664
0
    return hArg->ptr->IsRequired();
6665
0
}
6666
6667
/************************************************************************/
6668
/*                   GDALAlgorithmArgGetMinCount()                      */
6669
/************************************************************************/
6670
6671
/** Return the minimum number of values for the argument.
6672
 *
6673
 * Defaults to 0.
6674
 * Only applies to list type of arguments.
6675
 *
6676
 * @param hArg Handle to an argument. Must NOT be null.
6677
 * @since 3.11
6678
 */
6679
int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
6680
0
{
6681
0
    VALIDATE_POINTER1(hArg, __func__, 0);
6682
0
    return hArg->ptr->GetMinCount();
6683
0
}
6684
6685
/************************************************************************/
6686
/*                   GDALAlgorithmArgGetMaxCount()                      */
6687
/************************************************************************/
6688
6689
/** Return the maximum number of values for the argument.
6690
 *
6691
 * Defaults to 1 for scalar types, and INT_MAX for list types.
6692
 * Only applies to list type of arguments.
6693
 *
6694
 * @param hArg Handle to an argument. Must NOT be null.
6695
 * @since 3.11
6696
 */
6697
int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
6698
0
{
6699
0
    VALIDATE_POINTER1(hArg, __func__, 0);
6700
0
    return hArg->ptr->GetMaxCount();
6701
0
}
6702
6703
/************************************************************************/
6704
/*                GDALAlgorithmArgGetPackedValuesAllowed()              */
6705
/************************************************************************/
6706
6707
/** Return whether, for list type of arguments, several values, space
6708
 * separated, may be specified. That is "--foo=bar,baz".
6709
 * The default is true.
6710
 *
6711
 * @param hArg Handle to an argument. Must NOT be null.
6712
 * @since 3.11
6713
 */
6714
bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
6715
0
{
6716
0
    VALIDATE_POINTER1(hArg, __func__, false);
6717
0
    return hArg->ptr->GetPackedValuesAllowed();
6718
0
}
6719
6720
/************************************************************************/
6721
/*                GDALAlgorithmArgGetRepeatedArgAllowed()               */
6722
/************************************************************************/
6723
6724
/** Return whether, for list type of arguments, the argument may be
6725
 * repeated. That is "--foo=bar --foo=baz".
6726
 * The default is true.
6727
 *
6728
 * @param hArg Handle to an argument. Must NOT be null.
6729
 * @since 3.11
6730
 */
6731
bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
6732
0
{
6733
0
    VALIDATE_POINTER1(hArg, __func__, false);
6734
0
    return hArg->ptr->GetRepeatedArgAllowed();
6735
0
}
6736
6737
/************************************************************************/
6738
/*                    GDALAlgorithmArgGetChoices()                      */
6739
/************************************************************************/
6740
6741
/** Return the allowed values (as strings) for the argument.
6742
 *
6743
 * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
6744
 *
6745
 * @param hArg Handle to an argument. Must NOT be null.
6746
 * @return a NULL terminated list of names, which must be destroyed with
6747
 * CSLDestroy()
6748
6749
 * @since 3.11
6750
 */
6751
char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
6752
0
{
6753
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6754
0
    return CPLStringList(hArg->ptr->GetChoices()).StealList();
6755
0
}
6756
6757
/************************************************************************/
6758
/*                  GDALAlgorithmArgGetMetadataItem()                   */
6759
/************************************************************************/
6760
6761
/** Return the values of the metadata item of an argument.
6762
 *
6763
 * @param hArg Handle to an argument. Must NOT be null.
6764
 * @param pszItem Name of the item. Must NOT be null.
6765
 * @return a NULL terminated list of values, which must be destroyed with
6766
 * CSLDestroy()
6767
6768
 * @since 3.11
6769
 */
6770
char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
6771
                                       const char *pszItem)
6772
0
{
6773
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6774
0
    VALIDATE_POINTER1(pszItem, __func__, nullptr);
6775
0
    const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
6776
0
    return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
6777
0
}
6778
6779
/************************************************************************/
6780
/*                   GDALAlgorithmArgIsExplicitlySet()                  */
6781
/************************************************************************/
6782
6783
/** Return whether the argument value has been explicitly set with Set()
6784
 *
6785
 * @param hArg Handle to an argument. Must NOT be null.
6786
 * @since 3.11
6787
 */
6788
bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
6789
0
{
6790
0
    VALIDATE_POINTER1(hArg, __func__, false);
6791
0
    return hArg->ptr->IsExplicitlySet();
6792
0
}
6793
6794
/************************************************************************/
6795
/*                   GDALAlgorithmArgHasDefaultValue()                  */
6796
/************************************************************************/
6797
6798
/** Return if the argument has a declared default value.
6799
 *
6800
 * @param hArg Handle to an argument. Must NOT be null.
6801
 * @since 3.11
6802
 */
6803
bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
6804
0
{
6805
0
    VALIDATE_POINTER1(hArg, __func__, false);
6806
0
    return hArg->ptr->HasDefaultValue();
6807
0
}
6808
6809
/************************************************************************/
6810
/*                 GDALAlgorithmArgGetDefaultAsBoolean()                */
6811
/************************************************************************/
6812
6813
/** Return the argument default value as a integer.
6814
 *
6815
 * Must only be called on arguments whose type is GAAT_BOOLEAN
6816
 *
6817
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6818
 * argument has a default value.
6819
 *
6820
 * @param hArg Handle to an argument. Must NOT be null.
6821
 * @since 3.12
6822
 */
6823
bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
6824
0
{
6825
0
    VALIDATE_POINTER1(hArg, __func__, false);
6826
0
    if (hArg->ptr->GetType() != GAAT_BOOLEAN)
6827
0
    {
6828
0
        CPLError(CE_Failure, CPLE_AppDefined,
6829
0
                 "%s must only be called on arguments of type GAAT_BOOLEAN",
6830
0
                 __func__);
6831
0
        return false;
6832
0
    }
6833
0
    return hArg->ptr->GetDefault<bool>();
6834
0
}
6835
6836
/************************************************************************/
6837
/*                 GDALAlgorithmArgGetDefaultAsString()                 */
6838
/************************************************************************/
6839
6840
/** Return the argument default value as a string.
6841
 *
6842
 * Must only be called on arguments whose type is GAAT_STRING.
6843
 *
6844
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6845
 * argument has a default value.
6846
 *
6847
 * @param hArg Handle to an argument. Must NOT be null.
6848
 * @return string whose lifetime is bound to hArg and which must not
6849
 * be freed.
6850
 * @since 3.11
6851
 */
6852
const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
6853
0
{
6854
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6855
0
    if (hArg->ptr->GetType() != GAAT_STRING)
6856
0
    {
6857
0
        CPLError(CE_Failure, CPLE_AppDefined,
6858
0
                 "%s must only be called on arguments of type GAAT_STRING",
6859
0
                 __func__);
6860
0
        return nullptr;
6861
0
    }
6862
0
    return hArg->ptr->GetDefault<std::string>().c_str();
6863
0
}
6864
6865
/************************************************************************/
6866
/*                 GDALAlgorithmArgGetDefaultAsInteger()                */
6867
/************************************************************************/
6868
6869
/** Return the argument default value as a integer.
6870
 *
6871
 * Must only be called on arguments whose type is GAAT_INTEGER
6872
 *
6873
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6874
 * argument has a default value.
6875
 *
6876
 * @param hArg Handle to an argument. Must NOT be null.
6877
 * @since 3.12
6878
 */
6879
int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
6880
0
{
6881
0
    VALIDATE_POINTER1(hArg, __func__, 0);
6882
0
    if (hArg->ptr->GetType() != GAAT_INTEGER)
6883
0
    {
6884
0
        CPLError(CE_Failure, CPLE_AppDefined,
6885
0
                 "%s must only be called on arguments of type GAAT_INTEGER",
6886
0
                 __func__);
6887
0
        return 0;
6888
0
    }
6889
0
    return hArg->ptr->GetDefault<int>();
6890
0
}
6891
6892
/************************************************************************/
6893
/*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
6894
/************************************************************************/
6895
6896
/** Return the argument default value as a double.
6897
 *
6898
 * Must only be called on arguments whose type is GAAT_REAL
6899
 *
6900
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6901
 * argument has a default value.
6902
 *
6903
 * @param hArg Handle to an argument. Must NOT be null.
6904
 * @since 3.12
6905
 */
6906
double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
6907
0
{
6908
0
    VALIDATE_POINTER1(hArg, __func__, 0);
6909
0
    if (hArg->ptr->GetType() != GAAT_REAL)
6910
0
    {
6911
0
        CPLError(CE_Failure, CPLE_AppDefined,
6912
0
                 "%s must only be called on arguments of type GAAT_REAL",
6913
0
                 __func__);
6914
0
        return 0;
6915
0
    }
6916
0
    return hArg->ptr->GetDefault<double>();
6917
0
}
6918
6919
/************************************************************************/
6920
/*                GDALAlgorithmArgGetDefaultAsStringList()              */
6921
/************************************************************************/
6922
6923
/** Return the argument default value as a string list.
6924
 *
6925
 * Must only be called on arguments whose type is GAAT_STRING_LIST.
6926
 *
6927
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6928
 * argument has a default value.
6929
 *
6930
 * @param hArg Handle to an argument. Must NOT be null.
6931
 * @return a NULL terminated list of names, which must be destroyed with
6932
 * CSLDestroy()
6933
6934
 * @since 3.12
6935
 */
6936
char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
6937
0
{
6938
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6939
0
    if (hArg->ptr->GetType() != GAAT_STRING_LIST)
6940
0
    {
6941
0
        CPLError(CE_Failure, CPLE_AppDefined,
6942
0
                 "%s must only be called on arguments of type GAAT_STRING_LIST",
6943
0
                 __func__);
6944
0
        return nullptr;
6945
0
    }
6946
0
    return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
6947
0
        .StealList();
6948
0
}
6949
6950
/************************************************************************/
6951
/*               GDALAlgorithmArgGetDefaultAsIntegerList()              */
6952
/************************************************************************/
6953
6954
/** Return the argument default value as a integer list.
6955
 *
6956
 * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
6957
 *
6958
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6959
 * argument has a default value.
6960
 *
6961
 * @param hArg Handle to an argument. Must NOT be null.
6962
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
6963
 * @since 3.12
6964
 */
6965
const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
6966
                                                   size_t *pnCount)
6967
0
{
6968
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
6969
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
6970
0
    if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
6971
0
    {
6972
0
        CPLError(
6973
0
            CE_Failure, CPLE_AppDefined,
6974
0
            "%s must only be called on arguments of type GAAT_INTEGER_LIST",
6975
0
            __func__);
6976
0
        *pnCount = 0;
6977
0
        return nullptr;
6978
0
    }
6979
0
    const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
6980
0
    *pnCount = val.size();
6981
0
    return val.data();
6982
0
}
6983
6984
/************************************************************************/
6985
/*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
6986
/************************************************************************/
6987
6988
/** Return the argument default value as a real list.
6989
 *
6990
 * Must only be called on arguments whose type is GAAT_REAL_LIST.
6991
 *
6992
 * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
6993
 * argument has a default value.
6994
 *
6995
 * @param hArg Handle to an argument. Must NOT be null.
6996
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
6997
 * @since 3.12
6998
 */
6999
const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
7000
                                                     size_t *pnCount)
7001
0
{
7002
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7003
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
7004
0
    if (hArg->ptr->GetType() != GAAT_REAL_LIST)
7005
0
    {
7006
0
        CPLError(CE_Failure, CPLE_AppDefined,
7007
0
                 "%s must only be called on arguments of type GAAT_REAL_LIST",
7008
0
                 __func__);
7009
0
        *pnCount = 0;
7010
0
        return nullptr;
7011
0
    }
7012
0
    const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
7013
0
    *pnCount = val.size();
7014
0
    return val.data();
7015
0
}
7016
7017
/************************************************************************/
7018
/*                   GDALAlgorithmArgIsHiddenForCLI()                   */
7019
/************************************************************************/
7020
7021
/** Return whether the argument must not be mentioned in CLI usage.
7022
 *
7023
 * For example, "output-value" for "gdal raster info", which is only
7024
 * meant when the algorithm is used from a non-CLI context.
7025
 *
7026
 * @param hArg Handle to an argument. Must NOT be null.
7027
 * @since 3.11
7028
 */
7029
bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
7030
0
{
7031
0
    VALIDATE_POINTER1(hArg, __func__, false);
7032
0
    return hArg->ptr->IsHiddenForCLI();
7033
0
}
7034
7035
/************************************************************************/
7036
/*                   GDALAlgorithmArgIsOnlyForCLI()                     */
7037
/************************************************************************/
7038
7039
/** Return whether the argument is only for CLI usage.
7040
 *
7041
 * For example "--help"
7042
 *
7043
 * @param hArg Handle to an argument. Must NOT be null.
7044
 * @since 3.11
7045
 */
7046
bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
7047
0
{
7048
0
    VALIDATE_POINTER1(hArg, __func__, false);
7049
0
    return hArg->ptr->IsOnlyForCLI();
7050
0
}
7051
7052
/************************************************************************/
7053
/*                     GDALAlgorithmArgIsInput()                        */
7054
/************************************************************************/
7055
7056
/** Indicate whether the value of the argument is read-only during the
7057
 * execution of the algorithm.
7058
 *
7059
 * Default is true.
7060
 *
7061
 * @param hArg Handle to an argument. Must NOT be null.
7062
 * @since 3.11
7063
 */
7064
bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
7065
0
{
7066
0
    VALIDATE_POINTER1(hArg, __func__, false);
7067
0
    return hArg->ptr->IsInput();
7068
0
}
7069
7070
/************************************************************************/
7071
/*                     GDALAlgorithmArgIsOutput()                       */
7072
/************************************************************************/
7073
7074
/** Return whether (at least part of) the value of the argument is set
7075
 * during the execution of the algorithm.
7076
 *
7077
 * For example, "output-value" for "gdal raster info"
7078
 * Default is false.
7079
 * An argument may return both IsInput() and IsOutput() as true.
7080
 * For example the "gdal raster convert" algorithm consumes the dataset
7081
 * name of its "output" argument, and sets the dataset object during its
7082
 * execution.
7083
 *
7084
 * @param hArg Handle to an argument. Must NOT be null.
7085
 * @since 3.11
7086
 */
7087
bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
7088
0
{
7089
0
    VALIDATE_POINTER1(hArg, __func__, false);
7090
0
    return hArg->ptr->IsOutput();
7091
0
}
7092
7093
/************************************************************************/
7094
/*                 GDALAlgorithmArgGetDatasetType()                     */
7095
/************************************************************************/
7096
7097
/** Get which type of dataset is allowed / generated.
7098
 *
7099
 * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
7100
 * GDAL_OF_MULTIDIM_RASTER.
7101
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7102
 *
7103
 * @param hArg Handle to an argument. Must NOT be null.
7104
 * @since 3.11
7105
 */
7106
GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
7107
0
{
7108
0
    VALIDATE_POINTER1(hArg, __func__, 0);
7109
0
    return hArg->ptr->GetDatasetType();
7110
0
}
7111
7112
/************************************************************************/
7113
/*                   GDALAlgorithmArgGetDatasetInputFlags()             */
7114
/************************************************************************/
7115
7116
/** Indicates which components among name and dataset are accepted as
7117
 * input, when this argument serves as an input.
7118
 *
7119
 * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
7120
 * input.
7121
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
7122
 * accepted as input.
7123
 * If both bits are set, the algorithm can accept either a name or a dataset
7124
 * object.
7125
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7126
 *
7127
 * @param hArg Handle to an argument. Must NOT be null.
7128
 * @return string whose lifetime is bound to hAlg and which must not
7129
 * be freed.
7130
 * @since 3.11
7131
 */
7132
int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
7133
0
{
7134
0
    VALIDATE_POINTER1(hArg, __func__, 0);
7135
0
    return hArg->ptr->GetDatasetInputFlags();
7136
0
}
7137
7138
/************************************************************************/
7139
/*                  GDALAlgorithmArgGetDatasetOutputFlags()             */
7140
/************************************************************************/
7141
7142
/** Indicates which components among name and dataset are modified,
7143
 * when this argument serves as an output.
7144
 *
7145
 * If the GADV_NAME bit is set, it indicates a dataset name is generated as
7146
 * output (that is the algorithm will generate the name. Rarely used).
7147
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
7148
 * generated as output, and available for use after the algorithm has
7149
 * completed.
7150
 * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7151
 *
7152
 * @param hArg Handle to an argument. Must NOT be null.
7153
 * @return string whose lifetime is bound to hAlg and which must not
7154
 * be freed.
7155
 * @since 3.11
7156
 */
7157
int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
7158
0
{
7159
0
    VALIDATE_POINTER1(hArg, __func__, 0);
7160
0
    return hArg->ptr->GetDatasetOutputFlags();
7161
0
}
7162
7163
/************************************************************************/
7164
/*               GDALAlgorithmArgGetMutualExclusionGroup()              */
7165
/************************************************************************/
7166
7167
/** Return the name of the mutual exclusion group to which this argument
7168
 * belongs to.
7169
 *
7170
 * Or empty string if it does not belong to any exclusion group.
7171
 *
7172
 * @param hArg Handle to an argument. Must NOT be null.
7173
 * @return string whose lifetime is bound to hArg and which must not
7174
 * be freed.
7175
 * @since 3.11
7176
 */
7177
const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
7178
0
{
7179
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7180
0
    return hArg->ptr->GetMutualExclusionGroup().c_str();
7181
0
}
7182
7183
/************************************************************************/
7184
/*                    GDALAlgorithmArgGetAsBoolean()                    */
7185
/************************************************************************/
7186
7187
/** Return the argument value as a boolean.
7188
 *
7189
 * Must only be called on arguments whose type is GAAT_BOOLEAN.
7190
 *
7191
 * @param hArg Handle to an argument. Must NOT be null.
7192
 * @since 3.11
7193
 */
7194
bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
7195
0
{
7196
0
    VALIDATE_POINTER1(hArg, __func__, false);
7197
0
    if (hArg->ptr->GetType() != GAAT_BOOLEAN)
7198
0
    {
7199
0
        CPLError(CE_Failure, CPLE_AppDefined,
7200
0
                 "%s must only be called on arguments of type GAAT_BOOLEAN",
7201
0
                 __func__);
7202
0
        return false;
7203
0
    }
7204
0
    return hArg->ptr->Get<bool>();
7205
0
}
7206
7207
/************************************************************************/
7208
/*                    GDALAlgorithmArgGetAsString()                     */
7209
/************************************************************************/
7210
7211
/** Return the argument value as a string.
7212
 *
7213
 * Must only be called on arguments whose type is GAAT_STRING.
7214
 *
7215
 * @param hArg Handle to an argument. Must NOT be null.
7216
 * @return string whose lifetime is bound to hArg and which must not
7217
 * be freed.
7218
 * @since 3.11
7219
 */
7220
const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
7221
0
{
7222
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7223
0
    if (hArg->ptr->GetType() != GAAT_STRING)
7224
0
    {
7225
0
        CPLError(CE_Failure, CPLE_AppDefined,
7226
0
                 "%s must only be called on arguments of type GAAT_STRING",
7227
0
                 __func__);
7228
0
        return nullptr;
7229
0
    }
7230
0
    return hArg->ptr->Get<std::string>().c_str();
7231
0
}
7232
7233
/************************************************************************/
7234
/*                 GDALAlgorithmArgGetAsDatasetValue()                  */
7235
/************************************************************************/
7236
7237
/** Return the argument value as a GDALArgDatasetValueH.
7238
 *
7239
 * Must only be called on arguments whose type is GAAT_DATASET
7240
 *
7241
 * @param hArg Handle to an argument. Must NOT be null.
7242
 * @return handle to a GDALArgDatasetValue that must be released with
7243
 * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
7244
 * the one of hArg.
7245
 * @since 3.11
7246
 */
7247
GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
7248
0
{
7249
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7250
0
    if (hArg->ptr->GetType() != GAAT_DATASET)
7251
0
    {
7252
0
        CPLError(CE_Failure, CPLE_AppDefined,
7253
0
                 "%s must only be called on arguments of type GAAT_DATASET",
7254
0
                 __func__);
7255
0
        return nullptr;
7256
0
    }
7257
0
    return std::make_unique<GDALArgDatasetValueHS>(
7258
0
               &(hArg->ptr->Get<GDALArgDatasetValue>()))
7259
0
        .release();
7260
0
}
7261
7262
/************************************************************************/
7263
/*                    GDALAlgorithmArgGetAsInteger()                    */
7264
/************************************************************************/
7265
7266
/** Return the argument value as a integer.
7267
 *
7268
 * Must only be called on arguments whose type is GAAT_INTEGER
7269
 *
7270
 * @param hArg Handle to an argument. Must NOT be null.
7271
 * @since 3.11
7272
 */
7273
int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
7274
0
{
7275
0
    VALIDATE_POINTER1(hArg, __func__, 0);
7276
0
    if (hArg->ptr->GetType() != GAAT_INTEGER)
7277
0
    {
7278
0
        CPLError(CE_Failure, CPLE_AppDefined,
7279
0
                 "%s must only be called on arguments of type GAAT_INTEGER",
7280
0
                 __func__);
7281
0
        return 0;
7282
0
    }
7283
0
    return hArg->ptr->Get<int>();
7284
0
}
7285
7286
/************************************************************************/
7287
/*                    GDALAlgorithmArgGetAsDouble()                     */
7288
/************************************************************************/
7289
7290
/** Return the argument value as a double.
7291
 *
7292
 * Must only be called on arguments whose type is GAAT_REAL
7293
 *
7294
 * @param hArg Handle to an argument. Must NOT be null.
7295
 * @since 3.11
7296
 */
7297
double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
7298
0
{
7299
0
    VALIDATE_POINTER1(hArg, __func__, 0);
7300
0
    if (hArg->ptr->GetType() != GAAT_REAL)
7301
0
    {
7302
0
        CPLError(CE_Failure, CPLE_AppDefined,
7303
0
                 "%s must only be called on arguments of type GAAT_REAL",
7304
0
                 __func__);
7305
0
        return 0;
7306
0
    }
7307
0
    return hArg->ptr->Get<double>();
7308
0
}
7309
7310
/************************************************************************/
7311
/*                   GDALAlgorithmArgGetAsStringList()                  */
7312
/************************************************************************/
7313
7314
/** Return the argument value as a string list.
7315
 *
7316
 * Must only be called on arguments whose type is GAAT_STRING_LIST.
7317
 *
7318
 * @param hArg Handle to an argument. Must NOT be null.
7319
 * @return a NULL terminated list of names, which must be destroyed with
7320
 * CSLDestroy()
7321
7322
 * @since 3.11
7323
 */
7324
char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
7325
0
{
7326
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7327
0
    if (hArg->ptr->GetType() != GAAT_STRING_LIST)
7328
0
    {
7329
0
        CPLError(CE_Failure, CPLE_AppDefined,
7330
0
                 "%s must only be called on arguments of type GAAT_STRING_LIST",
7331
0
                 __func__);
7332
0
        return nullptr;
7333
0
    }
7334
0
    return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
7335
0
        .StealList();
7336
0
}
7337
7338
/************************************************************************/
7339
/*                  GDALAlgorithmArgGetAsIntegerList()                  */
7340
/************************************************************************/
7341
7342
/** Return the argument value as a integer list.
7343
 *
7344
 * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
7345
 *
7346
 * @param hArg Handle to an argument. Must NOT be null.
7347
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7348
 * @since 3.11
7349
 */
7350
const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
7351
                                            size_t *pnCount)
7352
0
{
7353
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7354
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
7355
0
    if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
7356
0
    {
7357
0
        CPLError(
7358
0
            CE_Failure, CPLE_AppDefined,
7359
0
            "%s must only be called on arguments of type GAAT_INTEGER_LIST",
7360
0
            __func__);
7361
0
        *pnCount = 0;
7362
0
        return nullptr;
7363
0
    }
7364
0
    const auto &val = hArg->ptr->Get<std::vector<int>>();
7365
0
    *pnCount = val.size();
7366
0
    return val.data();
7367
0
}
7368
7369
/************************************************************************/
7370
/*                  GDALAlgorithmArgGetAsDoubleList()                   */
7371
/************************************************************************/
7372
7373
/** Return the argument value as a real list.
7374
 *
7375
 * Must only be called on arguments whose type is GAAT_REAL_LIST.
7376
 *
7377
 * @param hArg Handle to an argument. Must NOT be null.
7378
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7379
 * @since 3.11
7380
 */
7381
const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
7382
                                              size_t *pnCount)
7383
0
{
7384
0
    VALIDATE_POINTER1(hArg, __func__, nullptr);
7385
0
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
7386
0
    if (hArg->ptr->GetType() != GAAT_REAL_LIST)
7387
0
    {
7388
0
        CPLError(CE_Failure, CPLE_AppDefined,
7389
0
                 "%s must only be called on arguments of type GAAT_REAL_LIST",
7390
0
                 __func__);
7391
0
        *pnCount = 0;
7392
0
        return nullptr;
7393
0
    }
7394
0
    const auto &val = hArg->ptr->Get<std::vector<double>>();
7395
0
    *pnCount = val.size();
7396
0
    return val.data();
7397
0
}
7398
7399
/************************************************************************/
7400
/*                    GDALAlgorithmArgSetAsBoolean()                    */
7401
/************************************************************************/
7402
7403
/** Set the value for a GAAT_BOOLEAN argument.
7404
 *
7405
 * It cannot be called several times for a given argument.
7406
 * Validation checks and other actions are run.
7407
 *
7408
 * @param hArg Handle to an argument. Must NOT be null.
7409
 * @param value value.
7410
 * @return true if success.
7411
 * @since 3.11
7412
 */
7413
7414
bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
7415
0
{
7416
0
    VALIDATE_POINTER1(hArg, __func__, false);
7417
0
    return hArg->ptr->Set(value);
7418
0
}
7419
7420
/************************************************************************/
7421
/*                    GDALAlgorithmArgSetAsString()                     */
7422
/************************************************************************/
7423
7424
/** Set the value for a GAAT_STRING argument.
7425
 *
7426
 * It cannot be called several times for a given argument.
7427
 * Validation checks and other actions are run.
7428
 *
7429
 * @param hArg Handle to an argument. Must NOT be null.
7430
 * @param value value (may be null)
7431
 * @return true if success.
7432
 * @since 3.11
7433
 */
7434
7435
bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
7436
0
{
7437
0
    VALIDATE_POINTER1(hArg, __func__, false);
7438
0
    return hArg->ptr->Set(value ? value : "");
7439
0
}
7440
7441
/************************************************************************/
7442
/*                    GDALAlgorithmArgSetAsInteger()                    */
7443
/************************************************************************/
7444
7445
/** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
7446
 *
7447
 * It cannot be called several times for a given argument.
7448
 * Validation checks and other actions are run.
7449
 *
7450
 * @param hArg Handle to an argument. Must NOT be null.
7451
 * @param value value.
7452
 * @return true if success.
7453
 * @since 3.11
7454
 */
7455
7456
bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
7457
0
{
7458
0
    VALIDATE_POINTER1(hArg, __func__, false);
7459
0
    return hArg->ptr->Set(value);
7460
0
}
7461
7462
/************************************************************************/
7463
/*                    GDALAlgorithmArgSetAsDouble()                     */
7464
/************************************************************************/
7465
7466
/** Set the value for a GAAT_REAL argument.
7467
 *
7468
 * It cannot be called several times for a given argument.
7469
 * Validation checks and other actions are run.
7470
 *
7471
 * @param hArg Handle to an argument. Must NOT be null.
7472
 * @param value value.
7473
 * @return true if success.
7474
 * @since 3.11
7475
 */
7476
7477
bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
7478
0
{
7479
0
    VALIDATE_POINTER1(hArg, __func__, false);
7480
0
    return hArg->ptr->Set(value);
7481
0
}
7482
7483
/************************************************************************/
7484
/*                 GDALAlgorithmArgSetAsDatasetValue()                  */
7485
/************************************************************************/
7486
7487
/** Set the value for a GAAT_DATASET argument.
7488
 *
7489
 * It cannot be called several times for a given argument.
7490
 * Validation checks and other actions are run.
7491
 *
7492
 * @param hArg Handle to an argument. Must NOT be null.
7493
 * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
7494
 * @return true if success.
7495
 * @since 3.11
7496
 */
7497
bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
7498
                                       GDALArgDatasetValueH value)
7499
0
{
7500
0
    VALIDATE_POINTER1(hArg, __func__, false);
7501
0
    VALIDATE_POINTER1(value, __func__, false);
7502
0
    return hArg->ptr->SetFrom(*(value->ptr));
7503
0
}
7504
7505
/************************************************************************/
7506
/*                     GDALAlgorithmArgSetDataset()                     */
7507
/************************************************************************/
7508
7509
/** Set dataset object, increasing its reference counter.
7510
 *
7511
 * @param hArg Handle to an argument. Must NOT be null.
7512
 * @param hDS Dataset object. May be null.
7513
 * @return true if success.
7514
 * @since 3.11
7515
 */
7516
7517
bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
7518
0
{
7519
0
    VALIDATE_POINTER1(hArg, __func__, false);
7520
0
    return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
7521
0
}
7522
7523
/************************************************************************/
7524
/*                  GDALAlgorithmArgSetAsStringList()                   */
7525
/************************************************************************/
7526
7527
/** Set the value for a GAAT_STRING_LIST argument.
7528
 *
7529
 * It cannot be called several times for a given argument.
7530
 * Validation checks and other actions are run.
7531
 *
7532
 * @param hArg Handle to an argument. Must NOT be null.
7533
 * @param value value as a NULL terminated list (may be null)
7534
 * @return true if success.
7535
 * @since 3.11
7536
 */
7537
7538
bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
7539
0
{
7540
0
    VALIDATE_POINTER1(hArg, __func__, false);
7541
0
    return hArg->ptr->Set(
7542
0
        static_cast<std::vector<std::string>>(CPLStringList(value)));
7543
0
}
7544
7545
/************************************************************************/
7546
/*                  GDALAlgorithmArgSetAsIntegerList()                  */
7547
/************************************************************************/
7548
7549
/** Set the value for a GAAT_INTEGER_LIST argument.
7550
 *
7551
 * It cannot be called several times for a given argument.
7552
 * Validation checks and other actions are run.
7553
 *
7554
 * @param hArg Handle to an argument. Must NOT be null.
7555
 * @param nCount Number of values in pnValues.
7556
 * @param pnValues Pointer to an array of integer values of size nCount.
7557
 * @return true if success.
7558
 * @since 3.11
7559
 */
7560
bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
7561
                                      const int *pnValues)
7562
0
{
7563
0
    VALIDATE_POINTER1(hArg, __func__, false);
7564
0
    return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
7565
0
}
7566
7567
/************************************************************************/
7568
/*                   GDALAlgorithmArgSetAsDoubleList()                  */
7569
/************************************************************************/
7570
7571
/** Set the value for a GAAT_REAL_LIST argument.
7572
 *
7573
 * It cannot be called several times for a given argument.
7574
 * Validation checks and other actions are run.
7575
 *
7576
 * @param hArg Handle to an argument. Must NOT be null.
7577
 * @param nCount Number of values in pnValues.
7578
 * @param pnValues Pointer to an array of double values of size nCount.
7579
 * @return true if success.
7580
 * @since 3.11
7581
 */
7582
bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
7583
                                     const double *pnValues)
7584
0
{
7585
0
    VALIDATE_POINTER1(hArg, __func__, false);
7586
0
    return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
7587
0
}
7588
7589
/************************************************************************/
7590
/*                     GDALAlgorithmArgSetDatasets()                    */
7591
/************************************************************************/
7592
7593
/** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
7594
 *
7595
 * @param hArg Handle to an argument. Must NOT be null.
7596
 * @param nCount Number of values in pnValues.
7597
 * @param pahDS Pointer to an array of dataset of size nCount.
7598
 * @return true if success.
7599
 * @since 3.11
7600
 */
7601
7602
bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
7603
                                 GDALDatasetH *pahDS)
7604
0
{
7605
0
    VALIDATE_POINTER1(hArg, __func__, false);
7606
0
    std::vector<GDALArgDatasetValue> values;
7607
0
    for (size_t i = 0; i < nCount; ++i)
7608
0
    {
7609
0
        values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
7610
0
    }
7611
0
    return hArg->ptr->Set(std::move(values));
7612
0
}
7613
7614
/************************************************************************/
7615
/*                    GDALAlgorithmArgSetDatasetNames()                 */
7616
/************************************************************************/
7617
7618
/** Set dataset names to a GAAT_DATASET_LIST argument.
7619
 *
7620
 * @param hArg Handle to an argument. Must NOT be null.
7621
 * @param names Dataset names as a NULL terminated list (may be null)
7622
 * @return true if success.
7623
 * @since 3.11
7624
 */
7625
7626
bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
7627
0
{
7628
0
    VALIDATE_POINTER1(hArg, __func__, false);
7629
0
    std::vector<GDALArgDatasetValue> values;
7630
0
    for (size_t i = 0; names[i]; ++i)
7631
0
    {
7632
0
        values.emplace_back(names[i]);
7633
0
    }
7634
0
    return hArg->ptr->Set(std::move(values));
7635
0
}
7636
7637
/************************************************************************/
7638
/*                      GDALArgDatasetValueCreate()                     */
7639
/************************************************************************/
7640
7641
/** Instantiate an empty GDALArgDatasetValue
7642
 *
7643
 * @return new handle to free with GDALArgDatasetValueRelease()
7644
 * @since 3.11
7645
 */
7646
GDALArgDatasetValueH GDALArgDatasetValueCreate()
7647
0
{
7648
0
    return std::make_unique<GDALArgDatasetValueHS>().release();
7649
0
}
7650
7651
/************************************************************************/
7652
/*                      GDALArgDatasetValueRelease()                    */
7653
/************************************************************************/
7654
7655
/** Release a handle to a GDALArgDatasetValue
7656
 *
7657
 * @since 3.11
7658
 */
7659
void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
7660
0
{
7661
0
    delete hValue;
7662
0
}
7663
7664
/************************************************************************/
7665
/*                    GDALArgDatasetValueGetName()                      */
7666
/************************************************************************/
7667
7668
/** Return the name component of the GDALArgDatasetValue
7669
 *
7670
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7671
 * @return string whose lifetime is bound to hAlg and which must not
7672
 * be freed.
7673
 * @since 3.11
7674
 */
7675
const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
7676
0
{
7677
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
7678
0
    return hValue->ptr->GetName().c_str();
7679
0
}
7680
7681
/************************************************************************/
7682
/*               GDALArgDatasetValueGetDatasetRef()                     */
7683
/************************************************************************/
7684
7685
/** Return the dataset component of the GDALArgDatasetValue.
7686
 *
7687
 * This does not modify the reference counter, hence the lifetime of the
7688
 * returned object is not guaranteed to exceed the one of hValue.
7689
 *
7690
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7691
 * @since 3.11
7692
 */
7693
GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
7694
0
{
7695
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
7696
0
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
7697
0
}
7698
7699
/************************************************************************/
7700
/*               GDALArgDatasetValueGetDatasetIncreaseRefCount()        */
7701
/************************************************************************/
7702
7703
/** Return the dataset component of the GDALArgDatasetValue, and increase its
7704
 * reference count if not null. Once done with the dataset, the caller should
7705
 * call GDALReleaseDataset().
7706
 *
7707
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7708
 * @since 3.11
7709
 */
7710
GDALDatasetH
7711
GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
7712
0
{
7713
0
    VALIDATE_POINTER1(hValue, __func__, nullptr);
7714
0
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
7715
0
}
7716
7717
/************************************************************************/
7718
/*                    GDALArgDatasetValueSetName()                      */
7719
/************************************************************************/
7720
7721
/** Set dataset name
7722
 *
7723
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7724
 * @param pszName Dataset name. May be null.
7725
 * @since 3.11
7726
 */
7727
7728
void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
7729
                                const char *pszName)
7730
0
{
7731
0
    VALIDATE_POINTER0(hValue, __func__);
7732
0
    hValue->ptr->Set(pszName ? pszName : "");
7733
0
}
7734
7735
/************************************************************************/
7736
/*                  GDALArgDatasetValueSetDataset()                     */
7737
/************************************************************************/
7738
7739
/** Set dataset object, increasing its reference counter.
7740
 *
7741
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7742
 * @param hDS Dataset object. May be null.
7743
 * @since 3.11
7744
 */
7745
7746
void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
7747
                                   GDALDatasetH hDS)
7748
0
{
7749
0
    VALIDATE_POINTER0(hValue, __func__);
7750
0
    hValue->ptr->Set(GDALDataset::FromHandle(hDS));
7751
0
}