Coverage Report

Created: 2025-11-16 06:25

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