Coverage Report

Created: 2025-08-28 06:57

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