Coverage Report

Created: 2026-04-01 06:20

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